style: fix potential bugs reported by eslint
This commit is contained in:
+58
-42
@@ -2,11 +2,12 @@
|
||||
import js from "@eslint/js"
|
||||
import eslintConfigPrettier from "eslint-config-prettier"
|
||||
import jsdoc from "eslint-plugin-jsdoc"
|
||||
import { defineConfig, globalIgnores } from "eslint/config"
|
||||
import globals from "globals"
|
||||
import ts from "typescript-eslint"
|
||||
|
||||
/** @type {import('eslint').Linter.Config[]} */
|
||||
export default [
|
||||
export default defineConfig([
|
||||
globalIgnores(["lib/*", "scripts/*", "*.config.js"]),
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
jsdoc.configs["flat/recommended-typescript-error"],
|
||||
@@ -101,7 +102,54 @@ export default [
|
||||
"symbol-description": "error",
|
||||
yoda: "error",
|
||||
|
||||
// TypeScript
|
||||
// JSDoc
|
||||
"jsdoc/check-tag-names": [
|
||||
"error",
|
||||
{
|
||||
definedTags: ["main", "integer", "minItems"],
|
||||
},
|
||||
],
|
||||
"jsdoc/require-jsdoc": [
|
||||
"error",
|
||||
{
|
||||
contexts: [
|
||||
"TSInterfaceDeclaration",
|
||||
"TSMethodSignature",
|
||||
"TSEnumDeclaration",
|
||||
"TSTypeAliasDeclaration",
|
||||
"ExportNamedDeclaration > VariableDeclaration",
|
||||
],
|
||||
publicOnly: true,
|
||||
require: {
|
||||
ArrowFunctionExpression: true,
|
||||
ClassDeclaration: true,
|
||||
ClassExpression: true,
|
||||
FunctionDeclaration: true,
|
||||
FunctionExpression: true,
|
||||
MethodDefinition: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"jsdoc/require-param": "off",
|
||||
"jsdoc/require-returns": "off",
|
||||
"jsdoc/require-description": [
|
||||
"error",
|
||||
{
|
||||
contexts: ["any"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ["eslint.config.js"],
|
||||
extends: ts.configs.strictTypeChecked,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||
"@typescript-eslint/array-type": "error",
|
||||
"@typescript-eslint/consistent-type-assertions": "error",
|
||||
@@ -146,47 +194,15 @@ export default [
|
||||
"@typescript-eslint/require-array-sort-compare": "error",
|
||||
"@typescript-eslint/strict-boolean-expressions": "error",
|
||||
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
||||
|
||||
// JSDoc
|
||||
"jsdoc/check-tag-names": [
|
||||
"error",
|
||||
{
|
||||
definedTags: ["main", "integer", "minItems"],
|
||||
},
|
||||
],
|
||||
"jsdoc/require-jsdoc": [
|
||||
"error",
|
||||
{
|
||||
contexts: [
|
||||
"TSInterfaceDeclaration",
|
||||
"TSMethodSignature",
|
||||
"TSEnumDeclaration",
|
||||
"TSTypeAliasDeclaration",
|
||||
"ExportNamedDeclaration > VariableDeclaration",
|
||||
],
|
||||
publicOnly: true,
|
||||
require: {
|
||||
ArrowFunctionExpression: true,
|
||||
ClassDeclaration: true,
|
||||
ClassExpression: true,
|
||||
FunctionDeclaration: true,
|
||||
FunctionExpression: true,
|
||||
MethodDefinition: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"jsdoc/require-param": "off",
|
||||
"jsdoc/require-returns": "off",
|
||||
"jsdoc/require-description": [
|
||||
"error",
|
||||
{
|
||||
contexts: ["any"],
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/consistent-type-exports": "error",
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
},
|
||||
},
|
||||
eslintConfigPrettier,
|
||||
{
|
||||
ignores: ["lib/*", "scripts/*", "*.config.js"],
|
||||
files: ["test/**/*.{js,mjs,cjs,ts,mts,cts}"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-floating-promises": "off",
|
||||
},
|
||||
},
|
||||
]
|
||||
])
|
||||
|
||||
Generated
+8
-8
@@ -9,7 +9,7 @@
|
||||
"version": "0.4.0",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@elyukai/utils": "^0.3.0",
|
||||
"@elyukai/utils": "^0.3.1",
|
||||
"@optolith/adventure-points": "^0.1.1",
|
||||
"@optolith/helpers": "^0.2.2",
|
||||
"messageformat": "^4.0.0",
|
||||
@@ -22,7 +22,7 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jsdoc": "^62.8.0",
|
||||
"globals": "^17.4.0",
|
||||
"optolith-database-schema": "^0.34.17",
|
||||
"optolith-database-schema": "^0.35.0",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
@@ -82,9 +82,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@elyukai/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@elyukai/utils/-/utils-0.3.0.tgz",
|
||||
"integrity": "sha512-NLzeqPjO27qUE1OrGKdEHIs8p++Lg+Zl0xyFb/jiMNtxAjofvx6MBPNSpej+9Ngc9c38I3AUhvpXiXaFmyIg2Q==",
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@elyukai/utils/-/utils-0.3.1.tgz",
|
||||
"integrity": "sha512-OzfEkxVu8uYbPfrrUPaYLMVMh27dyx0VqK9gwULjDnZdMb9egtFSPeMQandGXDjEXGfb+USa4iWP8zSudh0QwQ==",
|
||||
"license": "MPL-2.0"
|
||||
},
|
||||
"node_modules/@es-joy/jsdoccomment": {
|
||||
@@ -3862,9 +3862,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/optolith-database-schema": {
|
||||
"version": "0.34.17",
|
||||
"resolved": "https://registry.npmjs.org/optolith-database-schema/-/optolith-database-schema-0.34.17.tgz",
|
||||
"integrity": "sha512-RhsLdWLcWKbDrmCEfim5iovj1piwk+uU0zkriRJkTnTCcpkLfAlXZ2YlepC2OcoVQjzWViM9EaRYlZgOoN8/OQ==",
|
||||
"version": "0.35.0",
|
||||
"resolved": "https://registry.npmjs.org/optolith-database-schema/-/optolith-database-schema-0.35.0.tgz",
|
||||
"integrity": "sha512-36VAqVSY4PXmz+IUiekldYVhz9CTaN166elqpgYnJaNd8UP+R0ew1gM8vjiwande1gHutFXuiYEF4/09rEpbEw==",
|
||||
"dev": true,
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/Optolith/entity-descriptions#readme",
|
||||
"dependencies": {
|
||||
"@elyukai/utils": "^0.3.0",
|
||||
"@elyukai/utils": "^0.3.1",
|
||||
"@optolith/adventure-points": "^0.1.1",
|
||||
"@optolith/helpers": "^0.2.2",
|
||||
"messageformat": "^4.0.0",
|
||||
@@ -46,7 +46,7 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jsdoc": "^62.8.0",
|
||||
"globals": "^17.4.0",
|
||||
"optolith-database-schema": "^0.34.17",
|
||||
"optolith-database-schema": "^0.35.0",
|
||||
"prettier": "^3.8.1",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3",
|
||||
|
||||
@@ -68,12 +68,14 @@ const unitListFormat = new Intl.ListFormat(localeId, {
|
||||
|
||||
const collator = new Intl.Collator(localeId, { usage: "sort" })
|
||||
const dateFormatter = new Intl.DateTimeFormat(localeId)
|
||||
const numberFormatter = new Intl.NumberFormat(localeId)
|
||||
|
||||
const localeEnv: LocaleEnvironment = {
|
||||
id: localeId,
|
||||
format: (text, args) => new MessageFormat(localeId, text, { bidiIsolation: "none" }).format(args),
|
||||
compare: collator.compare.bind(collator),
|
||||
formatDate: dateFormatter.format.bind(dateFormatter),
|
||||
formatNumber: numberFormatter.format.bind(numberFormatter),
|
||||
formatDate: date => dateFormatter.format(new Date(date)),
|
||||
translate: (key, ...rest) =>
|
||||
new MessageFormat(localeId, localeInstance.translations?.[key] ?? key, {
|
||||
bidiIsolation: "none",
|
||||
|
||||
+10
-10
@@ -2,18 +2,18 @@ import { isNotNullish } from "@elyukai/utils/nullable"
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import type { EntityMap } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "./helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "./helpers/locale.js"
|
||||
import {
|
||||
import type { LocaleEnvironment } from "./helpers/locale.js"
|
||||
import type {
|
||||
DefinitionListEntityDescriptionSection,
|
||||
EntityDescription,
|
||||
EntityDescriptionSection,
|
||||
EntityDescriptionSectionContent,
|
||||
NestedDefinitionListEntityDescriptionSection,
|
||||
RawDefinitionListEntityDescriptionSection,
|
||||
RawEntityDescription,
|
||||
type DefinitionListEntityDescriptionSection,
|
||||
type EntityDescriptionSection,
|
||||
type EntityDescriptionSectionContent,
|
||||
type NestedDefinitionListEntityDescriptionSection,
|
||||
type RawDefinitionListEntityDescriptionSection,
|
||||
type RawEntityDescriptionSection,
|
||||
type RawEntityDescriptionSectionContent,
|
||||
type RawNestedDefinitionListEntityDescriptionSection,
|
||||
RawEntityDescriptionSection,
|
||||
RawEntityDescriptionSectionContent,
|
||||
RawNestedDefinitionListEntityDescriptionSection,
|
||||
} from "./index.js"
|
||||
import { getReferencesTranslation } from "./references/index.js"
|
||||
|
||||
|
||||
+77
-181
@@ -41,11 +41,7 @@ import type {
|
||||
import { Case, fromUniformCase } from "tsondb/schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type { GetAllInstances, GetInstanceById } from "../helpers/getTypes.js"
|
||||
import type {
|
||||
LocaleCompare,
|
||||
LocaleEnvironment,
|
||||
LocaleJoin,
|
||||
} from "../helpers/locale.js"
|
||||
import type { LocaleCompare, LocaleEnvironment, LocaleJoin } from "../helpers/locale.js"
|
||||
import type { Translate, TranslateMap } from "../helpers/translate.js"
|
||||
import type {
|
||||
GetAllResolvedNewSkillApplications,
|
||||
@@ -122,9 +118,8 @@ const renderPropertyValue = (
|
||||
return translate("As chosen")
|
||||
case "Fixed":
|
||||
return (
|
||||
translateMap(
|
||||
getInstanceById("Property", propertyDecl.Fixed)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("Property", propertyDecl.Fixed)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
)
|
||||
default:
|
||||
return assertExhaustive(propertyDecl)
|
||||
@@ -149,9 +144,7 @@ const renderPenaltyByAttackLabel = (
|
||||
}
|
||||
|
||||
const renderPenaltyValue = (
|
||||
getInstanceById: GetInstanceById<
|
||||
CombatRelatedSpecialAbilityIdentifier["kind"]
|
||||
>,
|
||||
getInstanceById: GetInstanceById<CombatRelatedSpecialAbilityIdentifier["kind"]>,
|
||||
translate: Translate,
|
||||
translateMap: TranslateMap,
|
||||
name: string,
|
||||
@@ -161,15 +154,11 @@ const renderPenaltyValue = (
|
||||
case "Single":
|
||||
return (
|
||||
sign(penalty.Single.value) +
|
||||
(penalty.Single.applies_to_parry === true
|
||||
? ` (${translate("for parry")})`
|
||||
: "")
|
||||
(penalty.Single.applies_to_parry === true ? ` (${translate("for parry")})` : "")
|
||||
)
|
||||
case "ByHandedness": {
|
||||
const appendParry =
|
||||
penalty.ByHandedness.applies_to_parry === true
|
||||
? `; ${translate("for parry")}`
|
||||
: ""
|
||||
penalty.ByHandedness.applies_to_parry === true ? `; ${translate("for parry")}` : ""
|
||||
|
||||
return `${sign(penalty.ByHandedness.one_handed)} (${
|
||||
translate("one-handed weapon") + appendParry
|
||||
@@ -179,13 +168,8 @@ const renderPenaltyValue = (
|
||||
}
|
||||
case "ByActivation": {
|
||||
return `${sign(penalty.ByActivation.active)}/${sign(penalty.ByActivation.inactive)} (${[
|
||||
penalty.ByActivation.applies_to_parry === true
|
||||
? translate("for parry")
|
||||
: undefined,
|
||||
translate(
|
||||
"for secondary fighters with/without special ability {$name}",
|
||||
{ name },
|
||||
),
|
||||
penalty.ByActivation.applies_to_parry === true ? translate("for parry") : undefined,
|
||||
translate("for secondary fighters with/without special ability {$name}", { name }),
|
||||
]
|
||||
.filter(isNotNullish)
|
||||
.join("; ")})`
|
||||
@@ -193,9 +177,7 @@ const renderPenaltyValue = (
|
||||
case "Selection":
|
||||
switch (penalty.Selection.options.kind) {
|
||||
case "Specific":
|
||||
return penalty.Selection.options.Specific.list
|
||||
.map(option => sign(option.value))
|
||||
.join("/")
|
||||
return penalty.Selection.options.Specific.list.map(option => sign(option.value)).join("/")
|
||||
case "Range":
|
||||
return translate("{$start} to {$end}", {
|
||||
start: sign(penalty.Selection.options.Range.minimum),
|
||||
@@ -216,24 +198,19 @@ const renderPenaltyValue = (
|
||||
}
|
||||
|
||||
const externalName =
|
||||
translateMap(
|
||||
getInstanceById(external.kind, fromUniformCase(external))
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById(external.kind, fromUniformCase(external))?.translations)
|
||||
?.name ?? MISSING_VALUE
|
||||
|
||||
return `${main} (${translate(
|
||||
"depending on the level of the special ability {$name}",
|
||||
{
|
||||
name: externalName,
|
||||
},
|
||||
)})`
|
||||
return `${main} (${translate("depending on the level of the special ability {$name}", {
|
||||
name: externalName,
|
||||
})})`
|
||||
}
|
||||
case "ByAttack": {
|
||||
const offset = penalty.ByAttack.initial_order ?? 1
|
||||
return penalty.ByAttack.list
|
||||
.map(
|
||||
(penaltyByAttack, index) =>
|
||||
`${penaltyByAttack.value} (${renderPenaltyByAttackLabel(
|
||||
`${penaltyByAttack.value.toFixed()} (${renderPenaltyByAttackLabel(
|
||||
translate,
|
||||
penalty.ByAttack.attack_replacement,
|
||||
index + offset,
|
||||
@@ -287,8 +264,7 @@ const wrapInParens = (args: (string | undefined)[], append = ""): string => {
|
||||
return ` (${filteredArgs.join(", ") + append})`
|
||||
}
|
||||
|
||||
const addSpaceIfNoCommaAtStart = (str: string): string =>
|
||||
str.startsWith(",") ? str : ` ${str}`
|
||||
const addSpaceIfNoCommaAtStart = (str: string): string => (str.startsWith(",") ? str : ` ${str}`)
|
||||
|
||||
const renderApplicableCombatTechniquesRestriction = <
|
||||
T extends
|
||||
@@ -297,9 +273,7 @@ const renderApplicableCombatTechniquesRestriction = <
|
||||
| ApplicableRangedCombatTechniquesRestriction
|
||||
| ApplicableSpecificCombatTechniquesRestriction,
|
||||
>(
|
||||
getInstanceById: GetInstanceById<
|
||||
"CloseCombatTechnique" | "RangedCombatTechnique" | "Race"
|
||||
>,
|
||||
getInstanceById: GetInstanceById<"CloseCombatTechnique" | "RangedCombatTechnique" | "Race">,
|
||||
locale: LocaleEnvironment,
|
||||
main: string,
|
||||
restriction: T,
|
||||
@@ -313,18 +287,9 @@ const renderApplicableCombatTechniquesRestriction = <
|
||||
): string => {
|
||||
switch (restriction.kind) {
|
||||
case "Improvised":
|
||||
return (
|
||||
main +
|
||||
wrapInParens([locale.translate("only improvised weapons"), weapons])
|
||||
)
|
||||
return main + wrapInParens([locale.translate("only improvised weapons"), weapons])
|
||||
case "PointedBlade":
|
||||
return (
|
||||
main +
|
||||
wrapInParens([
|
||||
locale.translate("weapon must have a pointed blade"),
|
||||
weapons,
|
||||
])
|
||||
)
|
||||
return main + wrapInParens([locale.translate("weapon must have a pointed blade"), weapons])
|
||||
case "Mount":
|
||||
if (weapons === undefined) {
|
||||
return `${main} ${locale.translate("while mounted")}`
|
||||
@@ -333,8 +298,7 @@ const renderApplicableCombatTechniquesRestriction = <
|
||||
}
|
||||
case "Race": {
|
||||
const race = getInstanceById("Race", restriction.Race)
|
||||
const raceName =
|
||||
locale.translateMap(race?.translations)?.name ?? MISSING_VALUE
|
||||
const raceName = locale.translateMap(race?.translations)?.name ?? MISSING_VALUE
|
||||
|
||||
return (
|
||||
main +
|
||||
@@ -357,9 +321,7 @@ const renderApplicableCombatTechniquesRestriction = <
|
||||
.map(
|
||||
id =>
|
||||
locale.translateMap(
|
||||
getExcludedInstance?.(
|
||||
id as (CombatTechniqueIdentifier & string) & string,
|
||||
)?.translations,
|
||||
getExcludedInstance?.(id as CombatTechniqueIdentifier & string)?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
)
|
||||
.toSorted(locale.compare),
|
||||
@@ -380,28 +342,18 @@ const renderApplicableCombatTechniquesRestriction = <
|
||||
wrapInParens([weapons])
|
||||
)
|
||||
case "TwoHanded":
|
||||
return (
|
||||
locale.translate("All Two-Handed Weapons") + wrapInParens([weapons])
|
||||
)
|
||||
return locale.translate("All Two-Handed Weapons") + wrapInParens([weapons])
|
||||
case "ParryingWeapon":
|
||||
return locale.translate("All Parrying Weapons") + wrapInParens([weapons])
|
||||
case "Level": {
|
||||
const nameWithLevel = `${translation} ${romanize(restriction.Level.level)}`
|
||||
const nameWithLevel = `${translation.name_in_library ?? translation.name} ${romanize(restriction.Level.level)}`
|
||||
return (
|
||||
main +
|
||||
wrapInParens([
|
||||
locale.translate("only {$nameWithLevel}", { nameWithLevel }),
|
||||
weapons,
|
||||
])
|
||||
main + wrapInParens([locale.translate("only {$nameWithLevel}", { nameWithLevel }), weapons])
|
||||
)
|
||||
}
|
||||
case "OneBluntSide":
|
||||
return (
|
||||
main +
|
||||
wrapInParens([
|
||||
locale.translate("only those with at least one blunt side"),
|
||||
weapons,
|
||||
])
|
||||
main + wrapInParens([locale.translate("only those with at least one blunt side"), weapons])
|
||||
)
|
||||
default:
|
||||
return assertExhaustive(restriction)
|
||||
@@ -474,12 +426,8 @@ const renderApplicableCombatTechniquesValue = (
|
||||
case "Specific": {
|
||||
return applicableCombatTechniques.Specific.list
|
||||
.map(specific => {
|
||||
const entry = getInstanceById(
|
||||
specific.id.kind,
|
||||
fromUniformCase(specific.id),
|
||||
)
|
||||
const main =
|
||||
locale.translateMap(entry?.translations)?.name ?? MISSING_VALUE
|
||||
const entry = getInstanceById(specific.id.kind, fromUniformCase(specific.id))
|
||||
const main = locale.translateMap(entry?.translations)?.name ?? MISSING_VALUE
|
||||
const mainWithRestriction =
|
||||
specific.restriction === undefined
|
||||
? main
|
||||
@@ -496,10 +444,8 @@ const renderApplicableCombatTechniquesValue = (
|
||||
specific.weapons
|
||||
.map(
|
||||
weapon =>
|
||||
locale.translateMap(
|
||||
getInstanceById("Weapon", weapon)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
locale.translateMap(getInstanceById("Weapon", weapon)?.translations)
|
||||
?.name ?? MISSING_VALUE,
|
||||
)
|
||||
.toSorted(locale.compare),
|
||||
"conjunction",
|
||||
@@ -540,9 +486,7 @@ const renderVolumeValue = (
|
||||
case "ByLevel":
|
||||
return translate("{$points} points for levels {$levels}", {
|
||||
points: volume.ByLevel.list.map(item => item.points).join("/"),
|
||||
levels: volume.ByLevel.list
|
||||
.map((_, index) => romanize(index + 1))
|
||||
.join("/"),
|
||||
levels: volume.ByLevel.list.map((_, index) => romanize(index + 1)).join("/"),
|
||||
})
|
||||
case "Map":
|
||||
return renderResponsiveMap(
|
||||
@@ -570,11 +514,7 @@ const renderVolumeValue = (
|
||||
})} ${translate("for")} ${groups
|
||||
.map(group =>
|
||||
group[1]
|
||||
.map(
|
||||
groupItem =>
|
||||
translateMap(groupItem.content.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
)
|
||||
.map(groupItem => translateMap(groupItem.content.translations)?.name ?? MISSING_VALUE)
|
||||
.join(", "),
|
||||
)
|
||||
.join(separator)}`
|
||||
@@ -618,28 +558,26 @@ const renderArcaneEnergyCost = (
|
||||
),
|
||||
})
|
||||
|
||||
const wrapInPerLevel: (prev: (value: number) => string) => string =
|
||||
(() => {
|
||||
if (cost.Fixed.per_level === undefined) {
|
||||
return prev => prev(cost.Fixed.value)
|
||||
}
|
||||
const wrapInPerLevel: (prev: (value: number) => string) => string = (() => {
|
||||
if (cost.Fixed.per_level === undefined) {
|
||||
return prev => prev(cost.Fixed.value)
|
||||
}
|
||||
|
||||
switch (cost.Fixed.per_level?.kind) {
|
||||
case "Compressed":
|
||||
return prev =>
|
||||
translate("{$cost} per level", { cost: prev(cost.Fixed.value) })
|
||||
case "Verbose":
|
||||
return prev =>
|
||||
Array.from({ length: levels ?? 1 }, (_, index) =>
|
||||
translate("{$cost} for level {$level}", {
|
||||
cost: prev(cost.Fixed.value),
|
||||
level: romanize(index + 1),
|
||||
}),
|
||||
).join("; ")
|
||||
default:
|
||||
return assertExhaustive(cost.Fixed.per_level)
|
||||
}
|
||||
})()
|
||||
switch (cost.Fixed.per_level.kind) {
|
||||
case "Compressed":
|
||||
return prev => translate("{$cost} per level", { cost: prev(cost.Fixed.value) })
|
||||
case "Verbose":
|
||||
return prev =>
|
||||
Array.from({ length: levels ?? 1 }, (_, index) =>
|
||||
translate("{$cost} for level {$level}", {
|
||||
cost: prev(cost.Fixed.value),
|
||||
level: romanize(index + 1),
|
||||
}),
|
||||
).join("; ")
|
||||
default:
|
||||
return assertExhaustive(cost.Fixed.per_level)
|
||||
}
|
||||
})()
|
||||
|
||||
const noteInParens = parensIf(
|
||||
mapNullable(translateMap(cost.Fixed.translations)?.note, note =>
|
||||
@@ -647,10 +585,7 @@ const renderArcaneEnergyCost = (
|
||||
),
|
||||
)
|
||||
|
||||
return (
|
||||
wrapInPerLevel(value => wrapInInterval(translationWrapper(value))) +
|
||||
noteInParens
|
||||
)
|
||||
return wrapInPerLevel(value => wrapInInterval(translationWrapper(value))) + noteInParens
|
||||
}
|
||||
case "Constant":
|
||||
return (
|
||||
@@ -708,14 +643,12 @@ const renderArcaneEnergyCost = (
|
||||
)
|
||||
case "Indefinite":
|
||||
return (
|
||||
mapNullable(
|
||||
translateMap(cost.Indefinite.translations)?.description,
|
||||
description => getResponsiveText(description, responsiveTextSize),
|
||||
) +
|
||||
(mapNullable(translateMap(cost.Indefinite.translations)?.description, description =>
|
||||
getResponsiveText(description, responsiveTextSize),
|
||||
) ?? MISSING_VALUE) +
|
||||
mapNullableDefault(
|
||||
cost.Indefinite.modifier,
|
||||
modifier =>
|
||||
` + ${translate("{$value} AE", { value: modifier.value })}`,
|
||||
modifier => ` + ${translate("{$value} AE", { value: modifier.value })}`,
|
||||
"",
|
||||
)
|
||||
)
|
||||
@@ -724,11 +657,10 @@ const renderArcaneEnergyCost = (
|
||||
value: localeJoin(
|
||||
cost.Disjunction.options.map(
|
||||
option =>
|
||||
option.value +
|
||||
option.value.toFixed() +
|
||||
mapNullableDefault(
|
||||
translateMap(option.translations)?.note,
|
||||
note =>
|
||||
parensIf(getResponsiveTextOptional(note, responsiveTextSize)),
|
||||
note => parensIf(getResponsiveTextOptional(note, responsiveTextSize)),
|
||||
"",
|
||||
),
|
||||
),
|
||||
@@ -746,6 +678,7 @@ const renderArcaneEnergyCost = (
|
||||
translate(", {$value} of which are permanent", {
|
||||
value: values,
|
||||
}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
||||
getAdditionalValue: option => option.permanent_value!,
|
||||
}
|
||||
: undefined,
|
||||
@@ -763,9 +696,8 @@ const renderArcaneEnergyCost = (
|
||||
cost: translate("{$value} AE", {
|
||||
value: cost.ByLevel.levels.map(level => level.value).join("/"),
|
||||
}),
|
||||
level: Array.from(
|
||||
{ length: cost.ByLevel.levels.length },
|
||||
(_, index) => romanize(index + 1),
|
||||
level: Array.from({ length: cost.ByLevel.levels.length }, (_, index) =>
|
||||
romanize(index + 1),
|
||||
),
|
||||
})
|
||||
case "Verbose":
|
||||
@@ -818,8 +750,7 @@ const renderBindingCost = (
|
||||
case "DerivedFromSelection": {
|
||||
const groups = Map.groupBy(
|
||||
getAllResolvedSelectOptions(),
|
||||
option =>
|
||||
option.content.binding_cost ?? cost.DerivedFromSelection.fallback,
|
||||
option => option.content.binding_cost ?? cost.DerivedFromSelection.fallback,
|
||||
)
|
||||
.entries()
|
||||
.toArray()
|
||||
@@ -832,11 +763,7 @@ const renderBindingCost = (
|
||||
})} ${translate("for")} ${groups
|
||||
.map(group =>
|
||||
group[1]
|
||||
.map(
|
||||
groupItem =>
|
||||
translateMap(groupItem.content.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
)
|
||||
.map(groupItem => translateMap(groupItem.content.translations)?.name ?? MISSING_VALUE)
|
||||
.toSorted(localeCompare)
|
||||
.join(", "),
|
||||
)
|
||||
@@ -847,10 +774,7 @@ const renderBindingCost = (
|
||||
}
|
||||
}
|
||||
|
||||
const renderLifePointsCost = (
|
||||
translate: Translate,
|
||||
cost: LifePointsCost | undefined,
|
||||
): string =>
|
||||
const renderLifePointsCost = (translate: Translate, cost: LifePointsCost | undefined): string =>
|
||||
cost === undefined
|
||||
? ""
|
||||
: // eslint-disable-next-line no-irregular-whitespace
|
||||
@@ -889,8 +813,7 @@ const renderCost = (
|
||||
responsiveTextSize,
|
||||
levels,
|
||||
cost.ArcaneEnergyCost.ae_cost,
|
||||
) +
|
||||
renderLifePointsCost(translate, cost.ArcaneEnergyCost.lp_cost)
|
||||
) + renderLifePointsCost(translate, cost.ArcaneEnergyCost.lp_cost)
|
||||
: renderArcaneEnergyCost(
|
||||
translate,
|
||||
translateMap,
|
||||
@@ -956,19 +879,12 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator<
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
getInstanceById,
|
||||
getAllInstances,
|
||||
getResolvedSelectOptionById,
|
||||
getAllResolvedSelectOptions,
|
||||
},
|
||||
{ getInstanceById, getAllInstances, getResolvedSelectOptionById, getAllResolvedSelectOptions },
|
||||
locale,
|
||||
{ entity: entityName, content: entry, id },
|
||||
) => {
|
||||
const { translate, translateMap } = locale
|
||||
const translation = translateMap<BaseActivatableTranslation>(
|
||||
entry.translations,
|
||||
)
|
||||
const translation = translateMap<BaseActivatableTranslation>(entry.translations)
|
||||
const responsiveTextSize: ResponsiveTextSize = ResponsiveTextSize.Full
|
||||
|
||||
if (translation === undefined) {
|
||||
@@ -983,9 +899,7 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator<
|
||||
title:
|
||||
translation.name_in_library ??
|
||||
translation.name +
|
||||
(baseEntry.levels !== undefined
|
||||
? ` I–${romanize(baseEntry.levels)}`
|
||||
: ""),
|
||||
(baseEntry.levels !== undefined ? ` I–${romanize(baseEntry.levels)}` : ""),
|
||||
subtitle: mapNullable(baseEntry.usage_type, usageType => {
|
||||
switch (usageType.kind) {
|
||||
case "Passive":
|
||||
@@ -1032,8 +946,8 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator<
|
||||
mapNullable(baseEntry.aspect, aspect => ({
|
||||
label: translate("Aspect"),
|
||||
value:
|
||||
translateMap(getInstanceById("Aspect", aspect)?.translations)
|
||||
?.name ?? MISSING_VALUE,
|
||||
translateMap(getInstanceById("Aspect", aspect)?.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
})),
|
||||
mapNullable(translation.range, range => ({
|
||||
label: translate("Range"),
|
||||
@@ -1052,8 +966,7 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator<
|
||||
mapNullable(entry.prerequisites, prerequisites => ({
|
||||
label: translate("Prerequisites"),
|
||||
value:
|
||||
wrappedId.kind === "Advantage" ||
|
||||
wrappedId.kind === "Disadvantage"
|
||||
wrappedId.kind === "Advantage" || wrappedId.kind === "Disadvantage"
|
||||
? printAdvantageDisadvantagePrerequisites(
|
||||
getInstanceById,
|
||||
getResolvedSelectOptionById,
|
||||
@@ -1106,18 +1019,11 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator<
|
||||
),
|
||||
mapNullable(baseEntry.property, property => ({
|
||||
label: translate("Property"),
|
||||
value: renderPropertyValue(
|
||||
getInstanceById,
|
||||
translate,
|
||||
translateMap,
|
||||
property,
|
||||
),
|
||||
value: renderPropertyValue(getInstanceById, translate, translateMap, property),
|
||||
})),
|
||||
mapNullable(entry.ap_value, apValue => {
|
||||
const append =
|
||||
translation.ap_value_append !== undefined
|
||||
? ` ${translation.ap_value_append}`
|
||||
: ""
|
||||
translation.ap_value_append !== undefined ? ` ${translation.ap_value_append}` : ""
|
||||
return {
|
||||
label: translate("AP Value"),
|
||||
value:
|
||||
@@ -1125,31 +1031,21 @@ export const getActivatableEntityDescription = createEntityDescriptionCreator<
|
||||
locale,
|
||||
activatableId =>
|
||||
locale.translateMap<BaseActivatableTranslation>(
|
||||
getInstanceById(
|
||||
activatableId.kind,
|
||||
fromUniformCase(activatableId),
|
||||
)?.translations,
|
||||
getInstanceById(activatableId.kind, fromUniformCase(activatableId))
|
||||
?.translations,
|
||||
)?.name,
|
||||
selectOptionId => {
|
||||
const selectOption = getResolvedSelectOptionById(
|
||||
wrappedId,
|
||||
selectOptionId,
|
||||
)
|
||||
const selectOption = getResolvedSelectOptionById(wrappedId, selectOptionId)
|
||||
if (selectOption === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const name = locale.translateMap(
|
||||
selectOption.content.translations,
|
||||
)?.name
|
||||
const name = locale.translateMap(selectOption.content.translations)?.name
|
||||
if (name !== undefined) {
|
||||
return name
|
||||
}
|
||||
const { parent: parentId } = selectOption.content
|
||||
return locale.translateMap<BaseActivatableTranslation>(
|
||||
getInstanceById(
|
||||
parentId.kind,
|
||||
fromUniformCase(parentId),
|
||||
)?.translations,
|
||||
getInstanceById(parentId.kind, fromUniformCase(parentId))?.translations,
|
||||
)?.name
|
||||
},
|
||||
() => getAllResolvedSelectOptions(wrappedId),
|
||||
|
||||
+73
-166
@@ -7,6 +7,7 @@ import { isNotNullish, mapNullable } from "@elyukai/utils/nullable"
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { sign } from "@elyukai/utils/string/number"
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import { getAdventurePointsForRatingRange } from "@optolith/adventure-points/improvement-cost"
|
||||
import type {
|
||||
BlessedTraditionConstraint,
|
||||
CommonNames,
|
||||
@@ -21,10 +22,7 @@ import type {
|
||||
Weighted,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type {
|
||||
GetAllChildInstancesForParent,
|
||||
GetInstanceById,
|
||||
} from "../helpers/getTypes.js"
|
||||
import type { GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js"
|
||||
import type { TranslationKeysWithoutParams } from "../helpers/translate.js"
|
||||
import type {
|
||||
RawDefinitionListEntityDescriptionSectionItem,
|
||||
@@ -38,14 +36,8 @@ import {
|
||||
} from "./partial/commonnessRatedAdvantagesAndDisadvantages.js"
|
||||
import { getProfessionName } from "./partial/professions.js"
|
||||
import { parensIf } from "./partial/rated/activatable/parensIf.js"
|
||||
import {
|
||||
translateR,
|
||||
type EnvMap,
|
||||
type StdEnv,
|
||||
type StdReader,
|
||||
} from "./partial/reader.js"
|
||||
import { translateR, type EnvMap, type StdEnv, type StdReader } from "./partial/reader.js"
|
||||
import { MISSING_VALUE } from "./partial/unknown.js"
|
||||
import { getAdventurePointsForRatingRange } from "@optolith/adventure-points/improvement-cost"
|
||||
|
||||
const renderListOperation = (
|
||||
operation: CommonProfessionConstraintsOperation,
|
||||
@@ -55,10 +47,9 @@ const renderListOperation = (
|
||||
case "Intersection":
|
||||
return Reader.of(list.join(", "))
|
||||
case "Difference":
|
||||
return translateR(
|
||||
"all but {$excludedProfessions :list type=conjunction}",
|
||||
{ excludedProfessions: list },
|
||||
)
|
||||
return translateR("all but {$excludedProfessions :list type=conjunction}", {
|
||||
excludedProfessions: list,
|
||||
})
|
||||
default:
|
||||
return assertExhaustive(operation)
|
||||
}
|
||||
@@ -79,12 +70,11 @@ const renderCommonProfessionConstraints = <T>(
|
||||
"ProfessionVariant" | "BlessedTradition" | "MagicalTradition"
|
||||
> =>
|
||||
Reader.asks(({ localeCompare }: StdEnv<"lc">) =>
|
||||
Reader.sequence(constraints.constraints.map(renderConstraint)).thenW(
|
||||
constraintValues =>
|
||||
renderListOperation(
|
||||
constraints.operation,
|
||||
constraintValues.filter(isNotNullish).toSorted(localeCompare),
|
||||
),
|
||||
Reader.sequence(constraints.constraints.map(renderConstraint)).thenW(constraintValues =>
|
||||
renderListOperation(
|
||||
constraints.operation,
|
||||
constraintValues.filter(isNotNullish).toSorted(localeCompare),
|
||||
),
|
||||
),
|
||||
).thenW(identity)
|
||||
|
||||
@@ -109,9 +99,7 @@ const renderCommonProfessionGroup = <T>(
|
||||
})),
|
||||
)
|
||||
|
||||
const renderRarity = (
|
||||
rarity: Rarity | undefined,
|
||||
): StdReader<string | undefined, "t"> => {
|
||||
const renderRarity = (rarity: Rarity | undefined): StdReader<string | undefined, "t"> => {
|
||||
if (rarity === undefined) {
|
||||
return Reader.of(undefined)
|
||||
}
|
||||
@@ -136,10 +124,7 @@ const renderWeighted = <ID extends string>(
|
||||
|
||||
return Reader.asks(({ translate, localeCompare }) => {
|
||||
const variants = ensureNonEmpty(
|
||||
weightedVariants.elements
|
||||
.map(getName)
|
||||
.filter(isNotNullish)
|
||||
.toSorted(localeCompare),
|
||||
weightedVariants.elements.map(getName).filter(isNotNullish).toSorted(localeCompare),
|
||||
)
|
||||
|
||||
if (variants === undefined) {
|
||||
@@ -166,43 +151,27 @@ const renderProfessionConstraint = (
|
||||
constraint: ProfessionConstraint,
|
||||
) =>
|
||||
Reader.asks(({ translateMap }: StdEnv<"tm">) =>
|
||||
getProfessionName(
|
||||
translateMap,
|
||||
getChildInstancesForInstanceId,
|
||||
constraint.id,
|
||||
),
|
||||
getProfessionName(translateMap, getChildInstancesForInstanceId, constraint.id),
|
||||
).thenW(baseName =>
|
||||
baseName === undefined
|
||||
? Reader.of(undefined)
|
||||
: Reader.sequence<
|
||||
StdEnv<"t" | "tm" | "lc" | "ibi", "ProfessionVariant">,
|
||||
string | undefined
|
||||
>([
|
||||
renderRarity(constraint.rarity),
|
||||
Reader.asks(
|
||||
({
|
||||
translateMap,
|
||||
getInstanceById,
|
||||
}: StdEnv<"tm" | "ibi", "ProfessionVariant">) =>
|
||||
renderWeighted(
|
||||
constraint.weighted_variants,
|
||||
variantId =>
|
||||
translateMap(
|
||||
getInstanceById("ProfessionVariant", variantId)
|
||||
?.translations,
|
||||
)?.name.default,
|
||||
),
|
||||
).thenW(identity),
|
||||
]).map(
|
||||
notes =>
|
||||
baseName +
|
||||
parensIf(ensureNonEmpty(notes.filter(isNotNullish))?.join("; ")),
|
||||
),
|
||||
: Reader.sequence<StdEnv<"t" | "tm" | "lc" | "ibi", "ProfessionVariant">, string | undefined>(
|
||||
[
|
||||
renderRarity(constraint.rarity),
|
||||
Reader.asks(
|
||||
({ translateMap, getInstanceById }: StdEnv<"tm" | "ibi", "ProfessionVariant">) =>
|
||||
renderWeighted(
|
||||
constraint.weighted_variants,
|
||||
variantId =>
|
||||
translateMap(getInstanceById("ProfessionVariant", variantId)?.translations)
|
||||
?.name.default,
|
||||
),
|
||||
).thenW(identity),
|
||||
],
|
||||
).map(notes => baseName + parensIf(ensureNonEmpty(notes.filter(isNotNullish))?.join("; "))),
|
||||
)
|
||||
|
||||
const renderTraditionConstraint = <
|
||||
E extends "MagicalTradition" | "BlessedTradition",
|
||||
>(
|
||||
const renderTraditionConstraint = <E extends "MagicalTradition" | "BlessedTradition">(
|
||||
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"ProfessionVersion">,
|
||||
entity: E,
|
||||
constraint: MagicalTraditionConstraint | BlessedTraditionConstraint,
|
||||
@@ -221,17 +190,11 @@ const renderTraditionConstraint = <
|
||||
renderRarity(constraint.rarity),
|
||||
Reader.asks(({ translateMap }: StdEnv<"tm">) =>
|
||||
renderWeighted(constraint.weighted_professions, profId =>
|
||||
getProfessionName(
|
||||
translateMap,
|
||||
getChildInstancesForInstanceId,
|
||||
profId,
|
||||
),
|
||||
getProfessionName(translateMap, getChildInstancesForInstanceId, profId),
|
||||
),
|
||||
).thenW(identity),
|
||||
]).map(
|
||||
notes =>
|
||||
baseName +
|
||||
parensIf(ensureNonEmpty(notes.filter(isNotNullish))?.join("; ")),
|
||||
notes => baseName + parensIf(ensureNonEmpty(notes.filter(isNotNullish))?.join("; ")),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -239,25 +202,16 @@ const renderCommonProfessions = (
|
||||
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"ProfessionVersion">,
|
||||
commonProfessions: CommonProfessions,
|
||||
): StdReader<
|
||||
| string
|
||||
| [
|
||||
RawEntityDescriptionSectionContent<RawNestedDefinitionListEntityDescriptionSection>,
|
||||
],
|
||||
string | [RawEntityDescriptionSectionContent<RawNestedDefinitionListEntityDescriptionSection>],
|
||||
"t" | "tm" | "lc" | "ibi",
|
||||
"ProfessionVariant" | "BlessedTradition" | "MagicalTradition"
|
||||
> => {
|
||||
switch (commonProfessions.kind) {
|
||||
case "Plain":
|
||||
return renderCommonProfessionConstraints(
|
||||
commonProfessions.Plain,
|
||||
profId =>
|
||||
Reader.asks(({ translateMap }) =>
|
||||
getProfessionName(
|
||||
translateMap,
|
||||
getChildInstancesForInstanceId,
|
||||
profId,
|
||||
),
|
||||
),
|
||||
return renderCommonProfessionConstraints(commonProfessions.Plain, profId =>
|
||||
Reader.asks(({ translateMap }) =>
|
||||
getProfessionName(translateMap, getChildInstancesForInstanceId, profId),
|
||||
),
|
||||
)
|
||||
case "Grouped":
|
||||
return renderCommonProfessionGroup(
|
||||
@@ -266,10 +220,7 @@ const renderCommonProfessions = (
|
||||
constraint =>
|
||||
// switch (constraint.kind) {
|
||||
// case "Profession":
|
||||
renderProfessionConstraint(
|
||||
getChildInstancesForInstanceId,
|
||||
constraint.Profession,
|
||||
),
|
||||
renderProfessionConstraint(getChildInstancesForInstanceId, constraint.Profession),
|
||||
// case "ProfessionSubgroup":
|
||||
// switch (constraint.ProfessionSubgroup.kind) {
|
||||
// case "Profane":
|
||||
@@ -336,11 +287,7 @@ const renderCommonSkills = (
|
||||
items === undefined || !isNotEmpty(items)
|
||||
? translate("none")
|
||||
: items
|
||||
.map(
|
||||
itemId =>
|
||||
translateMap(getInstanceById("Skill", itemId)?.translations)
|
||||
?.name,
|
||||
)
|
||||
.map(itemId => translateMap(getInstanceById("Skill", itemId)?.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
.toSorted(localeCompare)
|
||||
.join(", "),
|
||||
@@ -386,31 +333,25 @@ const renderCommonNames = (
|
||||
|
||||
const renderCulturalPackage = (
|
||||
items: CulturalPackageItem[],
|
||||
): StdReader<
|
||||
{ text: string; apValue: number },
|
||||
"t" | "tm" | "lc" | "ibi",
|
||||
"Skill"
|
||||
> =>
|
||||
): StdReader<{ text: string; apValue: number }, "t" | "tm" | "lc" | "ibi", "Skill"> =>
|
||||
Reader.asks(({ translate, translateMap, getInstanceById, localeCompare }) => {
|
||||
if (!isNotEmpty(items)) {
|
||||
return { text: translate("none"), apValue: 0 }
|
||||
}
|
||||
|
||||
const processedItems = items.map(
|
||||
(item): [text: string, apValue: number] => {
|
||||
const instance = getInstanceById("Skill", item.id)
|
||||
const instanceTranslation = translateMap(instance?.translations)
|
||||
const processedItems = items.map((item): [text: string, apValue: number] => {
|
||||
const instance = getInstanceById("Skill", item.id)
|
||||
const instanceTranslation = translateMap(instance?.translations)
|
||||
|
||||
if (instance === undefined || instanceTranslation === undefined) {
|
||||
return [MISSING_VALUE, 0]
|
||||
}
|
||||
if (instance === undefined || instanceTranslation === undefined) {
|
||||
return [MISSING_VALUE, 0]
|
||||
}
|
||||
|
||||
return [
|
||||
`${instanceTranslation.name ?? MISSING_VALUE} ${sign(item.points)}`,
|
||||
getAdventurePointsForRatingRange(instance.improvement_cost.kind, 0, item.points),
|
||||
]
|
||||
},
|
||||
)
|
||||
return [
|
||||
`${instanceTranslation.name} ${sign(item.points)}`,
|
||||
getAdventurePointsForRatingRange(instance.improvement_cost.kind, 0, item.points),
|
||||
]
|
||||
})
|
||||
|
||||
return {
|
||||
text: processedItems
|
||||
@@ -461,8 +402,9 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
getInstanceById,
|
||||
} satisfies Partial<EnvMap>
|
||||
|
||||
const { text: culturePackageText, apValue: culturalPackageApValue } =
|
||||
renderCulturalPackage(entry.cultural_package).run(env)
|
||||
const { text: culturePackageText, apValue: culturalPackageApValue } = renderCulturalPackage(
|
||||
entry.cultural_package,
|
||||
).run(env)
|
||||
|
||||
return {
|
||||
title: translation.name,
|
||||
@@ -477,14 +419,9 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
entry.language
|
||||
.map(lang => {
|
||||
const language = getInstanceById("Language", lang.id)
|
||||
const languageTranslation = translateMap(
|
||||
language?.translations,
|
||||
)
|
||||
const languageTranslation = translateMap(language?.translations)
|
||||
|
||||
if (
|
||||
language === undefined ||
|
||||
languageTranslation === undefined
|
||||
) {
|
||||
if (language === undefined || languageTranslation === undefined) {
|
||||
return MISSING_VALUE
|
||||
}
|
||||
|
||||
@@ -492,16 +429,11 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
languageTranslation.name +
|
||||
parensIf(
|
||||
[
|
||||
translateMap(
|
||||
language.customSpecializations?.translations,
|
||||
)?.description,
|
||||
translateMap(language.customSpecializations?.translations)?.description,
|
||||
...(lang.specializations?.map(
|
||||
specId =>
|
||||
translateMap(
|
||||
getInstanceById(
|
||||
"LanguageSpecialization",
|
||||
specId,
|
||||
)?.translations,
|
||||
getInstanceById("LanguageSpecialization", specId)?.translations,
|
||||
)?.name,
|
||||
) ?? []),
|
||||
]
|
||||
@@ -521,23 +453,16 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
entry.script === undefined
|
||||
? translate("none")
|
||||
: (() => {
|
||||
const processedScripts = entry.script.map(
|
||||
(scriptId): [string, number] => {
|
||||
const script = getInstanceById("Script", scriptId)
|
||||
const scriptTranslation = translateMap(
|
||||
script?.translations,
|
||||
)
|
||||
const processedScripts = entry.script.map((scriptId): [string, number] => {
|
||||
const script = getInstanceById("Script", scriptId)
|
||||
const scriptTranslation = translateMap(script?.translations)
|
||||
|
||||
if (
|
||||
script === undefined ||
|
||||
scriptTranslation === undefined
|
||||
) {
|
||||
return [MISSING_VALUE, 0]
|
||||
}
|
||||
if (script === undefined || scriptTranslation === undefined) {
|
||||
return [MISSING_VALUE, 0]
|
||||
}
|
||||
|
||||
return [scriptTranslation.name, script.ap_value ?? 0]
|
||||
},
|
||||
)
|
||||
return [scriptTranslation.name, script.ap_value ?? 0]
|
||||
})
|
||||
|
||||
if (!isNotEmpty(processedScripts)) {
|
||||
return translate("none")
|
||||
@@ -551,9 +476,7 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
) {
|
||||
return (
|
||||
localeJoin(
|
||||
processedScripts
|
||||
.map(([name]) => name)
|
||||
.toSorted(localeCompare),
|
||||
processedScripts.map(([name]) => name).toSorted(localeCompare),
|
||||
"disjunction",
|
||||
) +
|
||||
parensIf(
|
||||
@@ -599,9 +522,8 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
value: entry.social_status
|
||||
.map(
|
||||
status =>
|
||||
translateMap(
|
||||
getInstanceById("SocialStatus", status)?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
translateMap(getInstanceById("SocialStatus", status)?.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
)
|
||||
.toSorted(localeCompare)
|
||||
.join(", "),
|
||||
@@ -617,40 +539,28 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
"Common Advantages",
|
||||
entry.common_advantages,
|
||||
values =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Advantage",
|
||||
values,
|
||||
).run(env),
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages("Advantage", values).run(env),
|
||||
translation.common_advantages,
|
||||
).run(env),
|
||||
renderValueWithPossibleTranslation(
|
||||
"Common Disadvantages",
|
||||
entry.common_disadvantages,
|
||||
values =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Disadvantage",
|
||||
values,
|
||||
).run(env),
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages("Disadvantage", values).run(env),
|
||||
translation.common_disadvantages,
|
||||
).run(env),
|
||||
renderValueWithPossibleTranslation(
|
||||
"Uncommon Advantages",
|
||||
entry.uncommon_advantages,
|
||||
values =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Advantage",
|
||||
values,
|
||||
).run(env),
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages("Advantage", values).run(env),
|
||||
translation.uncommon_advantages,
|
||||
).run(env),
|
||||
renderValueWithPossibleTranslation(
|
||||
"Uncommon Disadvantages",
|
||||
entry.uncommon_disadvantages,
|
||||
values =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Disadvantage",
|
||||
values,
|
||||
).run(env),
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages("Disadvantage", values).run(env),
|
||||
translation.uncommon_disadvantages,
|
||||
).run(env),
|
||||
{
|
||||
@@ -672,10 +582,7 @@ export const getCultureEntityDescription = createEntityDescriptionCreator<
|
||||
label:
|
||||
translate("Cultural Package {$cultureName}", {
|
||||
cultureName: translation.name,
|
||||
}) +
|
||||
parensIf(
|
||||
translate("{$value} AP", { value: culturalPackageApValue }),
|
||||
),
|
||||
}) + parensIf(translate("{$value} AP", { value: culturalPackageApValue })),
|
||||
value: {
|
||||
type: "plain",
|
||||
text: culturePackageText,
|
||||
|
||||
+65
-123
@@ -44,24 +44,19 @@ const renderElectiveSpellworks = (
|
||||
case "Specific":
|
||||
return electiveSpellworks.Specific.list
|
||||
.map(item =>
|
||||
mapNullable(
|
||||
translateMap(getInstanceById(item.id)?.translations)?.name,
|
||||
name => {
|
||||
if (item.restriction === undefined) {
|
||||
return name
|
||||
}
|
||||
mapNullable(translateMap(getInstanceById(item.id)?.translations)?.name, name => {
|
||||
if (item.restriction === undefined) {
|
||||
return name
|
||||
}
|
||||
|
||||
return (
|
||||
name +
|
||||
parensIf(
|
||||
translateMap(
|
||||
getInstanceById("Element", item.restriction.Element)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
)
|
||||
return (
|
||||
name +
|
||||
parensIf(
|
||||
translateMap(getInstanceById("Element", item.restriction.Element)?.translations)
|
||||
?.name ?? MISSING_VALUE,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.filter(isNotNullish)
|
||||
.toSorted(localeCompare)
|
||||
@@ -76,36 +71,27 @@ const renderRestrictedSpellworks = (
|
||||
translateMap: TranslateMap,
|
||||
localeCompare: LocaleCompare,
|
||||
localeJoin: LocaleJoin,
|
||||
getInstanceById: GetInstanceById<
|
||||
SpellworkIdentifier["kind"] | "Element" | "Property"
|
||||
>,
|
||||
getInstanceById: GetInstanceById<SpellworkIdentifier["kind"] | "Element" | "Property">,
|
||||
restrictedSpellworks: RestrictedSpellworks,
|
||||
): string => {
|
||||
const restrictedGroups = Dictionary.groupBy(
|
||||
restrictedSpellworks,
|
||||
restriction =>
|
||||
restriction.kind === "Spellwork"
|
||||
? "spellwork"
|
||||
: restriction.kind === "Property" &&
|
||||
restriction.Property.maximum !== undefined
|
||||
? "restrictedProperty"
|
||||
: restriction.kind === "Property" ||
|
||||
restriction.kind === "DemonSummoning"
|
||||
? "property"
|
||||
: "other",
|
||||
const restrictedGroups = Dictionary.groupBy(restrictedSpellworks, restriction =>
|
||||
restriction.kind === "Spellwork"
|
||||
? "spellwork"
|
||||
: restriction.kind === "Property" && restriction.Property.maximum !== undefined
|
||||
? "restrictedProperty"
|
||||
: restriction.kind === "Property" || restriction.kind === "DemonSummoning"
|
||||
? "property"
|
||||
: "other",
|
||||
)
|
||||
|
||||
const addExclusion = (
|
||||
excludedSpellworks: SpellworkIdentifier[] | undefined,
|
||||
) => {
|
||||
const addExclusion = (excludedSpellworks: SpellworkIdentifier[] | undefined) => {
|
||||
if (excludedSpellworks === undefined) {
|
||||
return ""
|
||||
}
|
||||
|
||||
const translatedSpellworks = excludedSpellworks
|
||||
.map(
|
||||
excludedSpellwork =>
|
||||
translateMap(getInstanceById(excludedSpellwork)?.translations)?.name,
|
||||
excludedSpellwork => translateMap(getInstanceById(excludedSpellwork)?.translations)?.name,
|
||||
)
|
||||
.filter(isNotNullish)
|
||||
|
||||
@@ -133,10 +119,8 @@ const renderRestrictedSpellworks = (
|
||||
{
|
||||
count: restriction.Property.maximum,
|
||||
property:
|
||||
translateMap(
|
||||
getInstanceById("Property", restriction.Property.id)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
translateMap(getInstanceById("Property", restriction.Property.id)?.translations)
|
||||
?.name ?? MISSING_VALUE,
|
||||
},
|
||||
) + addExclusion(restriction.Property.exclude),
|
||||
) ?? []
|
||||
@@ -158,10 +142,8 @@ const renderRestrictedSpellworks = (
|
||||
?.map(restriction => {
|
||||
switch (restriction.kind) {
|
||||
case "Property":
|
||||
return translateMap(
|
||||
getInstanceById("Property", restriction.Property.id)
|
||||
?.translations,
|
||||
)?.name
|
||||
return translateMap(getInstanceById("Property", restriction.Property.id)?.translations)
|
||||
?.name
|
||||
case "DemonSummoning":
|
||||
return translate("Demon Summoning")
|
||||
default:
|
||||
@@ -189,11 +171,7 @@ const renderRestrictedSpellworks = (
|
||||
|
||||
const translatedSpellworks =
|
||||
spellworkGroup
|
||||
?.map(
|
||||
restriction =>
|
||||
translateMap(getInstanceById(restriction.Spellwork)?.translations)
|
||||
?.name,
|
||||
)
|
||||
?.map(restriction => translateMap(getInstanceById(restriction.Spellwork)?.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
.toSorted(localeCompare) ?? []
|
||||
|
||||
@@ -229,39 +207,26 @@ const renderRestrictedSpellworks = (
|
||||
case "Borbaradian":
|
||||
return translate("no Borbaradian spellworks")
|
||||
case "DamageIntelligent":
|
||||
return translate(
|
||||
"no spellworks that inflict DP or sDP on intelligent creatures",
|
||||
)
|
||||
return translate("no spellworks that inflict DP or sDP on intelligent creatures")
|
||||
default:
|
||||
return assertExhaustive(restriction)
|
||||
}
|
||||
}) ?? []
|
||||
|
||||
return [
|
||||
...restrictedProperties,
|
||||
...properties,
|
||||
...others,
|
||||
...spellworks,
|
||||
].join("; ")
|
||||
return [...restrictedProperties, ...properties, ...others, ...spellworks].join("; ")
|
||||
}
|
||||
|
||||
const renderSpellworkAdjustment = (
|
||||
translateMap: TranslateMap,
|
||||
getInstanceById: GetInstanceById<
|
||||
SpellworkIdentifier["kind"] | "MagicalTradition"
|
||||
>,
|
||||
getInstanceById: GetInstanceById<SpellworkIdentifier["kind"] | "MagicalTradition">,
|
||||
adjustment: SpellworkAdjustment,
|
||||
): string =>
|
||||
`${
|
||||
translateMap(getInstanceById(adjustment.id)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
} ${adjustment.points}${parensIf(
|
||||
translateMap(getInstanceById(adjustment.id)?.translations)?.name ?? MISSING_VALUE
|
||||
} ${adjustment.points.toFixed()}${parensIf(
|
||||
adjustment.tradition === undefined
|
||||
? undefined
|
||||
: translateMap(
|
||||
getInstanceById("MagicalTradition", adjustment.tradition)
|
||||
?.translations,
|
||||
)?.name,
|
||||
: translateMap(getInstanceById("MagicalTradition", adjustment.tradition)?.translations)?.name,
|
||||
)}`
|
||||
|
||||
const renderAbilityAdjustmentName = (
|
||||
@@ -279,29 +244,24 @@ const renderAbilityAdjustmentName = (
|
||||
switch (abilityAdjustment.kind) {
|
||||
case "Skill":
|
||||
return (
|
||||
translateMap(
|
||||
getInstanceById("Skill", abilityAdjustment.Skill.id)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("Skill", abilityAdjustment.Skill.id)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
)
|
||||
case "CombatTechnique":
|
||||
return (
|
||||
translateMap(
|
||||
getInstanceById(abilityAdjustment.CombatTechnique.id)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById(abilityAdjustment.CombatTechnique.id)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
)
|
||||
case "Spellwork":
|
||||
return (
|
||||
(translateMap(
|
||||
getInstanceById(abilityAdjustment.Spellwork.id)?.translations,
|
||||
)?.name ?? MISSING_VALUE) +
|
||||
(translateMap(getInstanceById(abilityAdjustment.Spellwork.id)?.translations)?.name ??
|
||||
MISSING_VALUE) +
|
||||
parensIf(
|
||||
abilityAdjustment.Spellwork.tradition === undefined
|
||||
? undefined
|
||||
: translateMap(
|
||||
getInstanceById(
|
||||
"MagicalTradition",
|
||||
abilityAdjustment.Spellwork.tradition,
|
||||
)?.translations,
|
||||
getInstanceById("MagicalTradition", abilityAdjustment.Spellwork.tradition)
|
||||
?.translations,
|
||||
)?.name,
|
||||
)
|
||||
)
|
||||
@@ -316,9 +276,8 @@ const renderAbilityAdjustmentBaseValue = (
|
||||
) => {
|
||||
switch (abilityAdjustment.kind) {
|
||||
case "Skill":
|
||||
return baseProfessionPackage.skills?.find(
|
||||
skill => skill.id === abilityAdjustment.Skill.id,
|
||||
)?.rating_modifier
|
||||
return baseProfessionPackage.skills?.find(skill => skill.id === abilityAdjustment.Skill.id)
|
||||
?.rating_modifier
|
||||
case "CombatTechnique":
|
||||
return baseProfessionPackage.combat_techniques?.find(skill =>
|
||||
deepEqual(skill.id, abilityAdjustment.CombatTechnique.id),
|
||||
@@ -336,9 +295,7 @@ const renderAbilityAdjustmentBaseValue = (
|
||||
}
|
||||
}
|
||||
|
||||
const renderAbilityAdjustmentModifierValue = (
|
||||
abilityAdjustment: AbilityAdjustment,
|
||||
) => {
|
||||
const renderAbilityAdjustmentModifierValue = (abilityAdjustment: AbilityAdjustment) => {
|
||||
switch (abilityAdjustment.kind) {
|
||||
case "Skill":
|
||||
return abilityAdjustment.Skill.points
|
||||
@@ -351,9 +308,7 @@ const renderAbilityAdjustmentModifierValue = (
|
||||
}
|
||||
}
|
||||
|
||||
const renderAbilityAdjustmentDefaultValue = (
|
||||
abilityAdjustment: AbilityAdjustment,
|
||||
) => {
|
||||
const renderAbilityAdjustmentDefaultValue = (abilityAdjustment: AbilityAdjustment) => {
|
||||
switch (abilityAdjustment.kind) {
|
||||
case "Skill":
|
||||
return 0
|
||||
@@ -388,17 +343,15 @@ const renderAbilityAdjustment = (
|
||||
)} ${sign(renderAbilityAdjustmentModifierValue(abilityAdjustment))}`
|
||||
: abilityAdjustment => {
|
||||
const basePoints =
|
||||
(renderAbilityAdjustmentBaseValue(
|
||||
baseProfessionPackage,
|
||||
abilityAdjustment,
|
||||
) ?? 0) + renderAbilityAdjustmentDefaultValue(abilityAdjustment)
|
||||
(renderAbilityAdjustmentBaseValue(baseProfessionPackage, abilityAdjustment) ?? 0) +
|
||||
renderAbilityAdjustmentDefaultValue(abilityAdjustment)
|
||||
return translate("{$replacement} instead of {$base}", {
|
||||
base: basePoints,
|
||||
replacement: `${renderAbilityAdjustmentName(
|
||||
translateMap,
|
||||
getInstanceById,
|
||||
abilityAdjustment,
|
||||
)} ${basePoints + renderAbilityAdjustmentModifierValue(abilityAdjustment)}`,
|
||||
)} ${(basePoints + renderAbilityAdjustmentModifierValue(abilityAdjustment)).toFixed()}`,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -418,16 +371,9 @@ const renderAbilityAdjustments = (
|
||||
list: AbilityAdjustment[],
|
||||
) =>
|
||||
list
|
||||
?.map(
|
||||
renderAbilityAdjustment(
|
||||
translate,
|
||||
translateMap,
|
||||
getInstanceById,
|
||||
baseProfessionPackage,
|
||||
),
|
||||
)
|
||||
.map(renderAbilityAdjustment(translate, translateMap, getInstanceById, baseProfessionPackage))
|
||||
.toSorted(localeCompare)
|
||||
.join(", ") ?? translate("none")
|
||||
.join(", ")
|
||||
|
||||
/**
|
||||
* Get a JSON representation of the rules text for a curriculum.
|
||||
@@ -482,9 +428,8 @@ export const getCurriculumEntityDescription = createEntityDescriptionCreator<
|
||||
{
|
||||
label: translate("Guideline"),
|
||||
value:
|
||||
translateMap(
|
||||
getInstanceById("Guideline", entry.guideline)?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
translateMap(getInstanceById("Guideline", entry.guideline)?.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
},
|
||||
{
|
||||
label: translate("Elective Spellworks Package"),
|
||||
@@ -522,21 +467,18 @@ export const getCurriculumEntityDescription = createEntityDescriptionCreator<
|
||||
(
|
||||
lessonPackageTranslation,
|
||||
): LabeledEntityDescriptionSection<RawEntityDescriptionSectionContent> => {
|
||||
const [boni, mali] = partition(
|
||||
lessonPackage.content.skills ?? [],
|
||||
adjustment => {
|
||||
switch (adjustment.kind) {
|
||||
case "Skill":
|
||||
return adjustment.Skill.points > 0
|
||||
case "CombatTechnique":
|
||||
return adjustment.CombatTechnique.points > 0
|
||||
case "Spellwork":
|
||||
return adjustment.Spellwork.points > 0
|
||||
default:
|
||||
return assertExhaustive(adjustment)
|
||||
}
|
||||
},
|
||||
)
|
||||
const [boni, mali] = partition(lessonPackage.content.skills ?? [], adjustment => {
|
||||
switch (adjustment.kind) {
|
||||
case "Skill":
|
||||
return adjustment.Skill.points > 0
|
||||
case "CombatTechnique":
|
||||
return adjustment.CombatTechnique.points > 0
|
||||
case "Spellwork":
|
||||
return adjustment.Spellwork.points > 0
|
||||
default:
|
||||
return assertExhaustive(adjustment)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
type: "labeled",
|
||||
@@ -547,7 +489,7 @@ export const getCurriculumEntityDescription = createEntityDescriptionCreator<
|
||||
{
|
||||
label: translate("Spellwork Changes"),
|
||||
value:
|
||||
lessonPackageTranslation?.spellwork_changes ??
|
||||
lessonPackageTranslation.spellwork_changes ??
|
||||
lessonPackage.content.spellwork_changes
|
||||
?.map(change =>
|
||||
translate("{$replacement} instead of {$base}", {
|
||||
|
||||
@@ -72,14 +72,9 @@ const renderBaseCalculation = (
|
||||
renderMathOperation(calculation, value => {
|
||||
switch (value.kind) {
|
||||
case "Constant":
|
||||
return value.Constant.toString(10)
|
||||
return value.Constant.toFixed()
|
||||
case "Attribute":
|
||||
return getAttribute(
|
||||
getInstanceById,
|
||||
translateMap,
|
||||
value.Attribute,
|
||||
style,
|
||||
)
|
||||
return getAttribute(getInstanceById, translateMap, value.Attribute, style)
|
||||
case "RaceBaseValue":
|
||||
return getRaceBaseValue(
|
||||
translate,
|
||||
@@ -94,13 +89,9 @@ const renderBaseCalculation = (
|
||||
case "full":
|
||||
switch (value.PrimaryAttribute.kind) {
|
||||
case "Magical":
|
||||
return translate(
|
||||
"Primary attribute for the magic user’s Tradition",
|
||||
)
|
||||
return translate("Primary attribute for the magic user’s Tradition")
|
||||
case "Blessed":
|
||||
return translate(
|
||||
"Primary attribute for the Blessed One’s Tradition",
|
||||
)
|
||||
return translate("Primary attribute for the Blessed One’s Tradition")
|
||||
default:
|
||||
return assertExhaustive(value.PrimaryAttribute)
|
||||
}
|
||||
@@ -117,53 +108,44 @@ const renderBaseCalculation = (
|
||||
/**
|
||||
* Get a JSON representation of the rules text for a derived characteristic.
|
||||
*/
|
||||
export const getDerivedCharacteristicEntityDescription =
|
||||
createEntityDescriptionCreator<
|
||||
"DerivedCharacteristic",
|
||||
{
|
||||
getInstanceById: GetInstanceById<
|
||||
"Publication" | "Attribute" | "DerivedCharacteristic"
|
||||
>
|
||||
idMap: IdMap
|
||||
}
|
||||
>(
|
||||
(
|
||||
{ getInstanceById, idMap },
|
||||
{ translate, translateMap },
|
||||
{ content: entry },
|
||||
) => {
|
||||
const translation = translateMap(entry.translations)
|
||||
export const getDerivedCharacteristicEntityDescription = createEntityDescriptionCreator<
|
||||
"DerivedCharacteristic",
|
||||
{
|
||||
getInstanceById: GetInstanceById<"Publication" | "Attribute" | "DerivedCharacteristic">
|
||||
idMap: IdMap
|
||||
}
|
||||
>(({ getInstanceById, idMap }, { translate, translateMap }, { content: entry }) => {
|
||||
const translation = translateMap(entry.translations)
|
||||
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${translation.name} (${translation.abbreviation})`,
|
||||
className: "derived-characteristic",
|
||||
body: [
|
||||
translation.description === undefined
|
||||
? undefined
|
||||
: {
|
||||
type: "plain",
|
||||
text: translation.description,
|
||||
},
|
||||
return {
|
||||
title: `${translation.name} (${translation.abbreviation})`,
|
||||
className: "derived-characteristic",
|
||||
body: [
|
||||
translation.description === undefined
|
||||
? undefined
|
||||
: {
|
||||
type: "plain",
|
||||
text: translation.description,
|
||||
},
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
{
|
||||
label: translate("Base Value"),
|
||||
value: renderBaseCalculation(
|
||||
getInstanceById,
|
||||
translate,
|
||||
translateMap,
|
||||
idMap,
|
||||
entry.calculation.base,
|
||||
),
|
||||
},
|
||||
],
|
||||
label: translate("Base Value"),
|
||||
value: renderBaseCalculation(
|
||||
getInstanceById,
|
||||
translate,
|
||||
translateMap,
|
||||
idMap,
|
||||
entry.calculation.base,
|
||||
),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
+8
-23
@@ -10,18 +10,10 @@ import type {
|
||||
} from "optolith-database-schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type { GetInstanceById } from "../helpers/getTypes.js"
|
||||
import type {
|
||||
LocaleMap,
|
||||
Translate,
|
||||
TranslateMap,
|
||||
} from "../helpers/translate.js"
|
||||
import type { LocaleMap, Translate, TranslateMap } from "../helpers/translate.js"
|
||||
import type { IdMap } from "../index.js"
|
||||
import { renderAnimalTypesSection } from "./partial/animalTypes.js"
|
||||
import {
|
||||
renderAlternativeNames,
|
||||
renderChance,
|
||||
renderResistance,
|
||||
} from "./partial/herbary.js"
|
||||
import { renderAlternativeNames, renderChance, renderResistance } from "./partial/herbary.js"
|
||||
import { parensIf } from "./partial/rated/activatable/parensIf.js"
|
||||
import { MISSING_VALUE } from "./partial/unknown.js"
|
||||
|
||||
@@ -46,11 +38,7 @@ type BaseDiseaseTranslation = {
|
||||
errata?: Errata
|
||||
}
|
||||
|
||||
const renderCauses = (
|
||||
translate: Translate,
|
||||
translateMap: TranslateMap,
|
||||
causes: Cause[],
|
||||
) =>
|
||||
const renderCauses = (translate: Translate, translateMap: TranslateMap, causes: Cause[]) =>
|
||||
causes
|
||||
.map(cause => {
|
||||
const causeTranslation = translateMap(cause.translations)
|
||||
@@ -63,10 +51,9 @@ const renderCauses = (
|
||||
causeTranslation.name +
|
||||
parensIf(
|
||||
ensureNonEmpty(
|
||||
[
|
||||
renderChance(translate, translateMap, cause, true),
|
||||
causeTranslation.note,
|
||||
].filter(isNotNullish),
|
||||
[renderChance(translate, translateMap, cause, true), causeTranslation.note].filter(
|
||||
isNotNullish,
|
||||
),
|
||||
)?.join("; "),
|
||||
)
|
||||
)
|
||||
@@ -79,9 +66,7 @@ const renderCauses = (
|
||||
export const getDiseaseEntityDescription = createEntityDescriptionCreator<
|
||||
"AnimalDisease" | "Disease",
|
||||
{
|
||||
getInstanceById: GetInstanceById<
|
||||
"Publication" | "AnimalType" | "DerivedCharacteristic"
|
||||
>
|
||||
getInstanceById: GetInstanceById<"Publication" | "AnimalType" | "DerivedCharacteristic">
|
||||
idMap: IdMap
|
||||
}
|
||||
>(
|
||||
@@ -105,7 +90,7 @@ export const getDiseaseEntityDescription = createEntityDescriptionCreator<
|
||||
type: "definitionList",
|
||||
items: [
|
||||
renderAlternativeNames(translate, translation.alternative_names),
|
||||
{ label: translate("Level"), value: baseEntry.level.toString() },
|
||||
{ label: translate("Level"), value: baseEntry.level.toFixed() },
|
||||
{ label: translate("Progress"), value: translation.progress },
|
||||
{
|
||||
label: translate("Resistance"),
|
||||
|
||||
+82
-102
@@ -1,15 +1,9 @@
|
||||
import { mapNullable } from "@elyukai/utils/nullable"
|
||||
import { sign } from "@elyukai/utils/string/number"
|
||||
import type {
|
||||
ActivatableIdentifier,
|
||||
RatedIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { ActivatableIdentifier, RatedIdentifier } from "optolith-database-schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type { GetInstanceById } from "../helpers/getTypes.js"
|
||||
import {
|
||||
renderAlternativeNames,
|
||||
renderLaboratoryLevel,
|
||||
} from "./partial/herbary.js"
|
||||
import { renderAlternativeNames, renderLaboratoryLevel } from "./partial/herbary.js"
|
||||
import { printPlainGeneralPrerequisites } from "./partial/prerequisites/index.js"
|
||||
import type { GetResolvedSelectOptionById } from "./partial/prerequisites/single/activatable.js"
|
||||
import { parensIf } from "./partial/rated/activatable/parensIf.js"
|
||||
@@ -37,100 +31,86 @@ export const getElixirEntityDescription = createEntityDescriptionCreator<
|
||||
>
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById
|
||||
}
|
||||
>(
|
||||
(
|
||||
{ getInstanceById, getResolvedSelectOptionById },
|
||||
locale,
|
||||
{ content: entry },
|
||||
) => {
|
||||
const { translate, translateMap } = locale
|
||||
const translation = translateMap(entry.translations)
|
||||
>(({ getInstanceById, getResolvedSelectOptionById }, locale, { content: entry }) => {
|
||||
const { translate, translateMap } = locale
|
||||
const translation = translateMap(entry.translations)
|
||||
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
title: translation.name,
|
||||
className: "elixir",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
renderAlternativeNames(translate, translation.alternative_names),
|
||||
{
|
||||
label: translate("Typical Ingredients"),
|
||||
value: translation.typical_ingredients.join(", "),
|
||||
},
|
||||
{
|
||||
label: translate("Price of Ingredients/Level"),
|
||||
value: translate(
|
||||
".input {$value :number} {{{$value} silverthalers}}",
|
||||
{ value: entry.cost_per_ingredient_level },
|
||||
),
|
||||
},
|
||||
{
|
||||
label: translate("Laboratory"),
|
||||
value: renderLaboratoryLevel(translate, entry.laboratory),
|
||||
},
|
||||
{
|
||||
label: translate("Brewing Difficulty"),
|
||||
value: sign(entry.brewing_difficulty),
|
||||
},
|
||||
{
|
||||
label: `${translate("Prerequisites")} (${translate(
|
||||
"Brewing Process",
|
||||
)})`,
|
||||
value:
|
||||
translation?.brewing_process_prerequisites ?? translate("none"),
|
||||
},
|
||||
{
|
||||
label: `${translate("AP Value")} (${translate("Trade Secret")})`,
|
||||
value:
|
||||
translate("{$value} AP", {
|
||||
value: entry.trade_secret.ap_value,
|
||||
}) +
|
||||
parensIf(
|
||||
mapNullable(
|
||||
entry.trade_secret.prerequisites,
|
||||
prerequisites =>
|
||||
`${translate(
|
||||
"Prerequisites",
|
||||
)}: ${printPlainGeneralPrerequisites(
|
||||
getInstanceById,
|
||||
getResolvedSelectOptionById,
|
||||
locale,
|
||||
prerequisites,
|
||||
)}`,
|
||||
),
|
||||
return {
|
||||
title: translation.name,
|
||||
className: "elixir",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
renderAlternativeNames(translate, translation.alternative_names),
|
||||
{
|
||||
label: translate("Typical Ingredients"),
|
||||
value: translation.typical_ingredients.join(", "),
|
||||
},
|
||||
{
|
||||
label: translate("Price of Ingredients/Level"),
|
||||
value: translate(".input {$value :number} {{{$value} silverthalers}}", {
|
||||
value: entry.cost_per_ingredient_level,
|
||||
}),
|
||||
},
|
||||
{
|
||||
label: translate("Laboratory"),
|
||||
value: renderLaboratoryLevel(translate, entry.laboratory),
|
||||
},
|
||||
{
|
||||
label: translate("Brewing Difficulty"),
|
||||
value: sign(entry.brewing_difficulty),
|
||||
},
|
||||
{
|
||||
label: `${translate("Prerequisites")} (${translate("Brewing Process")})`,
|
||||
value: translation.brewing_process_prerequisites ?? translate("none"),
|
||||
},
|
||||
{
|
||||
label: `${translate("AP Value")} (${translate("Trade Secret")})`,
|
||||
value:
|
||||
translate("{$value} AP", {
|
||||
value: entry.trade_secret.ap_value,
|
||||
}) +
|
||||
parensIf(
|
||||
mapNullable(
|
||||
entry.trade_secret.prerequisites,
|
||||
prerequisites =>
|
||||
`${translate("Prerequisites")}: ${printPlainGeneralPrerequisites(
|
||||
getInstanceById,
|
||||
getResolvedSelectOptionById,
|
||||
locale,
|
||||
prerequisites,
|
||||
)}`,
|
||||
),
|
||||
},
|
||||
translation.special === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Special"),
|
||||
value: translation.special,
|
||||
},
|
||||
{
|
||||
label: translate("Quality Levels"),
|
||||
value: [
|
||||
{
|
||||
type: "definitionList",
|
||||
style: "nested",
|
||||
items: translation.quality_levels.map(
|
||||
(effectForLevel, index) => ({
|
||||
label: (index + 1).toString(),
|
||||
value: effectForLevel,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
errata: translation.errata,
|
||||
references: entry.src,
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
},
|
||||
translation.special === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Special"),
|
||||
value: translation.special,
|
||||
},
|
||||
{
|
||||
label: translate("Quality Levels"),
|
||||
value: [
|
||||
{
|
||||
type: "definitionList",
|
||||
style: "nested",
|
||||
items: translation.quality_levels.map((effectForLevel, index) => ({
|
||||
label: (index + 1).toFixed(),
|
||||
value: effectForLevel,
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
errata: translation.errata,
|
||||
references: entry.src,
|
||||
}
|
||||
})
|
||||
|
||||
+22
-16
@@ -50,7 +50,7 @@ import type {
|
||||
} from "optolith-database-schema/gen"
|
||||
import { createEntityDescriptionCreator, type TaggedEntity } from "../creator.js"
|
||||
import type { GetInstanceById } from "../helpers/getTypes.js"
|
||||
import type { LocaleCompare, LocaleJoin } from "../helpers/locale.js"
|
||||
import type { FormatNumber, LocaleCompare, LocaleJoin } from "../helpers/locale.js"
|
||||
import type { LocaleMap, Translate, TranslateMap } from "../helpers/translate.js"
|
||||
import type {
|
||||
IdMap,
|
||||
@@ -140,14 +140,16 @@ const renderPrimaryAttributeAndDamageThreshold = (
|
||||
case "Default":
|
||||
return `${
|
||||
closeCombatTechnique?.primary_attribute.map(getAttrAbbrv).join("/") ?? MISSING_VALUE
|
||||
} ${damageThreshold.Default.threshold}`
|
||||
} ${damageThreshold.Default.threshold.toFixed()}`
|
||||
case "List":
|
||||
if (isNotEmpty(damageThreshold.List.list)) {
|
||||
const { list } = damageThreshold.List
|
||||
if (list.some(item => item.threshold !== list[0].threshold)) {
|
||||
return list.map(item => `${getAttrAbbrv(item.attribute)} ${item.threshold}`).join("/")
|
||||
return list
|
||||
.map(item => `${getAttrAbbrv(item.attribute)} ${item.threshold.toFixed()}`)
|
||||
.join("/")
|
||||
} else {
|
||||
return `${list.map(item => getAttrAbbrv(item.attribute)).join("/")} ${list[0].threshold}`
|
||||
return `${list.map(item => getAttrAbbrv(item.attribute)).join("/")} ${list[0].threshold.toFixed()}`
|
||||
}
|
||||
} else {
|
||||
return MISSING_VALUE
|
||||
@@ -263,7 +265,7 @@ const renderReloadTime = (translate: Translate, reloadTime: ReloadTime[]) =>
|
||||
: MISSING_VALUE
|
||||
|
||||
const renderRangeBrackets = (rangeBrackets: RangeBrackets) =>
|
||||
`${rangeBrackets.close}/${rangeBrackets.medium}/${rangeBrackets.far}`
|
||||
`${rangeBrackets.close.toFixed()}/${rangeBrackets.medium.toFixed()}/${rangeBrackets.far.toFixed()}`
|
||||
|
||||
const renderAmmunition = (
|
||||
translateMap: TranslateMap,
|
||||
@@ -596,6 +598,7 @@ const renderNote = (
|
||||
|
||||
const renderWeight = (
|
||||
translate: Translate,
|
||||
formatNumber: FormatNumber,
|
||||
measurements: Required<LocaleMeasurementAdjustments>,
|
||||
weight: Weight | JewelryMaterialDifference<Weight>,
|
||||
): RawDefinitionListEntityDescriptionSectionItem => {
|
||||
@@ -611,7 +614,7 @@ const renderWeight = (
|
||||
label: translate("Weight (Bronze/Silver/Gold)"),
|
||||
value: translate("{$value} pounds", {
|
||||
value: [weight.bronze, weight.silver, weight.gold]
|
||||
.map(value => value * measurements.stonesMultiplier)
|
||||
.map(value => formatNumber(value * measurements.stonesMultiplier))
|
||||
.join("/"),
|
||||
}),
|
||||
}
|
||||
@@ -621,14 +624,15 @@ const renderWeight = (
|
||||
const renderCost = (
|
||||
translate: Translate,
|
||||
translateMap: TranslateMap,
|
||||
cost: Cost | BookCost | JewelryMaterialDifference<Cost>,
|
||||
): RawDefinitionListEntityDescriptionSectionItem => {
|
||||
formatNumber: FormatNumber,
|
||||
cost: Cost | BookCost | JewelryMaterialDifference<number>,
|
||||
): { label: string; value: string } => {
|
||||
const renderBookCostVariant = (bookCostVariant: BookCostVariant) => {
|
||||
switch (bookCostVariant.kind) {
|
||||
case "Definite": {
|
||||
const translation = translateMap(bookCostVariant.Definite.translations)
|
||||
return (
|
||||
renderCost(translate, translateMap, bookCostVariant.Definite.cost) +
|
||||
renderCost(translate, translateMap, formatNumber, bookCostVariant.Definite.cost).value +
|
||||
parensIf(translation?.label)
|
||||
)
|
||||
}
|
||||
@@ -645,7 +649,7 @@ const renderCost = (
|
||||
return {
|
||||
label: translate("Cost (Bronze/Silver/Gold)"),
|
||||
value: translate("{$value} silverthalers", {
|
||||
value: [cost.bronze, cost.silver, cost.gold].join("/"),
|
||||
value: [cost.bronze, cost.silver, cost.gold].map(formatNumber).join("/"),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
@@ -704,8 +708,8 @@ const renderArmorValues = (
|
||||
idMap: IdMap,
|
||||
values: NormalizedArmorValues,
|
||||
): RawDefinitionListEntityDescriptionSectionItem[] => [
|
||||
{ label: translate("Protection"), value: values.protection.toString() },
|
||||
{ label: translate("Encumbrance"), value: values.encumbrance.toString() },
|
||||
{ label: translate("Protection"), value: values.protection.toFixed() },
|
||||
{ label: translate("Encumbrance"), value: values.encumbrance.toFixed() },
|
||||
{
|
||||
label: translate("Additional Penalties"),
|
||||
value: values.has_additional_penalties
|
||||
@@ -724,7 +728,7 @@ const renderArmorValues = (
|
||||
]
|
||||
|
||||
type BaseItem = {
|
||||
cost?: Cost | BookCost | JewelryMaterialDifference<Cost>
|
||||
cost?: Cost | BookCost | JewelryMaterialDifference<number>
|
||||
weight?: Weight | JewelryMaterialDifference<Weight>
|
||||
complexity?: ArmorComplexity | Complexity
|
||||
structure_points?: StructurePoints
|
||||
@@ -960,7 +964,7 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator<
|
||||
baseItemTranslation?.language !== undefined || baseItemTranslation?.script !== undefined
|
||||
? {
|
||||
label: translate("Language/Script"),
|
||||
value: [baseItemTranslation?.language, baseItemTranslation?.script]
|
||||
value: [baseItemTranslation.language, baseItemTranslation.script]
|
||||
.map(value => value ?? "—")
|
||||
.join(" / "),
|
||||
}
|
||||
@@ -979,9 +983,11 @@ export const getEquipmentEntityDescription = createEntityDescriptionCreator<
|
||||
}
|
||||
: undefined,
|
||||
mapNullable(baseItem.weight, weight =>
|
||||
renderWeight(translate, locale.measurementAdjustments, weight),
|
||||
renderWeight(translate, locale.formatNumber, locale.measurementAdjustments, weight),
|
||||
),
|
||||
mapNullable(baseItem.cost, cost =>
|
||||
renderCost(translate, translateMap, locale.formatNumber, cost),
|
||||
),
|
||||
mapNullable(baseItem.cost, cost => renderCost(translate, translateMap, cost)),
|
||||
mapNullable(
|
||||
renderNote(
|
||||
translate,
|
||||
|
||||
@@ -129,12 +129,12 @@ const getAtomicEquipmentCost = (entry: TaggedEntity<EquipmentIdentifier["kind"]>
|
||||
}
|
||||
}
|
||||
case "Jewelry": {
|
||||
const costs: Cost[] = [
|
||||
const costs: AtomicCost[] = [
|
||||
entry.content.cost.bronze,
|
||||
entry.content.cost.silver,
|
||||
entry.content.cost.gold,
|
||||
]
|
||||
return costs.map(getAtomicCost).reduce(rangeAtomicEquipmentCost)
|
||||
return costs.reduce(rangeAtomicEquipmentCost)
|
||||
}
|
||||
case "Ammunition":
|
||||
case "Animal":
|
||||
@@ -212,7 +212,8 @@ const getAtomicEquipmentWeight = (
|
||||
return assertExhaustive(entry.content.type)
|
||||
}
|
||||
case "Jewelry": {
|
||||
const values = Object.values(entry.content.weight)
|
||||
const { bronze, silver, gold } = entry.content.weight
|
||||
const values = [bronze, silver, gold]
|
||||
return [Math.min(...values), Math.max(...values)]
|
||||
}
|
||||
case "Armor":
|
||||
@@ -313,7 +314,7 @@ export const getEquipmentPackageEntityDescription = createEntityDescriptionCreat
|
||||
item,
|
||||
): item is EquipmentPackageItem & {
|
||||
content: TaggedEntity<EquipmentIdentifier["kind"]>
|
||||
} => item.content !== undefined,
|
||||
} => item.content.content !== undefined,
|
||||
) ?? []
|
||||
|
||||
return {
|
||||
|
||||
@@ -21,31 +21,31 @@ export const getExperienceLevelEntityDescription =
|
||||
items: [
|
||||
{
|
||||
label: translate("Adventure Points"),
|
||||
value: entry.adventure_points.toString(),
|
||||
value: entry.adventure_points.toFixed(),
|
||||
},
|
||||
{
|
||||
label: translate("Maximum Attribute Value"),
|
||||
value: entry.max_attribute_value.toString(),
|
||||
value: entry.max_attribute_value.toFixed(),
|
||||
},
|
||||
{
|
||||
label: translate("Maximum Skill Value"),
|
||||
value: entry.max_skill_rating.toString(),
|
||||
value: entry.max_skill_rating.toFixed(),
|
||||
},
|
||||
{
|
||||
label: translate("Maximum Combat Technique"),
|
||||
value: entry.max_combat_technique_rating.toString(),
|
||||
value: entry.max_combat_technique_rating.toFixed(),
|
||||
},
|
||||
{
|
||||
label: translate("Maximum Attribute Total"),
|
||||
value: entry.max_attribute_total.toString(),
|
||||
value: entry.max_attribute_total.toFixed(),
|
||||
},
|
||||
{
|
||||
label: translate("Number of Spells/Liturgical Chants"),
|
||||
value: entry.max_number_of_spells_liturgical_chants.toString(),
|
||||
value: entry.max_number_of_spells_liturgical_chants.toFixed(),
|
||||
},
|
||||
{
|
||||
label: translate("Number from other Traditions"),
|
||||
value: entry.max_number_of_unfamiliar_spells.toString(),
|
||||
value: entry.max_number_of_unfamiliar_spells.toFixed(),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -44,10 +44,10 @@ export const getInfluenceEntityDescription = createEntityDescriptionCreator<
|
||||
: {
|
||||
type: "definitionList",
|
||||
items: [
|
||||
...(translation.effects?.map(effect => ({
|
||||
...translation.effects.map(effect => ({
|
||||
label: effect.label,
|
||||
value: effect.text,
|
||||
})) ?? []),
|
||||
})),
|
||||
entry.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Compare } from "@optolith/helpers/compare"
|
||||
import type { Compare } from "@optolith/helpers/compare"
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { type LiturgyTradition, type RatedIdentifier } from "optolith-database-schema/gen"
|
||||
import { Case } from "tsondb/schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type { GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js"
|
||||
import { Translate, TranslateMap, type TranslationKeysWithoutParams } from "../helpers/translate.js"
|
||||
import { type IdMap, type RawDefinitionListEntityDescriptionSectionItem } from "../index.js"
|
||||
import type { Translate, TranslateMap, TranslationKeysWithoutParams } from "../helpers/translate.js"
|
||||
import type { IdMap, RawDefinitionListEntityDescriptionSectionItem } from "../index.js"
|
||||
import { renderEnhancements } from "./partial/enhancements.js"
|
||||
import { renderOneTimeDuration } from "./partial/rated/activatable/duration.js"
|
||||
import { renderEffect } from "./partial/rated/activatable/effect.js"
|
||||
|
||||
@@ -14,22 +14,15 @@ import type {
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { GetAllInstances } from "../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../helpers/locale.js"
|
||||
import type {
|
||||
BaseActivatable,
|
||||
BaseActivatableTranslation,
|
||||
} from "../activatable.js"
|
||||
import type { BaseActivatable, BaseActivatableTranslation } from "../activatable.js"
|
||||
import { evaluateMathOperation } from "./mathOperation.js"
|
||||
import { MISSING_VALUE } from "./unknown.js"
|
||||
|
||||
const renderSelectOptionsAdventurePointsValue = <
|
||||
T extends ResolvedSelectOptionIdentifier,
|
||||
>(
|
||||
const renderSelectOptionsAdventurePointsValue = <T extends ResolvedSelectOptionIdentifier>(
|
||||
locale: LocaleEnvironment,
|
||||
derivedLabel: () => string,
|
||||
getAllSelectOptions: () => ResolvedSelectOption[],
|
||||
getNameForSelectOptionId: (
|
||||
id: ResolvedSelectOptionIdentifier,
|
||||
) => string | undefined,
|
||||
getNameForSelectOptionId: (id: ResolvedSelectOptionIdentifier) => string | undefined,
|
||||
config: SelectOptionsAdventurePointsValue<T> | undefined,
|
||||
) => {
|
||||
if (config === undefined) {
|
||||
@@ -49,13 +42,11 @@ const renderSelectOptionsAdventurePointsValue = <
|
||||
}
|
||||
case "Fixed":
|
||||
return Map.groupBy(
|
||||
getAllSelectOptions().map(
|
||||
(item): [apValue: number, item: ResolvedSelectOption] => [
|
||||
config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))
|
||||
?.ap_value ?? config.Fixed.default,
|
||||
item,
|
||||
],
|
||||
),
|
||||
getAllSelectOptions().map((item): [apValue: number, item: ResolvedSelectOption] => [
|
||||
config.Fixed.map.find(mapItem => deepEqual(mapItem.id, item.id))?.ap_value ??
|
||||
config.Fixed.default,
|
||||
item,
|
||||
]),
|
||||
item => item[0],
|
||||
)
|
||||
.entries()
|
||||
@@ -87,9 +78,7 @@ const renderSelectOptionsAdventurePointsValue = <
|
||||
export const renderAdventurePointsValue = (
|
||||
locale: LocaleEnvironment,
|
||||
getNameForId: (id: ActivatableIdentifier) => string | undefined,
|
||||
getNameForSelectOptionId: (
|
||||
id: ResolvedSelectOptionIdentifier,
|
||||
) => string | undefined,
|
||||
getNameForSelectOptionId: (id: ResolvedSelectOptionIdentifier) => string | undefined,
|
||||
getAllSelectOptions: () => ResolvedSelectOption[],
|
||||
getAllInstances: GetAllInstances<"Script" | "AnimalShapeSize">,
|
||||
value: AdventurePointsValue | number,
|
||||
@@ -106,15 +95,13 @@ export const renderAdventurePointsValue = (
|
||||
switch (value.kind) {
|
||||
case "Fixed": {
|
||||
if (entry.levels === undefined) {
|
||||
return translate(
|
||||
".input {$value :number} {{{$value} Adventure Points}}",
|
||||
{ value: value.Fixed },
|
||||
)
|
||||
return translate(".input {$value :number} {{{$value} Adventure Points}}", {
|
||||
value: value.Fixed,
|
||||
})
|
||||
} else {
|
||||
return translate(
|
||||
".input {$value :number} {{{$value} Adventure Points per level}}",
|
||||
{ value: value.Fixed },
|
||||
)
|
||||
return translate(".input {$value :number} {{{$value} Adventure Points per level}}", {
|
||||
value: value.Fixed,
|
||||
})
|
||||
}
|
||||
}
|
||||
case "ByLevel": {
|
||||
@@ -123,28 +110,18 @@ export const renderAdventurePointsValue = (
|
||||
}
|
||||
|
||||
const mainValue = `${translate("Level {$level}", {
|
||||
level: Array.from({ length: entry.levels }, (_, index) =>
|
||||
romanize(index + 1),
|
||||
).join("/"),
|
||||
level: Array.from({ length: entry.levels }, (_, index) => romanize(index + 1)).join("/"),
|
||||
})}: ${value.ByLevel.list.join("/")}`
|
||||
|
||||
const { additionalBySizeCategory } = value.ByLevel
|
||||
if (additionalBySizeCategory === undefined) {
|
||||
return mainValue
|
||||
} else {
|
||||
const sizeCategories = [
|
||||
"tiny",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"huge",
|
||||
] as const
|
||||
const sizeCategories = ["tiny", "small", "medium", "large", "huge"] as const
|
||||
const values = sizeCategories
|
||||
.map(sizeCategory => additionalBySizeCategory[sizeCategory])
|
||||
.join("/")
|
||||
const labels = sizeCategories
|
||||
.map(sizeCategory => translate(sizeCategory))
|
||||
.join("/")
|
||||
const labels = sizeCategories.map(sizeCategory => translate(sizeCategory)).join("/")
|
||||
|
||||
return `${mainValue} + ${translate("{$values} AP for size category {$labels} (per level)", { values, labels })}`
|
||||
}
|
||||
@@ -175,19 +152,12 @@ export const renderAdventurePointsValue = (
|
||||
const sizes = getAllInstances("AnimalShapeSize").toSorted(
|
||||
on(x => x.content.ap_value, numAsc),
|
||||
)
|
||||
return translate(
|
||||
"{$values} adventure points for a {$sized} animal shape",
|
||||
{
|
||||
values: sizes.map(size => size.content.ap_value).join("/"),
|
||||
sized: sizes
|
||||
.map(
|
||||
size =>
|
||||
translateMap(size.content.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
)
|
||||
.join("/"),
|
||||
},
|
||||
)
|
||||
return translate("{$values} adventure points for a {$sized} animal shape", {
|
||||
values: sizes.map(size => size.content.ap_value).join("/"),
|
||||
sized: sizes
|
||||
.map(size => translateMap(size.content.translations)?.name ?? MISSING_VALUE)
|
||||
.join("/"),
|
||||
})
|
||||
}
|
||||
case "ArcaneBardTraditions":
|
||||
return MISSING_VALUE
|
||||
@@ -212,22 +182,14 @@ export const renderAdventurePointsValue = (
|
||||
case "Aspects":
|
||||
return MISSING_VALUE
|
||||
case "Diseases":
|
||||
if (
|
||||
derivedSelectOptions.Diseases.use_half_level_as_ap_value === true
|
||||
) {
|
||||
return translate(
|
||||
"Half the chosen disease’s level in adventure points",
|
||||
)
|
||||
if (derivedSelectOptions.Diseases.use_half_level_as_ap_value === true) {
|
||||
return translate("Half the chosen disease’s level in adventure points")
|
||||
} else {
|
||||
return translate("The chosen disease’s level in adventure points")
|
||||
}
|
||||
case "Poisons":
|
||||
if (
|
||||
derivedSelectOptions.Poisons.use_half_level_as_ap_value === true
|
||||
) {
|
||||
return translate(
|
||||
"Half the chosen poison’s level in adventure points",
|
||||
)
|
||||
if (derivedSelectOptions.Poisons.use_half_level_as_ap_value === true) {
|
||||
return translate("Half the chosen poison’s level in adventure points")
|
||||
} else {
|
||||
return translate("The chosen poison’s level in adventure points")
|
||||
}
|
||||
@@ -239,6 +201,7 @@ export const renderAdventurePointsValue = (
|
||||
() => {
|
||||
switch (derivedSelectOptions.Skills.categories.length) {
|
||||
case 1: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ensured by the schema
|
||||
const first = derivedSelectOptions.Skills.categories[0]!
|
||||
switch (first.kind) {
|
||||
case "Skills":
|
||||
@@ -257,14 +220,12 @@ export const renderAdventurePointsValue = (
|
||||
}
|
||||
case 2:
|
||||
return derivedSelectOptions.Skills.categories.every(
|
||||
category =>
|
||||
category.kind === "Spells" || category.kind === "Rituals",
|
||||
category => category.kind === "Spells" || category.kind === "Rituals",
|
||||
)
|
||||
? translate("A/B/C/D spellwork")
|
||||
: derivedSelectOptions.Skills.categories.every(
|
||||
category =>
|
||||
category.kind === "LiturgicalChants" ||
|
||||
category.kind === "Ceremonies",
|
||||
category.kind === "LiturgicalChants" || category.kind === "Ceremonies",
|
||||
)
|
||||
? translate("A/B/C/D liturgical chant or ceremony")
|
||||
: translate("A/B/C/D ability")
|
||||
@@ -292,56 +253,42 @@ export const renderAdventurePointsValue = (
|
||||
}
|
||||
}
|
||||
case "DependingOnActive":
|
||||
return `${translate(
|
||||
".input {$value :number} {{{$value} Adventure Points}}",
|
||||
{
|
||||
value: value.DependingOnActive.inactive,
|
||||
},
|
||||
)} (${translate(
|
||||
".input {$value :number} {{{$value} Adventure Points with {$name}}}",
|
||||
{
|
||||
value: value.DependingOnActive.active,
|
||||
name: getNameForId(value.DependingOnActive.id) ?? MISSING_VALUE,
|
||||
},
|
||||
)})`
|
||||
return `${translate(".input {$value :number} {{{$value} Adventure Points}}", {
|
||||
value: value.DependingOnActive.inactive,
|
||||
})} (${translate(".input {$value :number} {{{$value} Adventure Points with {$name}}}", {
|
||||
value: value.DependingOnActive.active,
|
||||
name: getNameForId(value.DependingOnActive.id) ?? MISSING_VALUE,
|
||||
})})`
|
||||
case "DependingOnActiveInstances":
|
||||
switch (value.DependingOnActiveInstances.kind) {
|
||||
case "Threshold":
|
||||
return translate(
|
||||
".input {$value :number} {{{$value} Adventure Points}}",
|
||||
{
|
||||
value: value.DependingOnActiveInstances.Threshold.normal,
|
||||
},
|
||||
)
|
||||
return translate(".input {$value :number} {{{$value} Adventure Points}}", {
|
||||
value: value.DependingOnActiveInstances.Threshold.normal,
|
||||
})
|
||||
case "Expression": {
|
||||
const expression = value.DependingOnActiveInstances.Expression
|
||||
const values = Array.from(
|
||||
{ length: entry.maximum ?? 3 },
|
||||
(_, index) =>
|
||||
evaluateMathOperation(expression, exprValue => {
|
||||
switch (exprValue.kind) {
|
||||
case "Constant":
|
||||
return exprValue.Constant
|
||||
case "Active":
|
||||
return index
|
||||
default:
|
||||
return assertExhaustive(exprValue)
|
||||
}
|
||||
}),
|
||||
const values = Array.from({ length: entry.maximum ?? 3 }, (_, index) =>
|
||||
evaluateMathOperation(expression, exprValue => {
|
||||
switch (exprValue.kind) {
|
||||
case "Constant":
|
||||
return exprValue.Constant
|
||||
case "Active":
|
||||
return index
|
||||
default:
|
||||
return assertExhaustive(exprValue)
|
||||
}
|
||||
}),
|
||||
).join("/")
|
||||
const labels = Array.from(
|
||||
{ length: entry.maximum ?? 3 },
|
||||
(_, index) => `${index + 1}.`,
|
||||
(_, index) => `${(index + 1).toFixed()}.`,
|
||||
).join("/")
|
||||
|
||||
if (entry.maximum !== undefined) {
|
||||
return translate(
|
||||
"{$values} Adventure Points for the {$labels} purchase",
|
||||
{
|
||||
values,
|
||||
labels,
|
||||
},
|
||||
)
|
||||
return translate("{$values} Adventure Points for the {$labels} purchase", {
|
||||
values,
|
||||
labels,
|
||||
})
|
||||
} else {
|
||||
return translate(
|
||||
"{$values}/and so on Adventure Points for the {$labels}/and so on purchase",
|
||||
|
||||
@@ -3,10 +3,10 @@ import { isNotNullish } from "@elyukai/utils/nullable"
|
||||
import { compareNumber } from "@elyukai/utils/ordering"
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { getAdventurePointsForActivation } from "@optolith/adventure-points/improvement-cost"
|
||||
import {
|
||||
import type {
|
||||
ImprovementCost,
|
||||
RatedIdentifier,
|
||||
type ImprovementCost,
|
||||
type SkillWithEnhancementsIdentifier,
|
||||
SkillWithEnhancementsIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { RawEntityDescriptionSection } from "../../index.js"
|
||||
import { printEnhancementPrerequisites } from "./prerequisites/index.js"
|
||||
@@ -40,7 +40,7 @@ export const renderEnhancements = (
|
||||
>().map(env =>
|
||||
translation === undefined
|
||||
? undefined
|
||||
: `- ^[${translation.name}](entity: "Enhancement") (${env.translate("SR {$value}", { value: enhancement.content.skill_rating })}, ${env.translate("{$value} AP", { value: enhancement.content.adventure_points_modifier * getAdventurePointsForActivation(parentImprovementCost.kind) })}): ${translation.effect ?? ""}${enhancement.content.prerequisites === undefined ? "" : ` ${env.translate(".input {$hiddenCount :number} {{Prerequisites}}", { hiddenCount: enhancement.content.prerequisites.length })}: ${printEnhancementPrerequisites(env.getInstanceById, { translate: env.translate, translateMap: env.translateMap, compare: env.localeCompare, join: env.localeJoin }, enhancement.content.prerequisites)}`}`,
|
||||
: `- ^[${translation.name}](entity: "Enhancement") (${env.translate("SR {$value}", { value: enhancement.content.skill_rating })}, ${env.translate("{$value} AP", { value: enhancement.content.adventure_points_modifier * getAdventurePointsForActivation(parentImprovementCost.kind) })}): ${translation.effect}${enhancement.content.prerequisites === undefined ? "" : ` ${env.translate(".input {$hiddenCount :number} {{Prerequisites}}", { hiddenCount: enhancement.content.prerequisites.length })}: ${printEnhancementPrerequisites(env.getInstanceById, { translate: env.translate, translateMap: env.translateMap, compare: env.localeCompare, join: env.localeJoin }, enhancement.content.prerequisites)}`}`,
|
||||
),
|
||||
),
|
||||
).thenW(enhancementDescriptions => {
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import { isNotEmpty } from "@elyukai/utils/array/nonEmpty"
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import type {
|
||||
AlternativeName,
|
||||
LaboratoryLevel,
|
||||
Resistance,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { AlternativeName, LaboratoryLevel, Resistance } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../helpers/getTypes.js"
|
||||
import type {
|
||||
LocaleMap,
|
||||
Translate,
|
||||
TranslateMap,
|
||||
} from "../../helpers/translate.js"
|
||||
import type { LocaleMap, Translate, TranslateMap } from "../../helpers/translate.js"
|
||||
import type { IdMap } from "../../index.js"
|
||||
import { renderDice } from "./dice.js"
|
||||
import { parensIf } from "./rated/activatable/parensIf.js"
|
||||
@@ -19,10 +11,7 @@ import { MISSING_VALUE } from "./unknown.js"
|
||||
/**
|
||||
* Renders a laboratory level into a localized string.
|
||||
*/
|
||||
export const renderLaboratoryLevel = (
|
||||
translate: Translate,
|
||||
level: LaboratoryLevel,
|
||||
) => {
|
||||
export const renderLaboratoryLevel = (translate: Translate, level: LaboratoryLevel) => {
|
||||
switch (level.kind) {
|
||||
case "ArchaicLaboratory":
|
||||
return translate("Archaic laboratory")
|
||||
@@ -49,45 +38,34 @@ export const renderResistance = (
|
||||
case "Spirit":
|
||||
return (
|
||||
translateMap(
|
||||
getInstanceById(
|
||||
"DerivedCharacteristic",
|
||||
idMap.DerivedCharacteristic.Spirit,
|
||||
)?.translations,
|
||||
getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Spirit)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
)
|
||||
|
||||
case "Toughness":
|
||||
return (
|
||||
translateMap(
|
||||
getInstanceById(
|
||||
"DerivedCharacteristic",
|
||||
idMap.DerivedCharacteristic.Toughness,
|
||||
)?.translations,
|
||||
getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Toughness)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
)
|
||||
|
||||
case "LowerOfSpiritAndToughness": {
|
||||
const spiritTranslation =
|
||||
translateMap(
|
||||
getInstanceById(
|
||||
"DerivedCharacteristic",
|
||||
idMap.DerivedCharacteristic.Spirit,
|
||||
)?.translations,
|
||||
getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Spirit)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
const toughnessTranslation =
|
||||
translateMap(
|
||||
getInstanceById(
|
||||
"DerivedCharacteristic",
|
||||
idMap.DerivedCharacteristic.Toughness,
|
||||
)?.translations,
|
||||
getInstanceById("DerivedCharacteristic", idMap.DerivedCharacteristic.Toughness)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
return translate(
|
||||
"{$first} or {$second}, depending on which value is lower",
|
||||
{
|
||||
first: spiritTranslation,
|
||||
second: toughnessTranslation,
|
||||
},
|
||||
)
|
||||
return translate("{$first} or {$second}, depending on which value is lower", {
|
||||
first: spiritTranslation,
|
||||
second: toughnessTranslation,
|
||||
})
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -108,7 +86,7 @@ export const renderChance = (
|
||||
(item.chance === undefined
|
||||
? undefined
|
||||
: translate("{$valueRange} on {$dice}", {
|
||||
valueRange: item.chance === 5 ? 1 : `1–${item.chance / 5}`,
|
||||
valueRange: item.chance === 5 ? 1 : `1–${(item.chance / 5).toFixed()}`,
|
||||
dice: renderDice(translate, { number: 1, sides: 20 }),
|
||||
}) +
|
||||
(includePercentage
|
||||
@@ -125,11 +103,8 @@ export const renderAlternativeNames = (
|
||||
alternativeNames === undefined || !isNotEmpty(alternativeNames)
|
||||
? undefined
|
||||
: {
|
||||
label: translate(
|
||||
".input {$hiddenCount :number} {{Alternative Names}}",
|
||||
{ hiddenCount: alternativeNames.length },
|
||||
),
|
||||
value: alternativeNames
|
||||
.map(name => name.name + parensIf(name.region))
|
||||
.join(", "),
|
||||
label: translate(".input {$hiddenCount :number} {{Alternative Names}}", {
|
||||
hiddenCount: alternativeNames.length,
|
||||
}),
|
||||
value: alternativeNames.map(name => name.name + parensIf(name.region)).join(", "),
|
||||
}
|
||||
|
||||
@@ -4,43 +4,46 @@ import type { MathOperation } from "optolith-database-schema/gen"
|
||||
type UnaryFormatter = (value: string | number) => string
|
||||
type BinaryFormatter = (left: string | number, right: string | number) => string
|
||||
|
||||
const printOperand = (operand: string | number): string =>
|
||||
typeof operand === "number" ? operand.toFixed() : operand
|
||||
|
||||
/**
|
||||
* Typographic formatter for addition.
|
||||
*/
|
||||
export const additionFormatter: BinaryFormatter = (left, right) =>
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
`${left} + ${right}`
|
||||
`${printOperand(left)} + ${printOperand(right)}`
|
||||
|
||||
/**
|
||||
* Typographic formatter for subtraction.
|
||||
*/
|
||||
export const subtractionFormatter: BinaryFormatter = (left, right) =>
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
`${left} − ${right}`
|
||||
`${printOperand(left)} − ${printOperand(right)}`
|
||||
/**
|
||||
* Typographic formatter for multiplication.
|
||||
*/
|
||||
export const multiplicationFormatter: BinaryFormatter = (left, right) =>
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
`${left} × ${right}`
|
||||
`${printOperand(left)} × ${printOperand(right)}`
|
||||
|
||||
/**
|
||||
* Typographic formatter for division.
|
||||
*/
|
||||
export const divisionFormatter: BinaryFormatter = (left, right) =>
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
`${left} / ${right}`
|
||||
`${printOperand(left)} / ${printOperand(right)}`
|
||||
|
||||
/**
|
||||
* Typographic formatter for exponentiation. Uses Markdown syntax.
|
||||
*/
|
||||
export const exponentiationFormatter: BinaryFormatter = (left, right) =>
|
||||
`${left}^${right}^`
|
||||
`${printOperand(left)}^${printOperand(right)}^`
|
||||
|
||||
/**
|
||||
* Typographic formatter for grouping (parentheses).
|
||||
*/
|
||||
export const groupFormatter: UnaryFormatter = value => `(${value})`
|
||||
export const groupFormatter: UnaryFormatter = value => `(${printOperand(value)})`
|
||||
|
||||
/**
|
||||
* Render a math operation as a string, using the provided function to render the values.
|
||||
@@ -54,9 +57,7 @@ export const renderMathOperation = <T>(
|
||||
addParenthesisTo: MathOperation<T>["kind"][] = [],
|
||||
): string => {
|
||||
const rendered = renderMathOperation(op, renderValue)
|
||||
return addParenthesisTo.includes(op.kind)
|
||||
? groupFormatter(rendered)
|
||||
: rendered
|
||||
return addParenthesisTo.includes(op.kind) ? groupFormatter(rendered) : rendered
|
||||
}
|
||||
|
||||
const renderBinary = (
|
||||
@@ -69,10 +70,7 @@ export const renderMathOperation = <T>(
|
||||
): string =>
|
||||
options.formatter(
|
||||
renderWithParenthesis(left, options.addParenthesisTo),
|
||||
renderWithParenthesis(
|
||||
right,
|
||||
options.addParenthesisToRight ?? options.addParenthesisTo,
|
||||
),
|
||||
renderWithParenthesis(right, options.addParenthesisToRight ?? options.addParenthesisTo),
|
||||
)
|
||||
|
||||
switch (operation.kind) {
|
||||
@@ -117,29 +115,25 @@ export const evaluateMathOperation = <T>(
|
||||
case "Addition": {
|
||||
const [left, right] = operation.Addition
|
||||
return (
|
||||
evaluateMathOperation(left, evaluateValue) +
|
||||
evaluateMathOperation(right, evaluateValue)
|
||||
evaluateMathOperation(left, evaluateValue) + evaluateMathOperation(right, evaluateValue)
|
||||
)
|
||||
}
|
||||
case "Subtraction": {
|
||||
const [left, right] = operation.Subtraction
|
||||
return (
|
||||
evaluateMathOperation(left, evaluateValue) -
|
||||
evaluateMathOperation(right, evaluateValue)
|
||||
evaluateMathOperation(left, evaluateValue) - evaluateMathOperation(right, evaluateValue)
|
||||
)
|
||||
}
|
||||
case "Multiplication": {
|
||||
const [left, right] = operation.Multiplication
|
||||
return (
|
||||
evaluateMathOperation(left, evaluateValue) *
|
||||
evaluateMathOperation(right, evaluateValue)
|
||||
evaluateMathOperation(left, evaluateValue) * evaluateMathOperation(right, evaluateValue)
|
||||
)
|
||||
}
|
||||
case "Division": {
|
||||
const [left, right] = operation.Division
|
||||
return (
|
||||
evaluateMathOperation(left, evaluateValue) /
|
||||
evaluateMathOperation(right, evaluateValue)
|
||||
evaluateMathOperation(left, evaluateValue) / evaluateMathOperation(right, evaluateValue)
|
||||
)
|
||||
}
|
||||
case "Exponentiation": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { DisplayOption } from "optolith-database-schema/gen"
|
||||
import type { DisplayOption } from "optolith-database-schema/gen"
|
||||
import type { TranslateMap } from "../../../helpers/translate.js"
|
||||
import { MISSING_VALUE } from "../unknown.js"
|
||||
import { PrerequisitePart } from "./part.js"
|
||||
import type { PrerequisitePart } from "./part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a display option.
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { isNotEmpty } from "@elyukai/utils/array/nonEmpty"
|
||||
import { on } from "@elyukai/utils/function"
|
||||
import { mapNullable } from "@elyukai/utils/nullable"
|
||||
import { numAsc } from "@optolith/helpers/compare"
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { romanize } from "@optolith/helpers/roman"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
ActivatableIdentifier,
|
||||
AdvantageDisadvantagePrerequisites,
|
||||
AnimistPowerPrerequisites,
|
||||
ArcaneTraditionPrerequisites,
|
||||
DerivedCharacteristicPrerequisites,
|
||||
EnhancementPrerequisites,
|
||||
GeneralPrerequisiteGroup,
|
||||
GeneralPrerequisites,
|
||||
GeodeRitualPrerequisites,
|
||||
InfluencePrerequisites,
|
||||
@@ -25,14 +28,12 @@ import {
|
||||
PrerequisitesForLevels,
|
||||
ProfessionPrerequisites,
|
||||
PublicationPrerequisites,
|
||||
RatedIdentifier,
|
||||
SpecialAbilityIdentifier,
|
||||
SpellworkPrerequisites,
|
||||
type ActivatableIdentifier,
|
||||
type GeneralPrerequisiteGroup,
|
||||
type RatedIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { GetAllChildInstancesForParent, GetInstanceById } from "../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import type { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import type { TranslateMap } from "../../../helpers/translate.js"
|
||||
import {
|
||||
renderActivatableNameComponents,
|
||||
@@ -40,7 +41,7 @@ import {
|
||||
} from "../activatableNameChunks.js"
|
||||
import { MISSING_VALUE } from "../unknown.js"
|
||||
import { printDisplayOption } from "./displayOption.js"
|
||||
import { hasPartValueObject, joinPrerequisiteParts, PrerequisitePart } from "./part.js"
|
||||
import { hasPartValueObject, joinPrerequisiteParts, type PrerequisitePart } from "./part.js"
|
||||
import {
|
||||
printAdvantageDisadvantagePrerequisiteGroup,
|
||||
printAnimistPowerPrerequisiteGroup,
|
||||
@@ -57,7 +58,7 @@ import {
|
||||
printPublicationPrerequisiteGroup,
|
||||
printSpellworkPrerequisiteGroup,
|
||||
} from "./prerequisiteGroups.js"
|
||||
import { GetResolvedSelectOptionById } from "./single/activatable.js"
|
||||
import type { GetResolvedSelectOptionById } from "./single/activatable.js"
|
||||
|
||||
type Prerequisite = { kind: string }
|
||||
|
||||
@@ -79,15 +80,17 @@ const printPrerequisitesDisjunction = <T extends Prerequisite>(
|
||||
return printDisplayOption(locale.translateMap, disjunction.display_option)
|
||||
}
|
||||
|
||||
const [first, ...other] = disjunction.list.map(getPrerequisiteTranslation).filter(isNotNullish)
|
||||
const { list: disjunctionList } = disjunction
|
||||
const [first, ...other] = disjunctionList.map(getPrerequisiteTranslation).filter(isNotNullish)
|
||||
|
||||
if (first === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (
|
||||
disjunction.list.length < 2 ||
|
||||
disjunction.list.slice(1).every(part => part.kind === disjunction.list[0]!.kind)
|
||||
disjunctionList.length < 2 ||
|
||||
(isNotEmpty(disjunctionList) &&
|
||||
disjunctionList.slice(1).every(part => part.kind === disjunctionList[0].kind))
|
||||
) {
|
||||
return {
|
||||
label: first.label,
|
||||
@@ -114,7 +117,13 @@ const printPrerequisitesDisjunction = <T extends Prerequisite>(
|
||||
|
||||
return {
|
||||
value: locale.join(
|
||||
[first, ...other].map(part => (part.label ?? "") + part.value),
|
||||
[first, ...other].map(
|
||||
part =>
|
||||
(part.label ?? "") +
|
||||
(typeof part.value === "string"
|
||||
? part.value
|
||||
: renderActivatableNameComponents(locale.translateMap, part.value, true)),
|
||||
),
|
||||
"disjunction",
|
||||
),
|
||||
sentenceType: undefined,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { deepEqual, equal } from "@elyukai/utils/equality"
|
||||
import { on } from "@elyukai/utils/function"
|
||||
import { compareNumber, reduceCompare, type Compare } from "@elyukai/utils/ordering"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { SentenceType, type ActivatableIdentifier } from "optolith-database-schema/gen"
|
||||
import type { ActivatableIdentifier, SentenceType } from "optolith-database-schema/gen"
|
||||
import type { LocaleCompare } from "../../../helpers/locale.js"
|
||||
import type { Translate, TranslateMap } from "../../../helpers/translate.js"
|
||||
import {
|
||||
@@ -192,7 +192,7 @@ export const joinPrerequisiteParts = (
|
||||
on(part => part.type, equal),
|
||||
)
|
||||
.map(group => ({
|
||||
type: group[0]!.type,
|
||||
type: group[0].type,
|
||||
parts: group.map(groupItem => groupItem.part),
|
||||
}))
|
||||
.reduce(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
ActivatableIdentifier,
|
||||
AdvantageDisadvantagePrerequisiteGroup,
|
||||
AnimistPowerPrerequisiteGroup,
|
||||
ArcaneTraditionPrerequisiteGroup,
|
||||
@@ -14,14 +15,16 @@ import {
|
||||
PreconditionGroup,
|
||||
ProfessionPrerequisiteGroup,
|
||||
PublicationPrerequisiteGroup,
|
||||
RatedIdentifier,
|
||||
SpellworkPrerequisiteGroup,
|
||||
type ActivatableIdentifier,
|
||||
type RatedIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { GetAllChildInstancesForParent, GetInstanceById } from "../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import { PrerequisitePart } from "./part.js"
|
||||
import { GetResolvedSelectOptionById, printActivatablePrerequisite } from "./single/activatable.js"
|
||||
import type { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import type { PrerequisitePart } from "./part.js"
|
||||
import {
|
||||
type GetResolvedSelectOptionById,
|
||||
printActivatablePrerequisite,
|
||||
} from "./single/activatable.js"
|
||||
import { printAnimistPowerPrerequisite } from "./single/animistPower.js"
|
||||
import { printBlessedTraditionPrerequisite } from "./single/blessedTradition.js"
|
||||
import { printCommonSuggestedByRCPPrerequisite } from "./single/commonSuggestedByRCP.js"
|
||||
|
||||
@@ -8,12 +8,12 @@ import type {
|
||||
ActivatablePrerequisite,
|
||||
RequirableSelectOptionIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleMap, Translate } from "../../../../helpers/translate.js"
|
||||
import { getNameComponents } from "../../activatableNameChunks.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Gets a resolved select option by its identifier.
|
||||
|
||||
@@ -2,9 +2,9 @@ import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { romanize } from "@optolith/helpers/roman"
|
||||
import type { AnimistPowerPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a culture prerequisite.
|
||||
@@ -24,7 +24,7 @@ export const printAnimistPowerPrerequisite = (
|
||||
value: [
|
||||
locale.translateMap(animistPower?.translations)?.name ?? "MISSING_VALUE",
|
||||
prerequisite.level === undefined ? undefined : romanize(prerequisite.level),
|
||||
prerequisite.value.toString(),
|
||||
prerequisite.value.toFixed(),
|
||||
]
|
||||
.filter(isNotNullish)
|
||||
.join(" "),
|
||||
|
||||
@@ -3,9 +3,9 @@ import type {
|
||||
BlessedTraditionPrerequisite,
|
||||
BlessedTraditionPrerequisiteRestriction,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printValue = (
|
||||
locale: LocaleEnvironment,
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printType = (
|
||||
locale: LocaleEnvironment,
|
||||
type: "Advantage" | "Disadvantage",
|
||||
): string => {
|
||||
const printType = (locale: LocaleEnvironment, type: "Advantage" | "Disadvantage"): string => {
|
||||
switch (type) {
|
||||
case "Advantage":
|
||||
return locale.translate("advantage")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CulturePrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { CulturePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a culture prerequisite.
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
type EnhancementPrerequisite,
|
||||
type SkillWithEnhancementsIdentifier,
|
||||
import type {
|
||||
EnhancementPrerequisite,
|
||||
SkillWithEnhancementsIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleMap } from "../../../../helpers/translate.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printLabel = (
|
||||
locale: Pick<LocaleEnvironment, "translate">,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { InfluencePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { InfluencePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a culture prerequisite.
|
||||
|
||||
@@ -3,9 +3,9 @@ import type {
|
||||
MagicalTraditionPrerequisite,
|
||||
MagicalTraditionPrerequisiteRestriction,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printValue = (
|
||||
locale: LocaleEnvironment,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a culture prerequisite.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { romanize } from "@optolith/helpers/roman"
|
||||
import type { PactPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a culture prerequisite.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { type PersonalityTraitPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PersonalityTraitPrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a personality trait prerequisite.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PrimaryAttributePrerequisite } from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PrimaryAttributePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a state prerequisite.
|
||||
@@ -16,7 +16,7 @@ export const printPrimaryAttributePrerequisite = (
|
||||
|
||||
return {
|
||||
label: `${locale.translate("Primary Attribute")} `,
|
||||
value: prerequisite.value.toString(),
|
||||
value: prerequisite.value.toFixed(),
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type ProfessionPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetAllChildInstancesForParent } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { ProfessionPrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetAllChildInstancesForParent } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { getProfessionName } from "../../professions.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a profession prerequisite.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { PublicationPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PublicationPrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a state prerequisite.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { RacePrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { RacePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a race prerequisite.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { RatedIdentifier, RatedPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { TranslateMap } from "../../../../helpers/translate.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printRatedName = (
|
||||
getInstanceById: GetInstanceById<
|
||||
@@ -80,7 +80,7 @@ export const printRatedPrerequisite = (
|
||||
}
|
||||
|
||||
return {
|
||||
value: `${printRatedName(getInstanceById, locale.translateMap, prerequisite.id)} ${prerequisite.value}`,
|
||||
value: `${printRatedName(getInstanceById, locale.translateMap, prerequisite.id)} ${prerequisite.value.toFixed()}`,
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import type {
|
||||
RatedMinimumNumberPrerequisite,
|
||||
RatedMinimumNumberPrerequisiteCombatTechniquesTargetGroup,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printNumberOfTheFollowingSkills = (locale: LocaleEnvironment, number: number): string =>
|
||||
locale.translate(".input {$count :number} {{{$count} of the following skills}}", {
|
||||
@@ -91,7 +91,7 @@ export const printRatedMinimumNumberPrerequisite = (
|
||||
locale,
|
||||
prerequisite.targets.CombatTechniques.group,
|
||||
prerequisite.number,
|
||||
)} ${prerequisite.value}`,
|
||||
)} ${prerequisite.value.toFixed()}`,
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { RatedSumPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { RatedSumPrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a rated sum prerequisite.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RulePrerequisite } from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { RulePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a rule prerequisite.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { BinarySex, SexPrerequisite } from "optolith-database-schema/gen"
|
||||
import type { Translate } from "../../../../helpers/translate.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printId = (translate: Translate, id: BinarySex): string => {
|
||||
switch (id.kind) {
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
SexualCharacteristic,
|
||||
SexualCharacteristicPrerequisite,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printId = (
|
||||
locale: LocaleEnvironment,
|
||||
id: SexualCharacteristic,
|
||||
): string => {
|
||||
const printId = (locale: LocaleEnvironment, id: SexualCharacteristic): string => {
|
||||
switch (id.kind) {
|
||||
case "Penis":
|
||||
return locale.translate("Penis")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { SocialStatusPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a social status prerequisite.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { StatePrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { StatePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a state prerequisite.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TextPrerequisite } from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { TextPrerequisite } from "optolith-database-schema/gen"
|
||||
import type { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
import type { PrerequisitePart } from "../part.js"
|
||||
|
||||
/**
|
||||
* Get the translation of a text prerequisite.
|
||||
|
||||
@@ -1,52 +1,37 @@
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { isNotNullish, mapNullable } from "@optolith/helpers/nullable"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
CastingTime,
|
||||
CastingTimeDuringLovemaking,
|
||||
CastingTimeIncludingLovemaking,
|
||||
DurationUnitValue,
|
||||
FastCastingTime,
|
||||
FastSkillNonModifiableCastingTime,
|
||||
ModifiableCastingTime,
|
||||
SkillModificationLevel_ID,
|
||||
SlowCastingTime,
|
||||
SlowSkillNonModifiableCastingTime,
|
||||
type DurationUnitValue,
|
||||
type SkillModificationLevel_ID,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { Case } from "../../../../helpers/enums.js"
|
||||
import {
|
||||
getInstanceByIdFnR,
|
||||
modifiableBySpeedR,
|
||||
type StdReader,
|
||||
} from "../../reader.js"
|
||||
import {
|
||||
formatCombinedTimeSpanR,
|
||||
formatTimeSpanR,
|
||||
} from "../../units/timeSpan.js"
|
||||
import { getInstanceByIdFnR, modifiableBySpeedR, type StdReader } from "../../reader.js"
|
||||
import { formatCombinedTimeSpanR, formatTimeSpanR } from "../../units/timeSpan.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import {
|
||||
appendNonModifiableSuffix,
|
||||
ModifiableParameter,
|
||||
} from "./nonModifiableSuffix.js"
|
||||
import { appendNonModifiableSuffix, ModifiableParameter } from "./nonModifiableSuffix.js"
|
||||
import { Speed } from "./speed.js"
|
||||
|
||||
const deriveModifiableCastingTime = (
|
||||
modificationLevelId: SkillModificationLevel_ID,
|
||||
): StdReader<
|
||||
DurationUnitValue | undefined,
|
||||
"s" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> =>
|
||||
): StdReader<DurationUnitValue | undefined, "s" | "ibi", "SkillModificationLevel"> =>
|
||||
getInstanceByIdFnR<"SkillModificationLevel">().thenW(
|
||||
getInstanceById =>
|
||||
mapNullable(
|
||||
getInstanceById("SkillModificationLevel", modificationLevelId),
|
||||
modificationLevel =>
|
||||
modifiableBySpeedR("casting_time", modificationLevel).map(
|
||||
castingTime =>
|
||||
typeof castingTime === "number"
|
||||
? { value: castingTime, unit: Case("Actions") }
|
||||
: castingTime,
|
||||
modifiableBySpeedR("casting_time", modificationLevel).map(castingTime =>
|
||||
typeof castingTime === "number"
|
||||
? { value: castingTime, unit: Case("Actions") }
|
||||
: castingTime,
|
||||
),
|
||||
) ?? Reader.of(undefined),
|
||||
)
|
||||
@@ -54,11 +39,8 @@ const deriveModifiableCastingTime = (
|
||||
const renderModifiableCastingTime = (
|
||||
value: ModifiableCastingTime,
|
||||
): StdReader<string, "t" | "rts" | "s" | "ibi", "SkillModificationLevel"> =>
|
||||
deriveModifiableCastingTime(value.initial_modification_level).thenW(
|
||||
castingTime =>
|
||||
castingTime === undefined
|
||||
? Reader.of(MISSING_VALUE)
|
||||
: formatCombinedTimeSpanR(castingTime),
|
||||
deriveModifiableCastingTime(value.initial_modification_level).thenW(castingTime =>
|
||||
castingTime === undefined ? Reader.of(MISSING_VALUE) : formatCombinedTimeSpanR(castingTime),
|
||||
)
|
||||
|
||||
const renderCastingTimeDuringLovemaking = (
|
||||
@@ -70,8 +52,7 @@ const renderCastingTimeDuringLovemaking = (
|
||||
*/
|
||||
export const renderFastSkillNonModifiableCastingTime = (
|
||||
value: FastSkillNonModifiableCastingTime,
|
||||
): StdReader<string, "t" | "rts"> =>
|
||||
formatTimeSpanR(Case("Actions"), value.actions)
|
||||
): StdReader<string, "t" | "rts"> => formatTimeSpanR(Case("Actions"), value.actions)
|
||||
|
||||
/**
|
||||
* Get the text for a non-modifiable casting time of a slow activatable skill.
|
||||
@@ -88,11 +69,7 @@ export const renderCastingTime = <NonModifiable extends object>(
|
||||
value: NonModifiable,
|
||||
) => StdReader<string, "t" | "rts" | "s" | "nms">,
|
||||
value: CastingTime<NonModifiable>,
|
||||
): StdReader<
|
||||
string,
|
||||
"t" | "rts" | "s" | "nms" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> => {
|
||||
): StdReader<string, "t" | "rts" | "s" | "nms" | "ibi", "SkillModificationLevel"> => {
|
||||
switch (value.kind) {
|
||||
case "Modifiable":
|
||||
return renderModifiableCastingTime(value.Modifiable)
|
||||
@@ -112,11 +89,9 @@ const renderCastingTimeIncludingLovemaking = <NonModifiable extends object>(
|
||||
value: CastingTimeIncludingLovemaking<NonModifiable>,
|
||||
) =>
|
||||
Reader.sequence([
|
||||
mapNullable(value.default, def =>
|
||||
renderCastingTime(renderNonModifiableCastingTime, def),
|
||||
) ?? Reader.of(undefined),
|
||||
mapNullable(value.during_lovemaking, renderCastingTimeDuringLovemaking) ??
|
||||
mapNullable(value.default, def => renderCastingTime(renderNonModifiableCastingTime, def)) ??
|
||||
Reader.of(undefined),
|
||||
mapNullable(value.during_lovemaking, renderCastingTimeDuringLovemaking) ?? Reader.of(undefined),
|
||||
]).map(texts => texts.filter(isNotNullish).join(" / "))
|
||||
|
||||
/**
|
||||
@@ -125,10 +100,9 @@ const renderCastingTimeIncludingLovemaking = <NonModifiable extends object>(
|
||||
export const renderFastCastingTime = (
|
||||
value: FastCastingTime,
|
||||
): StdReader<string, "t" | "rts" | "nms" | "ibi", "SkillModificationLevel"> =>
|
||||
renderCastingTimeIncludingLovemaking(
|
||||
renderFastSkillNonModifiableCastingTime,
|
||||
value,
|
||||
).with(env => ({ ...env, speed: Speed.Fast }))
|
||||
renderCastingTimeIncludingLovemaking(renderFastSkillNonModifiableCastingTime, value).with(
|
||||
env => ({ ...env, speed: Speed.Fast }),
|
||||
)
|
||||
|
||||
/**
|
||||
* Get the text for the casting time of a slow activatable skill.
|
||||
@@ -136,7 +110,6 @@ export const renderFastCastingTime = (
|
||||
export const renderSlowCastingTime = (
|
||||
value: SlowCastingTime,
|
||||
): StdReader<string, "t" | "rts" | "nms" | "ibi", "SkillModificationLevel"> =>
|
||||
renderCastingTimeIncludingLovemaking(
|
||||
renderSlowSkillNonModifiableCastingTime,
|
||||
value,
|
||||
).with(env => ({ ...env, speed: Speed.Slow }))
|
||||
renderCastingTimeIncludingLovemaking(renderSlowSkillNonModifiableCastingTime, value).with(
|
||||
env => ({ ...env, speed: Speed.Slow }),
|
||||
)
|
||||
|
||||
@@ -2,23 +2,23 @@ import { identity } from "@elyukai/utils/function"
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { mapNullable } from "@optolith/helpers/nullable"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
CheckResultBasedModifier,
|
||||
DurationUnitValue,
|
||||
ElvenMagicalSongPermanentCost,
|
||||
FirstPersonMagicalMelodyCost,
|
||||
MultipleOneTimeCosts,
|
||||
NonModifiableOneTimeCostPerCountable,
|
||||
NonModifiableSustainedCost,
|
||||
OneTimeCost,
|
||||
OneTimeCostMap,
|
||||
ResponsiveText,
|
||||
ResponsiveTextOptional,
|
||||
ResponsiveTextReplace,
|
||||
SingleOneTimeCost,
|
||||
SkillModificationLevel_ID,
|
||||
SustainedCost,
|
||||
type CheckResultBasedModifier,
|
||||
type DurationUnitValue,
|
||||
type ElvenMagicalSongPermanentCost,
|
||||
type FirstPersonMagicalMelodyCost,
|
||||
type NonModifiableOneTimeCostPerCountable,
|
||||
type OneTimeCostMap,
|
||||
type ResponsiveText,
|
||||
type ResponsiveTextOptional,
|
||||
type ResponsiveTextReplace,
|
||||
type SkillModificationLevel_ID,
|
||||
type SustainedCostMap,
|
||||
SustainedCostMap,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type LocaleMap } from "../../../../helpers/translate.js"
|
||||
import { renderResponsiveMap } from "../../map.js"
|
||||
@@ -37,18 +37,12 @@ import {
|
||||
type StdEnv,
|
||||
type StdReader,
|
||||
} from "../../reader.js"
|
||||
import {
|
||||
appendNoteIfNeeded,
|
||||
replaceTextIfNeeded,
|
||||
} from "../../responsiveText.js"
|
||||
import { appendNoteIfNeeded, replaceTextIfNeeded } from "../../responsiveText.js"
|
||||
import { formatCombinedTimeSpanR } from "../../units/timeSpan.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { appendCheckResultModifier } from "./checkResultBased.js"
|
||||
import { wrapIfMinimum } from "./isMinimumMaximum.js"
|
||||
import {
|
||||
appendNonModifiableSuffix,
|
||||
ModifiableParameter,
|
||||
} from "./nonModifiableSuffix.js"
|
||||
import { appendNonModifiableSuffix, ModifiableParameter } from "./nonModifiableSuffix.js"
|
||||
|
||||
const deriveModifiableCost = (
|
||||
modificationLevelId: SkillModificationLevel_ID,
|
||||
@@ -85,11 +79,10 @@ const appendPerCountableToCostIfNeeded = (
|
||||
|
||||
return responsiveTextR(translation.countable)
|
||||
.thenW(countable =>
|
||||
responsiveTranslateR(
|
||||
"{$cost} per {$countable}",
|
||||
"{$cost}/{$countable}",
|
||||
{ cost: baseCost, countable },
|
||||
),
|
||||
responsiveTranslateR("{$cost} per {$countable}", "{$cost}/{$countable}", {
|
||||
cost: baseCost,
|
||||
countable,
|
||||
}),
|
||||
)
|
||||
.thenW(text =>
|
||||
minimum_total === undefined
|
||||
@@ -121,11 +114,9 @@ const appendElvenPermanentCostIfNeeded = (
|
||||
baseCost: string,
|
||||
): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
mapNullable(permanent, value =>
|
||||
responsiveTranslateR(
|
||||
".input {$value :number} {{{$value} permanent AE}}",
|
||||
"{$value} pAE",
|
||||
{ value: value.value },
|
||||
)
|
||||
responsiveTranslateR(".input {$value :number} {{{$value} permanent AE}}", "{$value} pAE", {
|
||||
value: value.value,
|
||||
})
|
||||
.thenW(text => replaceTextIfNeeded(value.translations, text))
|
||||
.map(text => baseCost + text),
|
||||
) ?? Reader.of(baseCost)
|
||||
@@ -138,9 +129,7 @@ const appendFamiliarsTrickLPCostIfNeeded = (
|
||||
? Reader.of(baseCost)
|
||||
: formatEnergyR(lpValue)
|
||||
.with((env: StdEnv<"t">) => ({ ...env, energyUnit: "LifePoints" }))
|
||||
.thenW(formattedLpCost =>
|
||||
responsiveLocaleJoinR([baseCost, formattedLpCost], "conjunction"),
|
||||
)
|
||||
.thenW(formattedLpCost => responsiveLocaleJoinR([baseCost, formattedLpCost], "conjunction"))
|
||||
|
||||
/**
|
||||
* Returns the text for the modifiable one-time cost of an activatable skill.
|
||||
@@ -152,11 +141,7 @@ export const renderModifiableOneTimeCost = (value: {
|
||||
replacement?: ResponsiveTextReplace
|
||||
additional?: ResponsiveText
|
||||
}>
|
||||
}): StdReader<
|
||||
string,
|
||||
"t" | "tm" | "rts" | "eu" | "s" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> =>
|
||||
}): StdReader<string, "t" | "tm" | "rts" | "eu" | "s" | "ibi", "SkillModificationLevel"> =>
|
||||
deriveModifiableCost(value.initial_modification_level).thenW(cost =>
|
||||
cost === undefined
|
||||
? Reader.of(MISSING_VALUE)
|
||||
@@ -175,10 +160,7 @@ export const renderModifiableOneTimeCost = (value: {
|
||||
),
|
||||
)
|
||||
|
||||
const appendIntervalToCost = (
|
||||
interval: DurationUnitValue | undefined,
|
||||
baseCost: string,
|
||||
) =>
|
||||
const appendIntervalToCost = (interval: DurationUnitValue | undefined, baseCost: string) =>
|
||||
interval === undefined
|
||||
? Reader.of(baseCost)
|
||||
: formatCombinedTimeSpanR(interval).then(formattedInterval =>
|
||||
@@ -219,7 +201,7 @@ export const renderNonModifiableOneTimeCost = (
|
||||
.then(
|
||||
shouldAppendNonModifiableSuffix
|
||||
? base => appendNonModifiableSuffix(ModifiableParameter.Cost, base)
|
||||
: Reader.of,
|
||||
: base => Reader.of(base),
|
||||
)
|
||||
|
||||
type IndefiniteCost = {
|
||||
@@ -243,15 +225,11 @@ export const renderIndefiniteCost = (
|
||||
? Reader.of(MISSING_VALUE)
|
||||
: responsiveTextR(translation.description),
|
||||
)
|
||||
.map(
|
||||
modifier === undefined
|
||||
? identity
|
||||
: base => appendCheckResultModifier(base, modifier),
|
||||
)
|
||||
.map(modifier === undefined ? identity : base => appendCheckResultModifier(base, modifier))
|
||||
.thenW(
|
||||
shouldAppendNonModifiableSuffix
|
||||
? base => appendNonModifiableSuffix(ModifiableParameter.Cost, base)
|
||||
: Reader.of,
|
||||
: base => Reader.of(base),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -282,12 +260,9 @@ const renderMultipleOneTimeCosts = (
|
||||
"t" | "tm" | "lj" | "rts" | "eu" | "s" | "nms" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> => {
|
||||
const appendNonModifiableIfRequested = !value.every(
|
||||
part => part.kind === "Modifiable",
|
||||
)
|
||||
? (text: string) =>
|
||||
appendNonModifiableSuffix(ModifiableParameter.Cost, text)
|
||||
: Reader.of
|
||||
const appendNonModifiableIfRequested = !value.every(part => part.kind === "Modifiable")
|
||||
? (text: string) => appendNonModifiableSuffix(ModifiableParameter.Cost, text)
|
||||
: (text: string) => Reader.of(text)
|
||||
|
||||
return Reader.sequence(value.map(renderSingleOneTimeCost))
|
||||
.thenW(list => responsiveLocaleJoinR(list, type))
|
||||
@@ -313,6 +288,7 @@ export const renderOneTimeCostMap = (
|
||||
translate(", {$value} of which are permanent", {
|
||||
value: values,
|
||||
}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- is checked beforehand
|
||||
getAdditionalValue: option => option.permanent_value!,
|
||||
}
|
||||
: undefined,
|
||||
@@ -323,9 +299,7 @@ export const renderOneTimeCostMap = (
|
||||
|
||||
const renderSustainedCostMap = (value: SustainedCostMap) =>
|
||||
formatEnergyFnR
|
||||
.thenW(formatEnergy =>
|
||||
renderResponsiveMap(value, option => option.value, formatEnergy),
|
||||
)
|
||||
.thenW(formatEnergy => renderResponsiveMap(value, option => option.value, formatEnergy))
|
||||
.then(text => appendNonModifiableSuffix(ModifiableParameter.Cost, text))
|
||||
|
||||
/**
|
||||
@@ -378,20 +352,14 @@ const renderModifiableSustainedCost = (value: {
|
||||
const activationCost = formatEnergy(cost)
|
||||
const intervalCost = formatEnergy(cost / 2)
|
||||
|
||||
return buildSustainedCost(
|
||||
activationCost,
|
||||
intervalCost,
|
||||
value.interval,
|
||||
)
|
||||
return buildSustainedCost(activationCost, intervalCost, value.interval)
|
||||
}),
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns the text for the non-modifiable cost of a sustained activatable skill.
|
||||
*/
|
||||
export const renderNonModifiableSustainedCost = (
|
||||
value: NonModifiableSustainedCost,
|
||||
) =>
|
||||
export const renderNonModifiableSustainedCost = (value: NonModifiableSustainedCost) =>
|
||||
formatEnergyFnR
|
||||
.thenW(formatEnergy => {
|
||||
const activationCost = formatEnergy(value.value)
|
||||
@@ -413,11 +381,7 @@ export const renderNonModifiableSustainedCost = (
|
||||
*/
|
||||
export const renderSustainedCost = (
|
||||
value: SustainedCost,
|
||||
): StdReader<
|
||||
string,
|
||||
"t" | "tm" | "rts" | "eu" | "s" | "nms" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> => {
|
||||
): StdReader<string, "t" | "tm" | "rts" | "eu" | "s" | "nms" | "ibi", "SkillModificationLevel"> => {
|
||||
switch (value.kind) {
|
||||
case "Modifiable":
|
||||
return renderModifiableSustainedCost(value.Modifiable)
|
||||
|
||||
@@ -20,18 +20,13 @@ import {
|
||||
type StdReader,
|
||||
} from "../../reader.js"
|
||||
import { replaceTextIfNeeded } from "../../responsiveText.js"
|
||||
import {
|
||||
formatCombinedTimeSpanR,
|
||||
formatTimeSpanR,
|
||||
} from "../../units/timeSpan.js"
|
||||
import { formatCombinedTimeSpanR, formatTimeSpanR } from "../../units/timeSpan.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { renderCheckResultBasedValue } from "./checkResultBased.js"
|
||||
import { wrapAsMaximum, wrapIfMaximum } from "./isMinimumMaximum.js"
|
||||
import { appendInParensIfNotEmpty } from "./parensIf.js"
|
||||
|
||||
const renderImmediateDuration = (
|
||||
value?: Immediate,
|
||||
): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
const renderImmediateDuration = (value?: Immediate): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
translateR("Immediate")
|
||||
.thenW(
|
||||
base =>
|
||||
@@ -43,16 +38,10 @@ const renderImmediateDuration = (
|
||||
)
|
||||
.thenW(text => replaceTextIfNeeded(value?.translations, text))
|
||||
|
||||
const renderPermanentDuration = (
|
||||
value: PermanentDuration,
|
||||
): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
translateR("Permanent").thenW(text =>
|
||||
replaceTextIfNeeded(value.translations, text),
|
||||
)
|
||||
const renderPermanentDuration = (value: PermanentDuration): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
translateR("Permanent").thenW(text => replaceTextIfNeeded(value.translations, text))
|
||||
|
||||
const renderFixedDuration = (
|
||||
value: FixedDuration,
|
||||
): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
const renderFixedDuration = (value: FixedDuration): StdReader<string, "t" | "tm" | "rts"> =>
|
||||
formatCombinedTimeSpanR(value)
|
||||
.then(text => wrapIfMaximum(value.is_maximum, text))
|
||||
.thenW(text => replaceTextIfNeeded(value.translations, text))
|
||||
@@ -84,17 +73,14 @@ const renderIndefiniteDuration = (value: {
|
||||
)
|
||||
.thenW(
|
||||
maximum === undefined
|
||||
? Reader.of
|
||||
? text => Reader.of(text)
|
||||
: text =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
renderOneTimeDuration(maximum).then(maximumText =>
|
||||
translateR(
|
||||
"{$defaultDuration}, but no more than {$maximumDuration}",
|
||||
{
|
||||
defaultDuration: text,
|
||||
maximumDuration: maximumText,
|
||||
},
|
||||
),
|
||||
translateR("{$defaultDuration}, but no more than {$maximumDuration}", {
|
||||
defaultDuration: text,
|
||||
maximumDuration: maximumText,
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -167,16 +153,12 @@ export const renderSustainedDuration = (
|
||||
): StdReader<string, "t" | "rts"> =>
|
||||
value === undefined
|
||||
? responsiveTranslateR("Sustained", "(S)")
|
||||
: formatCombinedTimeSpanR(value.maximum).then(maxText =>
|
||||
wrapAsMaximum(maxText),
|
||||
)
|
||||
: formatCombinedTimeSpanR(value.maximum).then(maxText => wrapAsMaximum(maxText))
|
||||
|
||||
/**
|
||||
* Returns the text for the duration of a musical activatable skill.
|
||||
*/
|
||||
export const renderMusicDuration = (
|
||||
duration: MusicDuration,
|
||||
): StdReader<string, "t"> =>
|
||||
export const renderMusicDuration = (duration: MusicDuration): StdReader<string, "t"> =>
|
||||
Reader.asks(({ translate }) => {
|
||||
const length = (() => {
|
||||
switch (duration.length.kind) {
|
||||
|
||||
@@ -51,13 +51,10 @@ export const renderEffect = (
|
||||
value: effect.Plain.text,
|
||||
}))
|
||||
case "ForEachQualityLevel":
|
||||
return getContentPartsForQualityLevels(
|
||||
index => index + 1,
|
||||
effect.ForEachQualityLevel,
|
||||
)
|
||||
return getContentPartsForQualityLevels(index => index + 1, effect.ForEachQualityLevel)
|
||||
case "ForEachTwoQualityLevels":
|
||||
return getContentPartsForQualityLevels(
|
||||
index => `${index * 2 + 1}–${index * 2 + 2}`,
|
||||
index => `${(index * 2 + 1).toFixed()}–${(index * 2 + 2).toFixed()}`,
|
||||
effect.ForEachTwoQualityLevels,
|
||||
)
|
||||
default:
|
||||
|
||||
@@ -18,18 +18,12 @@ import {
|
||||
translateR,
|
||||
type StdReader,
|
||||
} from "../../reader.js"
|
||||
import {
|
||||
appendNoteIfNeeded,
|
||||
replaceTextIfNeeded,
|
||||
} from "../../responsiveText.js"
|
||||
import { appendNoteIfNeeded, replaceTextIfNeeded } from "../../responsiveText.js"
|
||||
import { formatCombinedLengthR, formatLengthR } from "../../units/length.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { renderCheckResultBasedValue } from "./checkResultBased.js"
|
||||
import { wrapIfMaximum } from "./isMinimumMaximum.js"
|
||||
import {
|
||||
appendNonModifiableSuffix,
|
||||
ModifiableParameter,
|
||||
} from "./nonModifiableSuffix.js"
|
||||
import { appendNonModifiableSuffix, ModifiableParameter } from "./nonModifiableSuffix.js"
|
||||
|
||||
const deriveModifiableRange = (
|
||||
modificationLevelId: SkillModificationLevel_ID,
|
||||
@@ -47,21 +41,17 @@ const deriveModifiableRange = (
|
||||
translateMapR(modificationLevel.translations).thenW(translation =>
|
||||
translation === undefined
|
||||
? Reader.of({ value, translation: undefined })
|
||||
: modifiableBySpeedOptionalR("range", translation).map(
|
||||
valueTranslation => ({
|
||||
value,
|
||||
translation: valueTranslation,
|
||||
}),
|
||||
),
|
||||
: modifiableBySpeedOptionalR("range", translation).map(valueTranslation => ({
|
||||
value,
|
||||
translation: valueTranslation,
|
||||
})),
|
||||
),
|
||||
),
|
||||
) ?? Reader.of(undefined),
|
||||
)
|
||||
|
||||
const wrapIfRadius = (is_radius: boolean | undefined, text: string) =>
|
||||
translateFnR.map(translate =>
|
||||
is_radius === true ? `${text} ${translate("Radius")}` : text,
|
||||
)
|
||||
translateFnR.map(translate => (is_radius === true ? `${text} ${translate("Radius")}` : text))
|
||||
|
||||
const renderModifiableRange = (value: ModifiableRange) =>
|
||||
deriveModifiableRange(value.initial_modification_level)
|
||||
@@ -114,9 +104,8 @@ export const renderNonModifiableRange = (
|
||||
shouldAppendNonModifiableSuffix: boolean,
|
||||
): StdReader<string, "t" | "tm" | "rts" | "nms"> => {
|
||||
const appendNonModifiableSuffixIfNeeded = shouldAppendNonModifiableSuffix
|
||||
? (text: string) =>
|
||||
appendNonModifiableSuffix(ModifiableParameter.Range, text)
|
||||
: Reader.of
|
||||
? (text: string) => appendNonModifiableSuffix(ModifiableParameter.Range, text)
|
||||
: (text: string) => Reader.of(text)
|
||||
|
||||
switch (value.kind) {
|
||||
case "Sight":
|
||||
@@ -128,9 +117,7 @@ export const renderNonModifiableRange = (
|
||||
case "Touch":
|
||||
return translateR("Touch").thenW(appendNonModifiableSuffixIfNeeded)
|
||||
case "Fixed": {
|
||||
return renderFixedRange(value.Fixed).thenW(
|
||||
appendNonModifiableSuffixIfNeeded,
|
||||
)
|
||||
return renderFixedRange(value.Fixed).thenW(appendNonModifiableSuffixIfNeeded)
|
||||
}
|
||||
case "CheckResultBased":
|
||||
return getCheckResultBasedRangeTranslation(value.CheckResultBased).then(
|
||||
@@ -147,11 +134,7 @@ export const renderNonModifiableRange = (
|
||||
export const renderRangeValue = (
|
||||
value: RangeValue,
|
||||
shouldAppendNonModifiableSuffix: boolean,
|
||||
): StdReader<
|
||||
string,
|
||||
"t" | "tm" | "rts" | "s" | "nms" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> => {
|
||||
): StdReader<string, "t" | "tm" | "rts" | "s" | "nms" | "ibi", "SkillModificationLevel"> => {
|
||||
switch (value.kind) {
|
||||
case "Modifiable":
|
||||
return renderModifiableRange(value.Modifiable)
|
||||
@@ -172,11 +155,7 @@ export const renderRangeValue = (
|
||||
*/
|
||||
export const renderRange = (
|
||||
value: Range,
|
||||
): StdReader<
|
||||
string,
|
||||
"t" | "tm" | "rts" | "s" | "nms" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> =>
|
||||
): StdReader<string, "t" | "tm" | "rts" | "s" | "nms" | "ibi", "SkillModificationLevel"> =>
|
||||
renderRangeValue(value.value, true)
|
||||
.then(text => replaceTextIfNeeded(value.translations, text))
|
||||
.then(text => appendNoteIfNeeded(value.translations, text))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
FastSkillModificationLevelConfig,
|
||||
SkillModificationLevel,
|
||||
SlowSkillModificationLevelConfig,
|
||||
@@ -21,23 +21,16 @@ type SpeedMap = {
|
||||
/**
|
||||
* Returns a common value for a skill modification level depending on the speed.
|
||||
*/
|
||||
export const getModifiableBySpeed = <
|
||||
S extends Speed,
|
||||
K extends keyof SpeedMap[S],
|
||||
>(
|
||||
export const getModifiableBySpeed = <S extends Speed, K extends keyof SpeedMap[S]>(
|
||||
speed: S,
|
||||
key: K,
|
||||
level: SkillModificationLevel,
|
||||
): SpeedMap[S][K] => {
|
||||
switch (speed) {
|
||||
case Speed.Fast:
|
||||
return level.fast[
|
||||
key as keyof FastSkillModificationLevelConfig
|
||||
] as SpeedMap[S][K]
|
||||
return level.fast[key as keyof FastSkillModificationLevelConfig] as SpeedMap[S][K]
|
||||
case Speed.Slow:
|
||||
return level.slow[
|
||||
key as keyof SlowSkillModificationLevelConfig
|
||||
] as SpeedMap[S][K]
|
||||
return level.slow[key as keyof SlowSkillModificationLevelConfig] as SpeedMap[S][K]
|
||||
default:
|
||||
return assertExhaustive(speed)
|
||||
}
|
||||
|
||||
@@ -7,12 +7,7 @@ import type {
|
||||
TargetCategory_ID,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type RawDefinitionListEntityDescriptionSectionItem } from "../../../../index.js"
|
||||
import {
|
||||
getInstanceByIdFnR,
|
||||
translateMapR,
|
||||
translateR,
|
||||
type StdReader,
|
||||
} from "../../reader.js"
|
||||
import { getInstanceByIdFnR, translateMapR, translateR, type StdReader } from "../../reader.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { appendInParensIfNotEmpty } from "./parensIf.js"
|
||||
|
||||
@@ -20,12 +15,8 @@ const renderPredefined = (targetCategoryId: TargetCategory_ID) =>
|
||||
getInstanceByIdFnR<"TargetCategory">()
|
||||
.thenW(
|
||||
getInstanceById =>
|
||||
mapNullable(
|
||||
getInstanceById("TargetCategory", targetCategoryId),
|
||||
targetCategory =>
|
||||
translateMapR(targetCategory.translations).map(
|
||||
translation => translation?.name,
|
||||
),
|
||||
mapNullable(getInstanceById("TargetCategory", targetCategoryId), targetCategory =>
|
||||
translateMapR(targetCategory.translations).map(translation => translation?.name),
|
||||
) ?? Reader.of(undefined),
|
||||
)
|
||||
.map(translation => translation ?? MISSING_VALUE)
|
||||
@@ -56,21 +47,16 @@ const getTargetCategoryTranslationByType = (
|
||||
*/
|
||||
export const renderTargetCategory = (
|
||||
values: AffectedTargetCategories,
|
||||
): StdReader<
|
||||
RawDefinitionListEntityDescriptionSectionItem,
|
||||
"t" | "tm" | "ibi",
|
||||
"TargetCategory"
|
||||
> =>
|
||||
): StdReader<RawDefinitionListEntityDescriptionSectionItem, "t" | "tm" | "ibi", "TargetCategory"> =>
|
||||
translateR("Target Category").thenW(label =>
|
||||
(values.length === 0
|
||||
? translateR("all")
|
||||
: Reader.sequence(
|
||||
values.map(({ id, translations }) =>
|
||||
getTargetCategoryTranslationByType(id).then(
|
||||
text =>
|
||||
translateMapR(translations)
|
||||
.map(translation => translation?.note)
|
||||
.map(note => appendInParensIfNotEmpty(note, text)) ?? text,
|
||||
getTargetCategoryTranslationByType(id).then(text =>
|
||||
translateMapR(translations)
|
||||
.map(translation => translation?.note)
|
||||
.map(note => appendInParensIfNotEmpty(note, text)),
|
||||
),
|
||||
),
|
||||
).map(texts => texts.join(", "))
|
||||
|
||||
@@ -16,7 +16,12 @@ import type {
|
||||
GetAllInstances,
|
||||
GetInstanceById,
|
||||
} from "../../helpers/getTypes.js"
|
||||
import type { LocaleCompare, LocaleJoin, LocaleJoinType } from "../../helpers/locale.js"
|
||||
import type {
|
||||
FormatNumber,
|
||||
LocaleCompare,
|
||||
LocaleJoin,
|
||||
LocaleJoinType,
|
||||
} from "../../helpers/locale.js"
|
||||
import type {
|
||||
Format,
|
||||
LocaleMap,
|
||||
@@ -43,6 +48,7 @@ export type EnvMap<
|
||||
CE extends keyof ChildEntityMap = never,
|
||||
> = {
|
||||
format: Format
|
||||
formatNumber: FormatNumber
|
||||
translate: Translate
|
||||
translateMap: TranslateMap
|
||||
localeJoin: LocaleJoin
|
||||
@@ -62,6 +68,7 @@ export type EnvMap<
|
||||
*/
|
||||
export type EnvMapAbbr = {
|
||||
f: "format"
|
||||
fn: "formatNumber"
|
||||
t: "translate"
|
||||
tm: "translateMap"
|
||||
lj: "localeJoin"
|
||||
@@ -108,7 +115,7 @@ export type StdReader<
|
||||
*/
|
||||
export const formatR = (
|
||||
text: string,
|
||||
args?: Record<string, unknown> | undefined,
|
||||
args?: Record<string, unknown>,
|
||||
): Reader<{ format: Format }, string> => Reader.asks(env => env.format(text, args))
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import type { Translate, Translations } from "../../../helpers/translate.js"
|
||||
import type { StdReader } from "../reader.js"
|
||||
import { responsive, ResponsiveTextSize } from "../responsiveText.js"
|
||||
import { responsive, type ResponsiveTextSize } from "../responsiveText.js"
|
||||
|
||||
type LengthUnit = "Steps" | "Miles"
|
||||
|
||||
const lengthUnitTranslationKeys = {
|
||||
Steps: [
|
||||
".input {$value :number} {{{$value} yards}}",
|
||||
"{$value} yards",
|
||||
"{$value} yd",
|
||||
],
|
||||
Miles: [
|
||||
".input {$value :number} {{{$value} miles}}",
|
||||
"{$value} miles",
|
||||
"{$value} mi.",
|
||||
],
|
||||
Steps: [".input {$value :number} {{{$value} yards}}", "{$value} yards", "{$value} yd"],
|
||||
Miles: [".input {$value :number} {{{$value} miles}}", "{$value} miles", "{$value} mi."],
|
||||
} as const satisfies {
|
||||
[key in LengthUnit]: [
|
||||
fullNumber: keyof Translations,
|
||||
@@ -53,9 +45,7 @@ export const formatLengthR = (
|
||||
unit: LengthUnit | { kind: LengthUnit },
|
||||
value: number | string,
|
||||
): StdReader<string, "t" | "rts"> =>
|
||||
Reader.asks(env =>
|
||||
formatLength(env.translate, env.responsiveTextSize, unit, value),
|
||||
)
|
||||
Reader.asks(env => formatLength(env.translate, env.responsiveTextSize, unit, value))
|
||||
|
||||
/**
|
||||
* Returns the text for a length unit.
|
||||
@@ -64,11 +54,4 @@ export const formatCombinedLengthR = (object: {
|
||||
unit: LengthUnit | { kind: LengthUnit }
|
||||
value: number | string
|
||||
}): StdReader<string, "t" | "rts"> =>
|
||||
Reader.asks(env =>
|
||||
formatLength(
|
||||
env.translate,
|
||||
env.responsiveTextSize,
|
||||
object.unit,
|
||||
object.value,
|
||||
),
|
||||
)
|
||||
Reader.asks(env => formatLength(env.translate, env.responsiveTextSize, object.unit, object.value))
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import type {
|
||||
Translate,
|
||||
TranslationKeyMatchingParams,
|
||||
} from "../../../helpers/translate.js"
|
||||
import type { Translate, TranslationKeyMatchingParams } from "../../../helpers/translate.js"
|
||||
import type { StdEnv } from "../reader.js"
|
||||
import { ResponsiveTextSize, responsive } from "../responsiveText.js"
|
||||
import { type ResponsiveTextSize, responsive } from "../responsiveText.js"
|
||||
|
||||
/**
|
||||
* Possible units to use for time spans.
|
||||
@@ -83,5 +80,4 @@ export const formatTimeSpanR = (
|
||||
export const formatCombinedTimeSpanR = (object: {
|
||||
unit: { kind: TimeSpanUnit }
|
||||
value: number | string
|
||||
}): Reader<StdEnv<"t" | "rts">, string> =>
|
||||
formatTimeSpanR(object.unit, object.value)
|
||||
}): Reader<StdEnv<"t" | "rts">, string> => formatTimeSpanR(object.unit, object.value)
|
||||
|
||||
@@ -10,76 +10,71 @@ import { MISSING_VALUE } from "./partial/unknown.js"
|
||||
/**
|
||||
* Get a JSON representation of the rules text for a .
|
||||
*/
|
||||
export const getPersonalityTraitEntityDescription =
|
||||
createEntityDescriptionCreator<
|
||||
"PersonalityTrait",
|
||||
{
|
||||
getInstanceById: GetInstanceById<
|
||||
"Publication" | "Race" | "Culture" | "PersonalityTrait"
|
||||
>
|
||||
}
|
||||
>(({ getInstanceById }, locale, { content: entry }) => {
|
||||
const { translate, translateMap, join: localeJoin } = locale
|
||||
const translation = translateMap(entry.translations)
|
||||
export const getPersonalityTraitEntityDescription = createEntityDescriptionCreator<
|
||||
"PersonalityTrait",
|
||||
{
|
||||
getInstanceById: GetInstanceById<"Publication" | "Race" | "Culture" | "PersonalityTrait">
|
||||
}
|
||||
>(({ getInstanceById }, locale, { content: entry }) => {
|
||||
const { translate, translateMap, join: localeJoin } = locale
|
||||
const translation = translateMap(entry.translations)
|
||||
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${translation.name} (${translate("Level {$level}", {
|
||||
level: romanize(entry.level),
|
||||
})})`,
|
||||
className: "personality-trait",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
...(translation.effects.map(effect => ({
|
||||
label: effect.label,
|
||||
value: effect.text,
|
||||
})) ?? []),
|
||||
entry.combination_options === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Can be combined with"),
|
||||
value: Map.groupBy(
|
||||
entry.combination_options.map(optionId =>
|
||||
getInstanceById("PersonalityTrait", optionId),
|
||||
),
|
||||
option => option?.level ?? null,
|
||||
)
|
||||
.entries()
|
||||
.toArray()
|
||||
.toSorted(on(group => group[0], compareNullish(numAsc)))
|
||||
.map(([level, options]) =>
|
||||
level === null
|
||||
? MISSING_VALUE
|
||||
: `${translate("Level {$level}", { level: romanize(level) })} ${localeJoin(
|
||||
options.map(
|
||||
option =>
|
||||
translateMap(option?.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
),
|
||||
"disjunction",
|
||||
)}`,
|
||||
)
|
||||
.join(", "),
|
||||
},
|
||||
entry.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Prerequisites"),
|
||||
value: printPersonalityTraitPrerequisites(
|
||||
getInstanceById,
|
||||
locale,
|
||||
entry.prerequisites,
|
||||
return {
|
||||
title: `${translation.name} (${translate("Level {$level}", {
|
||||
level: romanize(entry.level),
|
||||
})})`,
|
||||
className: "personality-trait",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
...translation.effects.map(effect => ({
|
||||
label: effect.label,
|
||||
value: effect.text,
|
||||
})),
|
||||
entry.combination_options === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Can be combined with"),
|
||||
value: Map.groupBy(
|
||||
entry.combination_options.map(optionId =>
|
||||
getInstanceById("PersonalityTrait", optionId),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
errata: translation.errata,
|
||||
references: entry.src,
|
||||
}
|
||||
})
|
||||
option => option?.level ?? null,
|
||||
)
|
||||
.entries()
|
||||
.toArray()
|
||||
.toSorted(on(group => group[0], compareNullish(numAsc)))
|
||||
.map(([level, options]) =>
|
||||
level === null
|
||||
? MISSING_VALUE
|
||||
: `${translate("Level {$level}", { level: romanize(level) })} ${localeJoin(
|
||||
options.map(
|
||||
option => translateMap(option?.translations)?.name ?? MISSING_VALUE,
|
||||
),
|
||||
"disjunction",
|
||||
)}`,
|
||||
)
|
||||
.join(", "),
|
||||
},
|
||||
entry.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Prerequisites"),
|
||||
value: printPersonalityTraitPrerequisites(
|
||||
getInstanceById,
|
||||
locale,
|
||||
entry.prerequisites,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
errata: translation.errata,
|
||||
references: entry.src,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -80,7 +80,7 @@ const renderLevel = (
|
||||
case "BySubtype":
|
||||
return level.BySubtype.map(
|
||||
subtype =>
|
||||
`${subtype.value} (${translateMap(subtype.translations)?.name ?? MISSING_VALUE})`,
|
||||
`${subtype.value.toFixed()} (${translateMap(subtype.translations)?.name ?? MISSING_VALUE})`,
|
||||
).join(", ")
|
||||
default:
|
||||
return assertExhaustive(level)
|
||||
@@ -161,7 +161,7 @@ const renderIntoxicantValues = (
|
||||
legality: intoxicant.legality.is_legal ? translate("legal") : translate("illegal"),
|
||||
special: translation?.special,
|
||||
addiction:
|
||||
intoxicant?.addiction === undefined
|
||||
intoxicant.addiction === undefined
|
||||
? undefined
|
||||
: renderAddiction(translate, translateMap, getInstanceById, intoxicant.addiction),
|
||||
}
|
||||
@@ -319,7 +319,7 @@ const renderDuration = (
|
||||
return renderMathOperation(duration.ExpressionBased.value, value => {
|
||||
switch (value.kind) {
|
||||
case "Constant":
|
||||
return value.Constant.toString()
|
||||
return value.Constant.toFixed()
|
||||
case "Dice":
|
||||
return renderDice(translate, value.Dice)
|
||||
case "CircleOfDamnation":
|
||||
@@ -447,7 +447,7 @@ export const getPoisonEntityDescription = createEntityDescriptionCreator<
|
||||
renderAlternativeNames(translate, translation.alternative_names),
|
||||
{
|
||||
label: translate("Level"),
|
||||
value: level.toString(),
|
||||
value: typeof level === "number" ? level.toFixed() : level,
|
||||
},
|
||||
{
|
||||
label: translate("Type"),
|
||||
|
||||
+50
-42
@@ -17,38 +17,38 @@ import {
|
||||
import { compareNumber } from "@elyukai/utils/ordering"
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import {
|
||||
import type {
|
||||
ActivatableIdentifier,
|
||||
ActivatableNameBuilderRules,
|
||||
BlessedTradition_ID,
|
||||
Blessing_ID,
|
||||
Cantrip_ID,
|
||||
CantripsOptions,
|
||||
CombatTechniqueIdentifier,
|
||||
type ActivatableIdentifier,
|
||||
type ActivatableNameBuilderRules,
|
||||
type BlessedTradition_ID,
|
||||
type Blessing_ID,
|
||||
type Cantrip_ID,
|
||||
type CantripsOptions,
|
||||
type CombatTechniquesOptions,
|
||||
type ConstantProfessionSpecialAbility,
|
||||
type CursesOptions,
|
||||
type ExperienceLevel,
|
||||
type LanguagesScriptsOptions,
|
||||
type LiturgiesOptions,
|
||||
type LiturgyIdentifier,
|
||||
type MagicalActionIdentifier,
|
||||
type Profession_ID,
|
||||
type ProfessionMagicalSkillIdentifier,
|
||||
type ProfessionPackage,
|
||||
type ProfessionPackageOptions,
|
||||
type ProfessionPrerequisiteGroup,
|
||||
type ProfessionPrerequisites,
|
||||
type ProfessionSpecialAbility,
|
||||
type ProfessionVariant,
|
||||
type ProfessionVariantPackageOptions,
|
||||
type ProfessionVariantTranslation,
|
||||
type RatedIdentifier,
|
||||
type RestrictedBlessings,
|
||||
type SkillsOptions,
|
||||
type SkillSpecializationOptions,
|
||||
type SpecialAbilityIdentifier,
|
||||
type VariantOptionAction,
|
||||
CombatTechniquesOptions,
|
||||
ConstantProfessionSpecialAbility,
|
||||
CursesOptions,
|
||||
ExperienceLevel,
|
||||
LanguagesScriptsOptions,
|
||||
LiturgiesOptions,
|
||||
LiturgyIdentifier,
|
||||
MagicalActionIdentifier,
|
||||
Profession_ID,
|
||||
ProfessionMagicalSkillIdentifier,
|
||||
ProfessionPackage,
|
||||
ProfessionPackageOptions,
|
||||
ProfessionPrerequisiteGroup,
|
||||
ProfessionPrerequisites,
|
||||
ProfessionSpecialAbility,
|
||||
ProfessionVariant,
|
||||
ProfessionVariantPackageOptions,
|
||||
ProfessionVariantTranslation,
|
||||
RatedIdentifier,
|
||||
RestrictedBlessings,
|
||||
SkillsOptions,
|
||||
SkillSpecializationOptions,
|
||||
SpecialAbilityIdentifier,
|
||||
VariantOptionAction,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type {
|
||||
@@ -146,6 +146,7 @@ const renderNumericListAcrossPackages = <T, SelectorEnv, RenderEnv>(
|
||||
(filledAcc, [values, number]) => {
|
||||
const existing = filledAcc.findIndex(([value]) => equalityFn(value, values))
|
||||
if (existing >= 0) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- existing is checked to be >= 0
|
||||
filledAcc[existing]![1][pkgIndex] = number + defaultValue
|
||||
return filledAcc
|
||||
} else {
|
||||
@@ -153,9 +154,9 @@ const renderNumericListAcrossPackages = <T, SelectorEnv, RenderEnv>(
|
||||
...filledAcc,
|
||||
[
|
||||
values,
|
||||
Array(pkgIndex)
|
||||
.fill(defaultValue)
|
||||
.concat(number + defaultValue),
|
||||
Array.from({ length: pkgIndex }, () => defaultValue).concat(
|
||||
number + defaultValue,
|
||||
),
|
||||
],
|
||||
]
|
||||
}
|
||||
@@ -338,7 +339,8 @@ const getTotalingAPValues = (
|
||||
}
|
||||
|
||||
return allSame(options, deepEqual)
|
||||
? options[0]!.ap_value
|
||||
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- not all are nullish and because all are the same, none is nullish
|
||||
options[0]!.ap_value
|
||||
: options.map(option => option?.ap_value ?? 0).join("/")
|
||||
}
|
||||
|
||||
@@ -348,9 +350,7 @@ const renderSkillsOption = (
|
||||
): StdReader<string | undefined, "t"> =>
|
||||
mapNullable(
|
||||
getTotalingAPValues(professionPackages, pkg =>
|
||||
pkg.content.options?.skills?.group === skillGroup.id
|
||||
? pkg.content.options?.skills
|
||||
: undefined,
|
||||
pkg.content.options?.skills?.group === skillGroup.id ? pkg.content.options.skills : undefined,
|
||||
),
|
||||
apValue =>
|
||||
translateR("{$apValue} AP to improve other {$skillsOfGroup}", {
|
||||
@@ -442,6 +442,7 @@ const renderCantripsOption = (
|
||||
}
|
||||
|
||||
if (allSame(cantripsOptions, deepEqual)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- not all are nullish and because all are the same, none is nullish
|
||||
return renderSingleCantripsOption(cantripsOptions[0]!)
|
||||
} else {
|
||||
return Reader.traverse(cantripsOptions, option =>
|
||||
@@ -586,7 +587,13 @@ const renderVariantSkillsOption = (
|
||||
}),
|
||||
),
|
||||
update: (base, override) =>
|
||||
plainOrInsteadOfR(base, override, b => b.ap_value, equal, Reader.of).thenW(apValue =>
|
||||
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}", {
|
||||
@@ -907,10 +914,11 @@ const renderBlessings = (professionPackages: NonEmptyArray<PreparedProfessionPac
|
||||
if (traditions.every(isEmpty)) {
|
||||
return Reader.of(undefined)
|
||||
} else if (allSame(traditions, deepEqual)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- not all are empty and because all are the same, none is empty
|
||||
return renderSingleBlessings(traditions[0]!)
|
||||
} else {
|
||||
return Reader.traverse(traditions, option =>
|
||||
option === undefined ? Reader.of("—") : renderSingleBlessings(option),
|
||||
option.length === 0 ? Reader.of("—") : renderSingleBlessings(option),
|
||||
).map(list => list.join(" / "))
|
||||
}
|
||||
}
|
||||
@@ -947,7 +955,7 @@ const renderRatedVariantChanges = <ID, Env>(
|
||||
const baseValue = baseList?.find(item => deepEqual(item.id, id))?.rating_modifier ?? 0
|
||||
return renderInstance(id).thenW(name =>
|
||||
translateR("{$replacement} instead of {$base}", {
|
||||
replacement: `${name} ${baseValue + rating_modifier}`,
|
||||
replacement: `${name} ${(baseValue + rating_modifier).toFixed()}`,
|
||||
base: baseValue,
|
||||
}),
|
||||
)
|
||||
@@ -1240,7 +1248,7 @@ export const getProfessionVersionEntityDescription = createEntityDescriptionCrea
|
||||
Reader.of(
|
||||
specialAbilityLists
|
||||
.map(specialAbilities =>
|
||||
specialAbilities === undefined
|
||||
Object.values(specialAbilities).every(list => list === undefined)
|
||||
? translate("none")
|
||||
: renderSpecialAbilities(specialAbilities).run(env),
|
||||
)
|
||||
|
||||
+33
-85
@@ -35,7 +35,7 @@ const renderBaseValueItem = (
|
||||
Reader.asks(
|
||||
({ translate }): RawDefinitionListEntityDescriptionSectionItem => ({
|
||||
label: translate(label),
|
||||
value: value < 0 ? sign(value) : value.toString(),
|
||||
value: value < 0 ? sign(value) : value.toFixed(),
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -55,8 +55,7 @@ const renderAttributeAdjustmentsItem = (
|
||||
getInstanceById,
|
||||
}): RawDefinitionListEntityDescriptionSectionItem => {
|
||||
const getAttributeAbbreviation = (id: string) =>
|
||||
translateMap(getInstanceById("Attribute", id)?.translations)
|
||||
?.abbreviation ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("Attribute", id)?.translations)?.abbreviation ?? MISSING_VALUE
|
||||
return {
|
||||
label: translate("Attribute Adjustments"),
|
||||
value: [
|
||||
@@ -77,13 +76,8 @@ const renderVariantValues = <T>(
|
||||
variants: RaceVariant[],
|
||||
selector: (variant: RaceVariant) => T,
|
||||
renderValue: (value: T) => string,
|
||||
translationSelector?: (
|
||||
variantTranslation: RaceVariantTranslation,
|
||||
) => string | undefined,
|
||||
): StdReader<
|
||||
RawDefinitionListEntityDescriptionSectionItem,
|
||||
"t" | "tm" | "lc" | "lj"
|
||||
> =>
|
||||
translationSelector?: (variantTranslation: RaceVariantTranslation) => string | undefined,
|
||||
): StdReader<RawDefinitionListEntityDescriptionSectionItem, "t" | "tm" | "lc" | "lj"> =>
|
||||
Reader.asks(
|
||||
({
|
||||
translate,
|
||||
@@ -108,7 +102,11 @@ const renderVariantValues = <T>(
|
||||
on(value => [value.value, value.valueTranslation], deepEqual),
|
||||
)
|
||||
|
||||
if (sameValues.length === 1 && sameValues[0]!.length === values.length) {
|
||||
if (
|
||||
isNotEmpty(sameValues) &&
|
||||
sameValues.length === 1 &&
|
||||
sameValues[0].length === values.length
|
||||
) {
|
||||
return {
|
||||
label: translate(label),
|
||||
value:
|
||||
@@ -124,12 +122,10 @@ const renderVariantValues = <T>(
|
||||
type: "definitionList",
|
||||
style: "nested",
|
||||
items: Map.groupBy(
|
||||
values
|
||||
.toSorted(on(item => item.name, localeCompare))
|
||||
.map(value => ({
|
||||
label: value.name,
|
||||
value: value.valueTranslation ?? renderValue(value.value),
|
||||
})),
|
||||
values.toSorted(on(item => item.name, localeCompare)).map(value => ({
|
||||
label: value.name,
|
||||
value: value.valueTranslation ?? renderValue(value.value),
|
||||
})),
|
||||
item => item.value,
|
||||
)
|
||||
.entries()
|
||||
@@ -160,13 +156,9 @@ const renderAutomaticAdvantagesOrDisadvantages = <
|
||||
? translate("none")
|
||||
: items
|
||||
.map(item => {
|
||||
const instanceTranslation = translateMap(
|
||||
getInstanceById(entity, item.id)?.translations,
|
||||
)
|
||||
const instanceTranslation = translateMap(getInstanceById(entity, item.id)?.translations)
|
||||
return (
|
||||
instanceTranslation?.name_in_library ??
|
||||
instanceTranslation?.name ??
|
||||
MISSING_VALUE
|
||||
instanceTranslation?.name_in_library ?? instanceTranslation?.name ?? MISSING_VALUE
|
||||
)
|
||||
})
|
||||
.toSorted(localeCompare)
|
||||
@@ -180,11 +172,7 @@ const renderCommonCultures = (
|
||||
items === undefined || !isNotEmpty(items)
|
||||
? translate("none")
|
||||
: items
|
||||
.map(
|
||||
itemId =>
|
||||
translateMap(getInstanceById("Culture", itemId)?.translations)
|
||||
?.name,
|
||||
)
|
||||
.map(itemId => translateMap(getInstanceById("Culture", itemId)?.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
.toSorted(localeCompare)
|
||||
.join(", "),
|
||||
@@ -237,31 +225,17 @@ export const getRaceEntityDescription = createEntityDescriptionCreator<
|
||||
items: [
|
||||
{
|
||||
label: translate("AP Value"),
|
||||
value: translate(
|
||||
".input {$value :number} {{{$value} Adventure Points}}",
|
||||
{ value: entry.ap_value },
|
||||
),
|
||||
value: translate(".input {$value :number} {{{$value} Adventure Points}}", {
|
||||
value: entry.ap_value,
|
||||
}),
|
||||
},
|
||||
renderBaseValueItem(
|
||||
"Life Point Base Value",
|
||||
entry.base_values.life_points,
|
||||
).run(env),
|
||||
renderBaseValueItem(
|
||||
"Spirit Base Value",
|
||||
entry.base_values.spirit,
|
||||
).run(env),
|
||||
renderBaseValueItem(
|
||||
"Toughness Base Value",
|
||||
entry.base_values.toughness,
|
||||
).run(env),
|
||||
renderBaseValueItem(
|
||||
"Movement Base Value",
|
||||
entry.base_values.movement,
|
||||
).run(env),
|
||||
renderAttributeAdjustmentsItem(
|
||||
totalAttributesCount,
|
||||
entry.attribute_adjustments,
|
||||
).run(env),
|
||||
renderBaseValueItem("Life Point Base Value", entry.base_values.life_points).run(env),
|
||||
renderBaseValueItem("Spirit Base Value", entry.base_values.spirit).run(env),
|
||||
renderBaseValueItem("Toughness Base Value", entry.base_values.toughness).run(env),
|
||||
renderBaseValueItem("Movement Base Value", entry.base_values.movement).run(env),
|
||||
renderAttributeAdjustmentsItem(totalAttributesCount, entry.attribute_adjustments).run(
|
||||
env,
|
||||
),
|
||||
renderVariantValues(
|
||||
"Common Cultures",
|
||||
raceVariants,
|
||||
@@ -273,11 +247,7 @@ export const getRaceEntityDescription = createEntityDescriptionCreator<
|
||||
: renderValueWithPossibleTranslation(
|
||||
"Automatic Advantages",
|
||||
entry.automatic_advantages,
|
||||
v =>
|
||||
renderAutomaticAdvantagesOrDisadvantages(
|
||||
"Advantage",
|
||||
v,
|
||||
).run(env),
|
||||
v => renderAutomaticAdvantagesOrDisadvantages("Advantage", v).run(env),
|
||||
translation.automatic_advantages,
|
||||
).run(env),
|
||||
entry.automatic_disadvantages === undefined
|
||||
@@ -285,20 +255,14 @@ export const getRaceEntityDescription = createEntityDescriptionCreator<
|
||||
: renderValueWithPossibleTranslation(
|
||||
"Automatic Disadvantages",
|
||||
entry.automatic_disadvantages,
|
||||
v =>
|
||||
renderAutomaticAdvantagesOrDisadvantages(
|
||||
"Disadvantage",
|
||||
v,
|
||||
).run(env),
|
||||
v => renderAutomaticAdvantagesOrDisadvantages("Disadvantage", v).run(env),
|
||||
translation.automatic_disadvantages,
|
||||
).run(env),
|
||||
entry.strongly_recommended_advantages === undefined &&
|
||||
entry.strongly_recommended_disadvantages === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate(
|
||||
"Strongly recommended Advantages and Disadvantages",
|
||||
),
|
||||
label: translate("Strongly recommended Advantages and Disadvantages"),
|
||||
value: `${translate("The following advantages and disadvantages distinguish Aventurian {$race}. You should choose these advantages and disadvantages. If you don’t want to take them, talk to your GM.", { race: translation.name })} ${renderCommonnessRatedAdvantagesAndDisadvantages(
|
||||
entry.strongly_recommended_advantages,
|
||||
entry.strongly_recommended_disadvantages,
|
||||
@@ -308,44 +272,28 @@ export const getRaceEntityDescription = createEntityDescriptionCreator<
|
||||
"Common Advantages",
|
||||
raceVariants,
|
||||
v => v.common_advantages,
|
||||
advs =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Advantage",
|
||||
advs,
|
||||
).run(env),
|
||||
advs => renderCommonnessRatedAdvantagesOrDisadvantages("Advantage", advs).run(env),
|
||||
vt => vt.common_advantages,
|
||||
).run(env),
|
||||
renderVariantValues(
|
||||
"Common Disadvantages",
|
||||
raceVariants,
|
||||
v => v.common_disadvantages,
|
||||
advs =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Disadvantage",
|
||||
advs,
|
||||
).run(env),
|
||||
advs => renderCommonnessRatedAdvantagesOrDisadvantages("Disadvantage", advs).run(env),
|
||||
vt => vt.common_disadvantages,
|
||||
).run(env),
|
||||
renderVariantValues(
|
||||
"Uncommon Advantages",
|
||||
raceVariants,
|
||||
v => v.uncommon_advantages,
|
||||
advs =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Advantage",
|
||||
advs,
|
||||
).run(env),
|
||||
advs => renderCommonnessRatedAdvantagesOrDisadvantages("Advantage", advs).run(env),
|
||||
vt => vt.uncommon_advantages,
|
||||
).run(env),
|
||||
renderVariantValues(
|
||||
"Uncommon Disadvantages",
|
||||
raceVariants,
|
||||
v => v.uncommon_disadvantages,
|
||||
advs =>
|
||||
renderCommonnessRatedAdvantagesOrDisadvantages(
|
||||
"Disadvantage",
|
||||
advs,
|
||||
).run(env),
|
||||
advs => renderCommonnessRatedAdvantagesOrDisadvantages("Disadvantage", advs).run(env),
|
||||
vt => vt.uncommon_disadvantages,
|
||||
).run(env),
|
||||
],
|
||||
|
||||
+29
-32
@@ -3,37 +3,34 @@ import { createEntityDescriptionCreator } from "../creator.js"
|
||||
/**
|
||||
* Get a JSON representation of the rules text for a sex practice.
|
||||
*/
|
||||
export const getSexPracticeEntityDescription =
|
||||
createEntityDescriptionCreator<"SexPractice">(
|
||||
(_, { translate, translateMap }, { content: entry }) => {
|
||||
const translation = translateMap(entry.translations)
|
||||
export const getSexPracticeEntityDescription = createEntityDescriptionCreator<"SexPractice">(
|
||||
(_, { translate, translateMap }, { content: entry }) => {
|
||||
const translation = translateMap(entry.translations)
|
||||
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
title: translation.name,
|
||||
className: "sex-practice",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
{ label: translate("Rules"), value: translation.rules },
|
||||
{ label: translate("Duration"), value: translation.duration },
|
||||
translation.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Prerequisites"),
|
||||
value: translation.prerequisites,
|
||||
},
|
||||
translation.failed === undefined
|
||||
? undefined
|
||||
: { label: translate("Failed"), value: translation.failed },
|
||||
],
|
||||
},
|
||||
],
|
||||
references: entry.src,
|
||||
}
|
||||
},
|
||||
)
|
||||
return {
|
||||
title: translation.name,
|
||||
className: "sex-practice",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
{ label: translate("Rules"), value: translation.rules },
|
||||
{ label: translate("Duration"), value: translation.duration },
|
||||
translation.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Prerequisites"),
|
||||
value: translation.prerequisites,
|
||||
},
|
||||
{ label: translate("Failed"), value: translation.failed },
|
||||
],
|
||||
},
|
||||
],
|
||||
references: entry.src,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
+9
-26
@@ -1,9 +1,6 @@
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type {
|
||||
ResolvedNewSkillApplication,
|
||||
ResolvedSkillUse,
|
||||
} from "optolith-database-schema/cache"
|
||||
import type { ResolvedNewSkillApplication, ResolvedSkillUse } from "optolith-database-schema/cache"
|
||||
import type { ActivatableIdentifier } from "optolith-database-schema/gen"
|
||||
import { fromUniformCase } from "tsondb/schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
@@ -13,20 +10,15 @@ import type {
|
||||
GetInstanceById,
|
||||
} from "../helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "../helpers/locale.js"
|
||||
import type {
|
||||
GetAllResolvedNewSkillApplications,
|
||||
GetAllResolvedSkillUses,
|
||||
} from "../index.js"
|
||||
import { BaseActivatableTranslation } from "./activatable.js"
|
||||
import type { GetAllResolvedNewSkillApplications, GetAllResolvedSkillUses } from "../index.js"
|
||||
import type { BaseActivatableTranslation } from "./activatable.js"
|
||||
import { renderImprovementCost } from "./partial/rated/improvementCost.js"
|
||||
import { renderSkillCheck } from "./partial/rated/skillCheck.js"
|
||||
|
||||
const getUsesOrNewApplications = <
|
||||
T extends ResolvedNewSkillApplication | ResolvedSkillUse,
|
||||
>(
|
||||
const getUsesOrNewApplications = (
|
||||
getInstanceById: GetInstanceById<"Aspect" | ActivatableIdentifier["kind"]>,
|
||||
locale: LocaleEnvironment,
|
||||
items: T[],
|
||||
items: (ResolvedNewSkillApplication | ResolvedSkillUse)[],
|
||||
) =>
|
||||
items
|
||||
.map(x => {
|
||||
@@ -72,11 +64,7 @@ export const getSkillEntityDescription = createEntityDescriptionCreator<
|
||||
"Publication" | "Attribute" | ActivatableIdentifier["kind"] | "Aspect"
|
||||
>
|
||||
getAllInstances: GetAllInstances<
|
||||
| "BlessedTradition"
|
||||
| "Disease"
|
||||
| "Region"
|
||||
| "SkillUse"
|
||||
| "NewSkillApplication"
|
||||
"BlessedTradition" | "Disease" | "Region" | "SkillUse" | "NewSkillApplication"
|
||||
>
|
||||
getChildInstancesForInstanceId: GetAllChildInstancesForParent<"SkillApplication">
|
||||
getAllResolvedNewSkillApplications: GetAllResolvedNewSkillApplications
|
||||
@@ -107,11 +95,7 @@ export const getSkillEntityDescription = createEntityDescriptionCreator<
|
||||
getAllResolvedNewSkillApplications(id),
|
||||
)
|
||||
|
||||
const uses = getUsesOrNewApplications(
|
||||
getInstanceById,
|
||||
locale,
|
||||
getAllResolvedSkillUses(id),
|
||||
)
|
||||
const uses = getUsesOrNewApplications(getInstanceById, locale, getAllResolvedSkillUses(id))
|
||||
|
||||
const applications = [
|
||||
...getChildInstancesForInstanceId("SkillApplication", id)
|
||||
@@ -178,10 +162,9 @@ export const getSkillEntityDescription = createEntityDescriptionCreator<
|
||||
? translate("Yes")
|
||||
: entry.encumbrance.kind === "No"
|
||||
? translate("No")
|
||||
: (translation.encumbrance_description ??
|
||||
translate("Maybe")),
|
||||
: (translation.encumbrance_description ?? translate("Maybe")),
|
||||
},
|
||||
translation?.tools === undefined
|
||||
translation.tools === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Tools"),
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Lazy } from "@elyukai/utils/lazy"
|
||||
import { compareNullish } from "@elyukai/utils/ordering"
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { romanize } from "@elyukai/utils/roman"
|
||||
import { Compare, numAsc } from "@optolith/helpers/compare"
|
||||
import { numAsc, type Compare } from "@optolith/helpers/compare"
|
||||
import { isNotNullish, mapNullable } from "@optolith/helpers/nullable"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import {
|
||||
@@ -32,7 +32,7 @@ import { Case } from "tsondb/schema/gen"
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type { GetAllChildInstancesForParent, GetInstanceById } from "../helpers/getTypes.js"
|
||||
import type { LocaleCompare } from "../helpers/locale.js"
|
||||
import { Translate, TranslateMap, type TranslationKeysWithoutParams } from "../helpers/translate.js"
|
||||
import type { Translate, TranslateMap, TranslationKeysWithoutParams } from "../helpers/translate.js"
|
||||
import { type IdMap, type RawDefinitionListEntityDescriptionSectionItem } from "../index.js"
|
||||
import { renderAnimalTypesSection } from "./partial/animalTypes.js"
|
||||
import { renderEnhancements } from "./partial/enhancements.js"
|
||||
@@ -1449,7 +1449,7 @@ const renderMagicalRuneCost = (options: Lazy<MagicalRuneOption[]>, cost: Magical
|
||||
options,
|
||||
option => option.cost?.value,
|
||||
compareNullish(numAsc),
|
||||
num => num?.toString() ?? MISSING_VALUE,
|
||||
num => num?.toFixed() ?? MISSING_VALUE,
|
||||
).thenW(formatEnergyR)
|
||||
default:
|
||||
return assertExhaustive(cost)
|
||||
|
||||
+5
-11
@@ -1,11 +1,9 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
|
||||
import * as Database from "optolith-database-schema/gen"
|
||||
import { IdArgsVariant } from "tsondb/schema/gen"
|
||||
import type * as Database from "optolith-database-schema/gen"
|
||||
import type { IdArgsVariant } from "tsondb/schema/gen"
|
||||
|
||||
export type GetInstanceById<
|
||||
in T extends Extract<keyof Database.EntityMap, string>,
|
||||
> = <U extends T>(
|
||||
export type GetInstanceById<in T extends Extract<keyof Database.EntityMap, string>> = <U extends T>(
|
||||
...args: IdArgsVariant<Database.EntityMap, U>
|
||||
) => Database.EntityMap[U] | undefined
|
||||
|
||||
@@ -23,13 +21,9 @@ export type GetAllInstances<T extends keyof Database.EntityMap> = <U extends T>(
|
||||
entity: U,
|
||||
) => { id: string; content: Database.EntityMap[U] }[]
|
||||
|
||||
export type CountInstances<T extends keyof Database.EntityMap> = <U extends T>(
|
||||
entity: U,
|
||||
) => number
|
||||
export type CountInstances<T extends keyof Database.EntityMap> = <U extends T>(entity: U) => number
|
||||
|
||||
export type GetAllChildInstancesForParent<
|
||||
T extends keyof Database.ChildEntityMap,
|
||||
> = <U extends T>(
|
||||
export type GetAllChildInstancesForParent<T extends keyof Database.ChildEntityMap> = <U extends T>(
|
||||
entity: U,
|
||||
parentId: Database.ChildEntityMap[U][2],
|
||||
) => { id: string; content: Database.ChildEntityMap[U][0] }[]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Compare } from "@optolith/helpers/compare"
|
||||
import type { Compare } from "@optolith/helpers/compare"
|
||||
import type { LocaleMeasurementAdjustments } from "optolith-database-schema/gen"
|
||||
import { Translate, TranslateMap, type Format } from "./translate.js"
|
||||
import type { Format, Translate, TranslateMap } from "./translate.js"
|
||||
|
||||
/**
|
||||
* The type of list to join in a locale-aware way.
|
||||
@@ -14,6 +14,7 @@ export type LocaleEnvironment = {
|
||||
id: string
|
||||
format: Format
|
||||
formatDate: (date: Date) => string
|
||||
formatNumber: FormatNumber
|
||||
translate: Translate
|
||||
translateMap: TranslateMap
|
||||
compare: LocaleCompare
|
||||
@@ -30,3 +31,8 @@ export type LocaleCompare = Compare<string>
|
||||
* A function that joins a list of strings according to the locale's rules for the given type.
|
||||
*/
|
||||
export type LocaleJoin = (list: string[], type: LocaleJoinType) => string
|
||||
|
||||
/**
|
||||
* A function that formats a number according to the locale’s rules.
|
||||
*/
|
||||
export type FormatNumber = (value: number) => string
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import { Locale } from "optolith-database-schema/gen"
|
||||
import type { Locale } from "optolith-database-schema/gen"
|
||||
import { ResponsiveTextSize } from "../entities/partial/responsiveText.js"
|
||||
|
||||
/**
|
||||
@@ -23,18 +23,17 @@ export type Translate = <K extends keyof Translations>(
|
||||
/**
|
||||
* Extracts the parameters for a given translation key, or never if the key does not have parameters.
|
||||
*/
|
||||
export type TranslationParams<K extends keyof Translations> =
|
||||
Translations[K] extends string & { __params: infer Params }
|
||||
? Params
|
||||
: undefined
|
||||
export type TranslationParams<K extends keyof Translations> = Translations[K] extends string & {
|
||||
__params: infer Params
|
||||
}
|
||||
? Params
|
||||
: undefined
|
||||
|
||||
/**
|
||||
* Extracts the parameters for a given translation key as an array, or an empty array if the key does not have parameters.
|
||||
*/
|
||||
export type TranslationParamsInArray<K extends keyof Translations> =
|
||||
Translations[K] extends string & { __params: infer Params }
|
||||
? [params: Params]
|
||||
: []
|
||||
Translations[K] extends string & { __params: infer Params } ? [params: Params] : []
|
||||
|
||||
/**
|
||||
* A dictionary of locale identifiers to other values.
|
||||
|
||||
+2
-1
@@ -71,7 +71,7 @@ import type {
|
||||
} from "./helpers/getTypes.js"
|
||||
import type { LocaleEnvironment } from "./helpers/locale.js"
|
||||
|
||||
export { LocaleEnvironment }
|
||||
export type { LocaleEnvironment }
|
||||
|
||||
/**
|
||||
* A JSON representation of the rules text for a library entry.
|
||||
@@ -434,6 +434,7 @@ export type GetAllResolvedSkillUses = (id: Skill_ID) => ResolvedSkillUse[]
|
||||
/**
|
||||
* Get a JSON representation of the rules text for an entry in the database.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- false positive
|
||||
export const getEntityDescription = <E extends AvailableCreatorEntity>(
|
||||
database: TSONDB<TSONDBTypes>,
|
||||
localeEnv: LocaleEnvironment,
|
||||
|
||||
+7
-23
@@ -2,12 +2,8 @@ import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { PublicationRefs } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../helpers/locale.js"
|
||||
import {
|
||||
fromRawPageRange,
|
||||
normalizePageRanges,
|
||||
printPageRanges,
|
||||
} from "./pageRange.js"
|
||||
import type { LocaleEnvironment } from "../helpers/locale.js"
|
||||
import { fromRawPageRange, normalizePageRanges, printPageRanges } from "./pageRange.js"
|
||||
|
||||
/**
|
||||
* Returns the translation of the references.
|
||||
@@ -20,9 +16,7 @@ export const getReferencesTranslation = (
|
||||
references
|
||||
.map(ref => {
|
||||
const publication = getInstanceById("Publication", ref.id)
|
||||
const publicationTranslations = locale.translateMap(
|
||||
publication?.translations,
|
||||
)
|
||||
const publicationTranslations = locale.translateMap(publication?.translations)
|
||||
const occurrences = locale.translateMap(ref.occurrences)
|
||||
|
||||
if (
|
||||
@@ -33,17 +27,12 @@ export const getReferencesTranslation = (
|
||||
return undefined
|
||||
}
|
||||
|
||||
const initialPageRanges = normalizePageRanges(
|
||||
occurrences.initial.pages.map(fromRawPageRange),
|
||||
)
|
||||
const initialPageRanges = normalizePageRanges(occurrences.initial.pages.map(fromRawPageRange))
|
||||
|
||||
const initial =
|
||||
occurrences.initial.printing === undefined
|
||||
? printPageRanges(locale.translate, initialPageRanges)
|
||||
: `${printPageRanges(
|
||||
locale.translate,
|
||||
initialPageRanges,
|
||||
)} (${locale.translate(
|
||||
: `${printPageRanges(locale.translate, initialPageRanges)} (${locale.translate(
|
||||
".input {$printing :number} {{since the {$printing}. printing}}",
|
||||
{ printing: occurrences.initial.printing },
|
||||
)})`
|
||||
@@ -52,13 +41,8 @@ export const getReferencesTranslation = (
|
||||
occurrences.revisions?.map(rev => {
|
||||
switch (rev.kind) {
|
||||
case "Since": {
|
||||
const pageRanges = normalizePageRanges(
|
||||
rev.Since.pages.map(fromRawPageRange),
|
||||
)
|
||||
return `${printPageRanges(
|
||||
locale.translate,
|
||||
pageRanges,
|
||||
)} (${locale.translate(
|
||||
const pageRanges = normalizePageRanges(rev.Since.pages.map(fromRawPageRange))
|
||||
return `${printPageRanges(locale.translate, pageRanges)} (${locale.translate(
|
||||
".input {$printing :number} {{since the {$printing}. printing}}",
|
||||
{ printing: rev.Since.printing },
|
||||
)})`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Compare } from "@optolith/helpers/compare"
|
||||
import type { Compare } from "@optolith/helpers/compare"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { Page } from "optolith-database-schema/gen"
|
||||
import { Translate } from "../helpers/translate.js"
|
||||
import type { Page } from "optolith-database-schema/gen"
|
||||
import type { Translate } from "../helpers/translate.js"
|
||||
|
||||
/**
|
||||
* A comparison function for two pages.
|
||||
@@ -16,8 +16,8 @@ export const comparePage: Compare<Page> = (a, b) => {
|
||||
return b.kind === "Numbered"
|
||||
? a.Numbered - b.Numbered
|
||||
: b.kind === "InsideCoverFront"
|
||||
? 1
|
||||
: -1
|
||||
? 1
|
||||
: -1
|
||||
default:
|
||||
return assertExhaustive(a)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export const printPage = (translate: Translate, page: Page) => {
|
||||
case "InsideCoverBack":
|
||||
return translate("Back Cover Inside")
|
||||
case "Numbered":
|
||||
return page.Numbered.toString()
|
||||
return page.Numbered.toFixed()
|
||||
default:
|
||||
return assertExhaustive(page)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { range } from "@optolith/helpers/array"
|
||||
import { Page, PageRange as RawPageRange } from "optolith-database-schema/gen"
|
||||
import { Translate } from "../helpers/translate.js"
|
||||
import type { Page, PageRange as RawPageRange } from "optolith-database-schema/gen"
|
||||
import type { Translate } from "../helpers/translate.js"
|
||||
import { comparePage, equalsPage, printPage, succ } from "./page.js"
|
||||
|
||||
/**
|
||||
@@ -62,16 +62,10 @@ export const normalizePageRanges = (ranges: PageRange[]): PageRange[] =>
|
||||
export const printPageRange = (translate: Translate, pageRange: PageRange) =>
|
||||
pageRange.lastPage === undefined
|
||||
? printPage(translate, pageRange.firstPage)
|
||||
: `${printPage(translate, pageRange.firstPage)}–${printPage(
|
||||
translate,
|
||||
pageRange.lastPage,
|
||||
)}`
|
||||
: `${printPage(translate, pageRange.firstPage)}–${printPage(translate, pageRange.lastPage)}`
|
||||
|
||||
/**
|
||||
* Returns a string representation of a list of page ranges.
|
||||
*/
|
||||
export const printPageRanges = (
|
||||
translate: Translate,
|
||||
pageRanges: PageRange[],
|
||||
) =>
|
||||
export const printPageRanges = (translate: Translate, pageRanges: PageRange[]) =>
|
||||
pageRanges.map(pageRange => printPageRange(translate, pageRange)).join(", ")
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
import {
|
||||
joinPrerequisiteParts,
|
||||
PrerequisitePart,
|
||||
type PrerequisitePart,
|
||||
} from "../../../../src/entities/partial/prerequisites/part.js"
|
||||
import { Case } from "../../../../src/helpers/enums.js"
|
||||
import { defaultLocaleEnvironment } from "../../../helpers/locale.js"
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import { LocaleEnvironment } from "../../src/helpers/locale.js"
|
||||
import { formatDateMock, formatMock, translateMapMock, translateMock } from "./translate.js"
|
||||
import type { LocaleEnvironment } from "../../src/helpers/locale.js"
|
||||
import {
|
||||
formatDateMock,
|
||||
formatMock,
|
||||
formatNumberMock,
|
||||
translateMapMock,
|
||||
translateMock,
|
||||
} from "./translate.js"
|
||||
|
||||
const localeId = "en-US"
|
||||
|
||||
@@ -23,6 +29,7 @@ export const defaultLocaleEnvironment: LocaleEnvironment = {
|
||||
id: "en-US",
|
||||
format: formatMock,
|
||||
formatDate: formatDateMock,
|
||||
formatNumber: formatNumberMock,
|
||||
translate: translateMock,
|
||||
translateMap: translateMapMock,
|
||||
compare: (x, y) => collator.compare(x, y),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MessageFormat } from "messageformat"
|
||||
import { Translate, TranslateMap, type Format } from "../../src/helpers/translate.js"
|
||||
import type { FormatNumber } from "../../src/helpers/locale.js"
|
||||
import type { Format, Translate, TranslateMap } from "../../src/helpers/translate.js"
|
||||
|
||||
/**
|
||||
* A mocked format function.
|
||||
@@ -11,6 +12,11 @@ export const formatMock: Format = (text, args) => new MessageFormat("en", text).
|
||||
*/
|
||||
export const formatDateMock = (date: Date): string => date.toISOString().split("T")[0] ?? ""
|
||||
|
||||
/**
|
||||
* A mocked number format function.
|
||||
*/
|
||||
export const formatNumberMock: FormatNumber = value => value.toFixed()
|
||||
|
||||
/**
|
||||
* A mocked translate function.
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import assert from "assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
import { Page } from "optolith-database-schema/gen"
|
||||
import type { Page } from "optolith-database-schema/gen"
|
||||
import {
|
||||
fromRawPageRange,
|
||||
normalizePageRanges,
|
||||
PageRange,
|
||||
type PageRange,
|
||||
printPageRange,
|
||||
printPageRanges,
|
||||
} from "../../src/references/pageRange.js"
|
||||
|
||||
Reference in New Issue
Block a user