From 5f34eaafe50bf72fdec923071414fabf3b0bb00b Mon Sep 17 00:00:00 2001 From: Lukas Obermann Date: Fri, 13 Mar 2026 18:15:39 +0100 Subject: [PATCH] style: fix potential bugs reported by eslint --- eslint.config.js | 100 ++++--- package-lock.json | 16 +- package.json | 4 +- scripts/testOutput.ts | 4 +- src/creator.ts | 20 +- src/entities/activatable.ts | 258 ++++++------------ src/entities/culture.ts | 239 +++++----------- src/entities/curriculum.ts | 188 +++++-------- src/entities/derivedCharacteristic.ts | 98 +++---- src/entities/disease.ts | 31 +-- src/entities/elixir.ts | 184 ++++++------- src/entities/equipment.ts | 38 +-- src/entities/equipmentPackage.ts | 9 +- src/entities/experienceLevel.ts | 14 +- src/entities/influence.ts | 4 +- src/entities/liturgicalChant.ts | 6 +- src/entities/partial/adventurePointsValue.ts | 165 ++++------- src/entities/partial/enhancements.ts | 8 +- src/entities/partial/herbary.ts | 65 ++--- src/entities/partial/mathOperation.ts | 36 +-- .../partial/prerequisites/displayOption.ts | 4 +- src/entities/partial/prerequisites/index.ts | 31 ++- src/entities/partial/prerequisites/part.ts | 4 +- .../prerequisites/prerequisiteGroups.ts | 15 +- .../prerequisites/single/activatable.ts | 6 +- .../prerequisites/single/animistPower.ts | 6 +- .../prerequisites/single/blessedTradition.ts | 4 +- .../single/commonSuggestedByRCP.ts | 9 +- .../partial/prerequisites/single/culture.ts | 8 +- .../prerequisites/single/enhancement.ts | 12 +- .../partial/prerequisites/single/influence.ts | 6 +- .../prerequisites/single/magicalTradition.ts | 4 +- .../single/noOtherAncestorBloodAdvantage.ts | 4 +- .../partial/prerequisites/single/pact.ts | 6 +- .../prerequisites/single/personalityTrait.ts | 8 +- .../prerequisites/single/primaryAttribute.ts | 8 +- .../prerequisites/single/profession.ts | 8 +- .../prerequisites/single/publication.ts | 8 +- .../partial/prerequisites/single/race.ts | 8 +- .../partial/prerequisites/single/rated.ts | 6 +- .../single/ratedMinimumNumber.ts | 8 +- .../partial/prerequisites/single/ratedSum.ts | 8 +- .../partial/prerequisites/single/rule.ts | 6 +- .../partial/prerequisites/single/sex.ts | 2 +- .../single/sexualCharacteristic.ts | 11 +- .../prerequisites/single/socialStatus.ts | 6 +- .../partial/prerequisites/single/state.ts | 8 +- .../partial/prerequisites/single/text.ts | 6 +- .../partial/rated/activatable/castingTime.ts | 73 ++--- .../partial/rated/activatable/cost.ts | 106 +++---- .../partial/rated/activatable/duration.ts | 42 +-- .../partial/rated/activatable/effect.ts | 7 +- .../partial/rated/activatable/range.ts | 45 +-- .../partial/rated/activatable/speed.ts | 15 +- .../rated/activatable/targetCategory.ts | 30 +- src/entities/partial/reader.ts | 11 +- src/entities/partial/units/length.ts | 27 +- src/entities/partial/units/timeSpan.ts | 10 +- src/entities/personalityTrait.ts | 135 +++++---- src/entities/poison.ts | 8 +- src/entities/profession.ts | 92 ++++--- src/entities/race.ts | 118 +++----- src/entities/sexPractice.ts | 61 ++--- src/entities/skill.ts | 35 +-- src/entities/spell.ts | 6 +- src/helpers/getTypes.ts | 16 +- src/helpers/locale.ts | 10 +- src/helpers/translate.ts | 15 +- src/index.ts | 3 +- src/references/index.ts | 30 +- src/references/page.ts | 12 +- src/references/pageRange.ts | 14 +- test/entities/partial/prerequisites/part.ts | 2 +- test/helpers/locale.ts | 11 +- test/helpers/translate.ts | 8 +- test/references/pageRange.ts | 4 +- 76 files changed, 1037 insertions(+), 1616 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 9766c92..32d5e21 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -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", + }, }, -] +]) diff --git a/package-lock.json b/package-lock.json index 975a851..e1f97aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/package.json b/package.json index 821ca73..8b1d83a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/testOutput.ts b/scripts/testOutput.ts index c9964a3..ff7e822 100644 --- a/scripts/testOutput.ts +++ b/scripts/testOutput.ts @@ -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", diff --git a/src/creator.ts b/src/creator.ts index a0cb93f..85c08c9 100644 --- a/src/creator.ts +++ b/src/creator.ts @@ -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" diff --git a/src/entities/activatable.ts b/src/entities/activatable.ts index 697ced6..114fbed 100644 --- a/src/entities/activatable.ts +++ b/src/entities/activatable.ts @@ -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, 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( - entry.translations, - ) + const translation = translateMap(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( - 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( - getInstanceById( - parentId.kind, - fromUniformCase(parentId), - )?.translations, + getInstanceById(parentId.kind, fromUniformCase(parentId))?.translations, )?.name }, () => getAllResolvedSelectOptions(wrappedId), diff --git a/src/entities/culture.ts b/src/entities/culture.ts index 5fed73f..2144e56 100644 --- a/src/entities/culture.ts +++ b/src/entities/culture.ts @@ -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 = ( "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 = ( })), ) -const renderRarity = ( - rarity: Rarity | undefined, -): StdReader => { +const renderRarity = (rarity: Rarity | undefined): StdReader => { if (rarity === undefined) { return Reader.of(undefined) } @@ -136,10 +124,7 @@ const renderWeighted = ( 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, 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 = ( 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, - ], + string | [RawEntityDescriptionSectionContent], "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 - 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, diff --git a/src/entities/curriculum.ts b/src/entities/curriculum.ts index d4ab399..d1a3a89 100644 --- a/src/entities/curriculum.ts +++ b/src/entities/curriculum.ts @@ -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, 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, 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 => { - 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}", { diff --git a/src/entities/derivedCharacteristic.ts b/src/entities/derivedCharacteristic.ts index 4f671c3..3630a43 100644 --- a/src/entities/derivedCharacteristic.ts +++ b/src/entities/derivedCharacteristic.ts @@ -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, + ), }, ], - } - }, - ) + }, + ], + } +}) diff --git a/src/entities/disease.ts b/src/entities/disease.ts index 81cfd0a..504ecaf 100644 --- a/src/entities/disease.ts +++ b/src/entities/disease.ts @@ -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"), diff --git a/src/entities/elixir.ts b/src/entities/elixir.ts index c9374d6..b272569 100644 --- a/src/entities/elixir.ts +++ b/src/entities/elixir.ts @@ -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, + } +}) diff --git a/src/entities/equipment.ts b/src/entities/equipment.ts index 6770256..c1983cf 100644 --- a/src/entities/equipment.ts +++ b/src/entities/equipment.ts @@ -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, weight: Weight | JewelryMaterialDifference, ): 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, -): RawDefinitionListEntityDescriptionSectionItem => { + formatNumber: FormatNumber, + cost: Cost | BookCost | JewelryMaterialDifference, +): { 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 | BookCost | JewelryMaterialDifference weight?: Weight | JewelryMaterialDifference 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, diff --git a/src/entities/equipmentPackage.ts b/src/entities/equipmentPackage.ts index 0000370..e4ca168 100644 --- a/src/entities/equipmentPackage.ts +++ b/src/entities/equipmentPackage.ts @@ -129,12 +129,12 @@ const getAtomicEquipmentCost = (entry: TaggedEntity } } 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 - } => item.content !== undefined, + } => item.content.content !== undefined, ) ?? [] return { diff --git a/src/entities/experienceLevel.ts b/src/entities/experienceLevel.ts index 52362f5..3f0193d 100644 --- a/src/entities/experienceLevel.ts +++ b/src/entities/experienceLevel.ts @@ -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(), }, ], }, diff --git a/src/entities/influence.ts b/src/entities/influence.ts index a9f7f58..de0bc0c 100644 --- a/src/entities/influence.ts +++ b/src/entities/influence.ts @@ -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 : { diff --git a/src/entities/liturgicalChant.ts b/src/entities/liturgicalChant.ts index 24c4179..04b8a56 100644 --- a/src/entities/liturgicalChant.ts +++ b/src/entities/liturgicalChant.ts @@ -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" diff --git a/src/entities/partial/adventurePointsValue.ts b/src/entities/partial/adventurePointsValue.ts index 32c676f..7dbde2c 100644 --- a/src/entities/partial/adventurePointsValue.ts +++ b/src/entities/partial/adventurePointsValue.ts @@ -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 = ( locale: LocaleEnvironment, derivedLabel: () => string, getAllSelectOptions: () => ResolvedSelectOption[], - getNameForSelectOptionId: ( - id: ResolvedSelectOptionIdentifier, - ) => string | undefined, + getNameForSelectOptionId: (id: ResolvedSelectOptionIdentifier) => string | undefined, config: SelectOptionsAdventurePointsValue | 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", diff --git a/src/entities/partial/enhancements.ts b/src/entities/partial/enhancements.ts index 08e3e96..bbd33e3 100644 --- a/src/entities/partial/enhancements.ts +++ b/src/entities/partial/enhancements.ts @@ -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 => { diff --git a/src/entities/partial/herbary.ts b/src/entities/partial/herbary.ts index 7202ffa..eb2ace3 100644 --- a/src/entities/partial/herbary.ts +++ b/src/entities/partial/herbary.ts @@ -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(", "), } diff --git a/src/entities/partial/mathOperation.ts b/src/entities/partial/mathOperation.ts index 9f3772e..5b83f0f 100644 --- a/src/entities/partial/mathOperation.ts +++ b/src/entities/partial/mathOperation.ts @@ -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 = ( addParenthesisTo: MathOperation["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 = ( ): 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 = ( 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": { diff --git a/src/entities/partial/prerequisites/displayOption.ts b/src/entities/partial/prerequisites/displayOption.ts index cc7508c..658a46f 100644 --- a/src/entities/partial/prerequisites/displayOption.ts +++ b/src/entities/partial/prerequisites/displayOption.ts @@ -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. diff --git a/src/entities/partial/prerequisites/index.ts b/src/entities/partial/prerequisites/index.ts index 977ac10..6d7935c 100644 --- a/src/entities/partial/prerequisites/index.ts +++ b/src/entities/partial/prerequisites/index.ts @@ -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 = ( 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 = ( 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, diff --git a/src/entities/partial/prerequisites/part.ts b/src/entities/partial/prerequisites/part.ts index 555dbcd..1debc5b 100644 --- a/src/entities/partial/prerequisites/part.ts +++ b/src/entities/partial/prerequisites/part.ts @@ -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( diff --git a/src/entities/partial/prerequisites/prerequisiteGroups.ts b/src/entities/partial/prerequisites/prerequisiteGroups.ts index 447f671..e2a475e 100644 --- a/src/entities/partial/prerequisites/prerequisiteGroups.ts +++ b/src/entities/partial/prerequisites/prerequisiteGroups.ts @@ -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" diff --git a/src/entities/partial/prerequisites/single/activatable.ts b/src/entities/partial/prerequisites/single/activatable.ts index 3ae1e91..92edb0e 100644 --- a/src/entities/partial/prerequisites/single/activatable.ts +++ b/src/entities/partial/prerequisites/single/activatable.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/animistPower.ts b/src/entities/partial/prerequisites/single/animistPower.ts index bd32ffc..eead2c0 100644 --- a/src/entities/partial/prerequisites/single/animistPower.ts +++ b/src/entities/partial/prerequisites/single/animistPower.ts @@ -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(" "), diff --git a/src/entities/partial/prerequisites/single/blessedTradition.ts b/src/entities/partial/prerequisites/single/blessedTradition.ts index 347f6a7..045c9f5 100644 --- a/src/entities/partial/prerequisites/single/blessedTradition.ts +++ b/src/entities/partial/prerequisites/single/blessedTradition.ts @@ -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, diff --git a/src/entities/partial/prerequisites/single/commonSuggestedByRCP.ts b/src/entities/partial/prerequisites/single/commonSuggestedByRCP.ts index a48fcba..ebde929 100644 --- a/src/entities/partial/prerequisites/single/commonSuggestedByRCP.ts +++ b/src/entities/partial/prerequisites/single/commonSuggestedByRCP.ts @@ -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") diff --git a/src/entities/partial/prerequisites/single/culture.ts b/src/entities/partial/prerequisites/single/culture.ts index 4e807c1..7f0b230 100644 --- a/src/entities/partial/prerequisites/single/culture.ts +++ b/src/entities/partial/prerequisites/single/culture.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/enhancement.ts b/src/entities/partial/prerequisites/single/enhancement.ts index 79084b5..56ae06f 100644 --- a/src/entities/partial/prerequisites/single/enhancement.ts +++ b/src/entities/partial/prerequisites/single/enhancement.ts @@ -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, diff --git a/src/entities/partial/prerequisites/single/influence.ts b/src/entities/partial/prerequisites/single/influence.ts index 4b0177d..cef9243 100644 --- a/src/entities/partial/prerequisites/single/influence.ts +++ b/src/entities/partial/prerequisites/single/influence.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/magicalTradition.ts b/src/entities/partial/prerequisites/single/magicalTradition.ts index 1c53554..3ccaf5d 100644 --- a/src/entities/partial/prerequisites/single/magicalTradition.ts +++ b/src/entities/partial/prerequisites/single/magicalTradition.ts @@ -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, diff --git a/src/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.ts b/src/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.ts index 14168f5..1dba1a4 100644 --- a/src/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.ts +++ b/src/entities/partial/prerequisites/single/noOtherAncestorBloodAdvantage.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/pact.ts b/src/entities/partial/prerequisites/single/pact.ts index acc2197..c61dd82 100644 --- a/src/entities/partial/prerequisites/single/pact.ts +++ b/src/entities/partial/prerequisites/single/pact.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/personalityTrait.ts b/src/entities/partial/prerequisites/single/personalityTrait.ts index 291d8e6..5fc7d4e 100644 --- a/src/entities/partial/prerequisites/single/personalityTrait.ts +++ b/src/entities/partial/prerequisites/single/personalityTrait.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/primaryAttribute.ts b/src/entities/partial/prerequisites/single/primaryAttribute.ts index f26a0fe..91f711d 100644 --- a/src/entities/partial/prerequisites/single/primaryAttribute.ts +++ b/src/entities/partial/prerequisites/single/primaryAttribute.ts @@ -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, } diff --git a/src/entities/partial/prerequisites/single/profession.ts b/src/entities/partial/prerequisites/single/profession.ts index 9be941c..b0e3a3e 100644 --- a/src/entities/partial/prerequisites/single/profession.ts +++ b/src/entities/partial/prerequisites/single/profession.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/publication.ts b/src/entities/partial/prerequisites/single/publication.ts index 820b37c..5e9a63e 100644 --- a/src/entities/partial/prerequisites/single/publication.ts +++ b/src/entities/partial/prerequisites/single/publication.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/race.ts b/src/entities/partial/prerequisites/single/race.ts index b4a4b00..b5892e0 100644 --- a/src/entities/partial/prerequisites/single/race.ts +++ b/src/entities/partial/prerequisites/single/race.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/rated.ts b/src/entities/partial/prerequisites/single/rated.ts index 9110db5..426ca87 100644 --- a/src/entities/partial/prerequisites/single/rated.ts +++ b/src/entities/partial/prerequisites/single/rated.ts @@ -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, } diff --git a/src/entities/partial/prerequisites/single/ratedMinimumNumber.ts b/src/entities/partial/prerequisites/single/ratedMinimumNumber.ts index 12e0c9a..44abd69 100644 --- a/src/entities/partial/prerequisites/single/ratedMinimumNumber.ts +++ b/src/entities/partial/prerequisites/single/ratedMinimumNumber.ts @@ -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, } diff --git a/src/entities/partial/prerequisites/single/ratedSum.ts b/src/entities/partial/prerequisites/single/ratedSum.ts index 8bc1c62..bcb98fb 100644 --- a/src/entities/partial/prerequisites/single/ratedSum.ts +++ b/src/entities/partial/prerequisites/single/ratedSum.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/rule.ts b/src/entities/partial/prerequisites/single/rule.ts index 8a42f2e..175954d 100644 --- a/src/entities/partial/prerequisites/single/rule.ts +++ b/src/entities/partial/prerequisites/single/rule.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/sex.ts b/src/entities/partial/prerequisites/single/sex.ts index e35859a..336e0b2 100644 --- a/src/entities/partial/prerequisites/single/sex.ts +++ b/src/entities/partial/prerequisites/single/sex.ts @@ -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) { diff --git a/src/entities/partial/prerequisites/single/sexualCharacteristic.ts b/src/entities/partial/prerequisites/single/sexualCharacteristic.ts index d7878db..2bbb9c6 100644 --- a/src/entities/partial/prerequisites/single/sexualCharacteristic.ts +++ b/src/entities/partial/prerequisites/single/sexualCharacteristic.ts @@ -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") diff --git a/src/entities/partial/prerequisites/single/socialStatus.ts b/src/entities/partial/prerequisites/single/socialStatus.ts index 97941fc..ec9dac4 100644 --- a/src/entities/partial/prerequisites/single/socialStatus.ts +++ b/src/entities/partial/prerequisites/single/socialStatus.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/state.ts b/src/entities/partial/prerequisites/single/state.ts index c4346e8..108ee25 100644 --- a/src/entities/partial/prerequisites/single/state.ts +++ b/src/entities/partial/prerequisites/single/state.ts @@ -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. diff --git a/src/entities/partial/prerequisites/single/text.ts b/src/entities/partial/prerequisites/single/text.ts index 11addea..8181239 100644 --- a/src/entities/partial/prerequisites/single/text.ts +++ b/src/entities/partial/prerequisites/single/text.ts @@ -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. diff --git a/src/entities/partial/rated/activatable/castingTime.ts b/src/entities/partial/rated/activatable/castingTime.ts index f1fa6ee..f70780b 100644 --- a/src/entities/partial/rated/activatable/castingTime.ts +++ b/src/entities/partial/rated/activatable/castingTime.ts @@ -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 => 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 => - 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 => - formatTimeSpanR(Case("Actions"), value.actions) +): StdReader => 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 = ( value: NonModifiable, ) => StdReader, value: CastingTime, -): StdReader< - string, - "t" | "rts" | "s" | "nms" | "ibi", - "SkillModificationLevel" -> => { +): StdReader => { switch (value.kind) { case "Modifiable": return renderModifiableCastingTime(value.Modifiable) @@ -112,11 +89,9 @@ const renderCastingTimeIncludingLovemaking = ( value: CastingTimeIncludingLovemaking, ) => 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 = ( export const renderFastCastingTime = ( value: FastCastingTime, ): StdReader => - 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 => - renderCastingTimeIncludingLovemaking( - renderSlowSkillNonModifiableCastingTime, - value, - ).with(env => ({ ...env, speed: Speed.Slow })) + renderCastingTimeIncludingLovemaking(renderSlowSkillNonModifiableCastingTime, value).with( + env => ({ ...env, speed: Speed.Slow }), + ) diff --git a/src/entities/partial/rated/activatable/cost.ts b/src/entities/partial/rated/activatable/cost.ts index 5290984..384e5e8 100644 --- a/src/entities/partial/rated/activatable/cost.ts +++ b/src/entities/partial/rated/activatable/cost.ts @@ -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 => 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 => 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 => { switch (value.kind) { case "Modifiable": return renderModifiableSustainedCost(value.Modifiable) diff --git a/src/entities/partial/rated/activatable/duration.ts b/src/entities/partial/rated/activatable/duration.ts index c55d69f..d2f11eb 100644 --- a/src/entities/partial/rated/activatable/duration.ts +++ b/src/entities/partial/rated/activatable/duration.ts @@ -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 => +const renderImmediateDuration = (value?: Immediate): StdReader => translateR("Immediate") .thenW( base => @@ -43,16 +38,10 @@ const renderImmediateDuration = ( ) .thenW(text => replaceTextIfNeeded(value?.translations, text)) -const renderPermanentDuration = ( - value: PermanentDuration, -): StdReader => - translateR("Permanent").thenW(text => - replaceTextIfNeeded(value.translations, text), - ) +const renderPermanentDuration = (value: PermanentDuration): StdReader => + translateR("Permanent").thenW(text => replaceTextIfNeeded(value.translations, text)) -const renderFixedDuration = ( - value: FixedDuration, -): StdReader => +const renderFixedDuration = (value: FixedDuration): StdReader => 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 => 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 => +export const renderMusicDuration = (duration: MusicDuration): StdReader => Reader.asks(({ translate }) => { const length = (() => { switch (duration.length.kind) { diff --git a/src/entities/partial/rated/activatable/effect.ts b/src/entities/partial/rated/activatable/effect.ts index 345e0e9..dcd39ac 100644 --- a/src/entities/partial/rated/activatable/effect.ts +++ b/src/entities/partial/rated/activatable/effect.ts @@ -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: diff --git a/src/entities/partial/rated/activatable/range.ts b/src/entities/partial/rated/activatable/range.ts index 5f0335b..7776fa5 100644 --- a/src/entities/partial/rated/activatable/range.ts +++ b/src/entities/partial/rated/activatable/range.ts @@ -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 => { 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 => { 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 => renderRangeValue(value.value, true) .then(text => replaceTextIfNeeded(value.translations, text)) .then(text => appendNoteIfNeeded(value.translations, text)) diff --git a/src/entities/partial/rated/activatable/speed.ts b/src/entities/partial/rated/activatable/speed.ts index 97158ab..9ebc337 100644 --- a/src/entities/partial/rated/activatable/speed.ts +++ b/src/entities/partial/rated/activatable/speed.ts @@ -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 = ( 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) } diff --git a/src/entities/partial/rated/activatable/targetCategory.ts b/src/entities/partial/rated/activatable/targetCategory.ts index 85f8c58..2877957 100644 --- a/src/entities/partial/rated/activatable/targetCategory.ts +++ b/src/entities/partial/rated/activatable/targetCategory.ts @@ -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 => 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(", ")) diff --git a/src/entities/partial/reader.ts b/src/entities/partial/reader.ts index f5c2276..5549e47 100644 --- a/src/entities/partial/reader.ts +++ b/src/entities/partial/reader.ts @@ -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 | undefined, + args?: Record, ): Reader<{ format: Format }, string> => Reader.asks(env => env.format(text, args)) /** diff --git a/src/entities/partial/units/length.ts b/src/entities/partial/units/length.ts index 92bcecc..df25f5a 100644 --- a/src/entities/partial/units/length.ts +++ b/src/entities/partial/units/length.ts @@ -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 => - 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 => - Reader.asks(env => - formatLength( - env.translate, - env.responsiveTextSize, - object.unit, - object.value, - ), - ) + Reader.asks(env => formatLength(env.translate, env.responsiveTextSize, object.unit, object.value)) diff --git a/src/entities/partial/units/timeSpan.ts b/src/entities/partial/units/timeSpan.ts index d92be0e..c3098db 100644 --- a/src/entities/partial/units/timeSpan.ts +++ b/src/entities/partial/units/timeSpan.ts @@ -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, string> => - formatTimeSpanR(object.unit, object.value) +}): Reader, string> => formatTimeSpanR(object.unit, object.value) diff --git a/src/entities/personalityTrait.ts b/src/entities/personalityTrait.ts index 94f1ab8..b26e6e0 100644 --- a/src/entities/personalityTrait.ts +++ b/src/entities/personalityTrait.ts @@ -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, + } +}) diff --git a/src/entities/poison.ts b/src/entities/poison.ts index 4ee83bf..ccabf1c 100644 --- a/src/entities/poison.ts +++ b/src/entities/poison.ts @@ -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"), diff --git a/src/entities/profession.ts b/src/entities/profession.ts index a3ad34a..c1cd5bf 100644 --- a/src/entities/profession.ts +++ b/src/entities/profession.ts @@ -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 = ( (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 = ( ...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 => 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 - option === undefined ? Reader.of("—") : renderSingleBlessings(option), + option.length === 0 ? Reader.of("—") : renderSingleBlessings(option), ).map(list => list.join(" / ")) } } @@ -947,7 +955,7 @@ const renderRatedVariantChanges = ( 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), ) diff --git a/src/entities/race.ts b/src/entities/race.ts index 2bc6e1c..5b13ac7 100644 --- a/src/entities/race.ts +++ b/src/entities/race.ts @@ -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 = ( 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 => Reader.asks( ({ translate, @@ -108,7 +102,11 @@ const renderVariantValues = ( 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 = ( 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), ], diff --git a/src/entities/sexPractice.ts b/src/entities/sexPractice.ts index e3cfaa3..dbb8c36 100644 --- a/src/entities/sexPractice.ts +++ b/src/entities/sexPractice.ts @@ -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, + } + }, +) diff --git a/src/entities/skill.ts b/src/entities/skill.ts index fa18ffc..4eceae0 100644 --- a/src/entities/skill.ts +++ b/src/entities/skill.ts @@ -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"), diff --git a/src/entities/spell.ts b/src/entities/spell.ts index d840a5f..c3f415c 100644 --- a/src/entities/spell.ts +++ b/src/entities/spell.ts @@ -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, 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) diff --git a/src/helpers/getTypes.ts b/src/helpers/getTypes.ts index 9024eb0..eae3127 100644 --- a/src/helpers/getTypes.ts +++ b/src/helpers/getTypes.ts @@ -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, -> = ( +export type GetInstanceById> = ( ...args: IdArgsVariant ) => Database.EntityMap[U] | undefined @@ -23,13 +21,9 @@ export type GetAllInstances = ( entity: U, ) => { id: string; content: Database.EntityMap[U] }[] -export type CountInstances = ( - entity: U, -) => number +export type CountInstances = (entity: U) => number -export type GetAllChildInstancesForParent< - T extends keyof Database.ChildEntityMap, -> = ( +export type GetAllChildInstancesForParent = ( entity: U, parentId: Database.ChildEntityMap[U][2], ) => { id: string; content: Database.ChildEntityMap[U][0] }[] diff --git a/src/helpers/locale.ts b/src/helpers/locale.ts index 9ccc080..c6cbbde 100644 --- a/src/helpers/locale.ts +++ b/src/helpers/locale.ts @@ -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 * 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 diff --git a/src/helpers/translate.ts b/src/helpers/translate.ts index 1e1385c..94c3d3a 100644 --- a/src/helpers/translate.ts +++ b/src/helpers/translate.ts @@ -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 = ( /** * Extracts the parameters for a given translation key, or never if the key does not have parameters. */ -export type TranslationParams = - Translations[K] extends string & { __params: infer Params } - ? Params - : undefined +export type TranslationParams = 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 = - 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. diff --git a/src/index.ts b/src/index.ts index 319c5da..6e00ba7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 = ( database: TSONDB, localeEnv: LocaleEnvironment, diff --git a/src/references/index.ts b/src/references/index.ts index 93b4fca..01f9b7f 100644 --- a/src/references/index.ts +++ b/src/references/index.ts @@ -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 }, )})` diff --git a/src/references/page.ts b/src/references/page.ts index 694d1cb..f3f66be 100644 --- a/src/references/page.ts +++ b/src/references/page.ts @@ -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 = (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) } diff --git a/src/references/pageRange.ts b/src/references/pageRange.ts index dc8c3e0..3575c01 100644 --- a/src/references/pageRange.ts +++ b/src/references/pageRange.ts @@ -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(", ") diff --git a/test/entities/partial/prerequisites/part.ts b/test/entities/partial/prerequisites/part.ts index 42e3ecd..f3a2ffd 100644 --- a/test/entities/partial/prerequisites/part.ts +++ b/test/entities/partial/prerequisites/part.ts @@ -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" diff --git a/test/helpers/locale.ts b/test/helpers/locale.ts index d3f3469..cb0eff3 100644 --- a/test/helpers/locale.ts +++ b/test/helpers/locale.ts @@ -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), diff --git a/test/helpers/translate.ts b/test/helpers/translate.ts index 09e6ecf..668d2c7 100644 --- a/test/helpers/translate.ts +++ b/test/helpers/translate.ts @@ -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. */ diff --git a/test/references/pageRange.ts b/test/references/pageRange.ts index 7733947..4b1b3a5 100644 --- a/test/references/pageRange.ts +++ b/test/references/pageRange.ts @@ -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"