From 938da657d606616831f90e731b61c39d56621f85 Mon Sep 17 00:00:00 2001 From: Lukas Obermann Date: Mon, 7 Oct 2024 23:13:10 +0200 Subject: [PATCH] refactor: outsource entity description generation to @optolith/entity-descriptions --- package-lock.json | 29 ++ package.json | 1 + .../entities/InlineLibraryBlessing.tsx | 28 +- .../entities/InlineLibraryCantrip.tsx | 34 +- .../entities/InlineLibraryCeremony.tsx | 51 +- .../InlineLibraryCloseCombatTechnique.tsx | 28 +- .../entities/InlineLibraryExperienceLevel.tsx | 17 +- .../entities/InlineLibraryFocusRule.tsx | 24 +- .../entities/InlineLibraryLiturgicalChant.tsx | 51 +- .../entities/InlineLibraryOptionalRule.tsx | 17 +- .../InlineLibraryRangedCombatTechnique.tsx | 28 +- .../entities/InlineLibraryRitual.tsx | 51 +- .../entities/InlineLibrarySkill.tsx | 36 +- .../entities/InlineLibrarySpell.tsx | 51 +- .../components/libraryEntry/LibraryEntry.tsx | 39 +- .../libraryEntry/LibraryEntryContents.tsx | 4 +- .../libraryEntry/LibraryEntryLabelList.tsx | 4 +- .../libraryEntry/LibraryEntryReferences.tsx | 132 +---- .../domain/adventurePoints/improvementCost.ts | 13 - src/shared/domain/experienceLevel.ts | 50 -- src/shared/domain/libraryEntry.ts | 80 --- .../activatableSkill/castingTime.ts | 167 ------- .../activatableSkill/checkResultBased.test.ts | 25 - .../activatableSkill/checkResultBased.ts | 42 -- .../libraryEntry/activatableSkill/cost.ts | 456 ------------------ .../libraryEntry/activatableSkill/duration.ts | 222 --------- .../libraryEntry/activatableSkill/effect.ts | 59 --- .../libraryEntry/activatableSkill/entity.ts | 11 - .../activatableSkill/isMaximum.ts | 21 - .../activatableSkill/modifiableParameter.ts | 8 - .../activatableSkill/nonModifiable.ts | 83 ---- .../libraryEntry/activatableSkill/parensIf.ts | 13 - .../libraryEntry/activatableSkill/range.ts | 221 --------- .../libraryEntry/activatableSkill/speed.ts | 33 -- .../activatableSkill/targetCategory.ts | 55 --- .../libraryEntry/activatableSkill/units.ts | 123 ----- .../domain/libraryEntry/responsiveText.ts | 89 ---- src/shared/domain/libraryEntry/unknown.ts | 4 - src/shared/domain/rated/activatableSkill.ts | 128 ----- src/shared/domain/rated/combatTechnique.ts | 74 --- src/shared/domain/rated/liturgicalChant.ts | 370 +------------- src/shared/domain/rated/skill.ts | 130 ----- src/shared/domain/rated/skillCheck.ts | 127 +---- src/shared/domain/rated/spell.ts | 444 +---------------- src/shared/domain/rules/focusRule.ts | 25 - src/shared/domain/rules/optionalRule.ts | 23 - src/shared/domain/sources/pages.test.ts | 82 ---- src/shared/domain/sources/pages.ts | 114 ----- 48 files changed, 372 insertions(+), 3545 deletions(-) delete mode 100644 src/shared/domain/libraryEntry.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/castingTime.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/checkResultBased.test.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/checkResultBased.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/cost.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/duration.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/effect.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/entity.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/isMaximum.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/modifiableParameter.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/nonModifiable.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/parensIf.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/range.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/speed.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/targetCategory.ts delete mode 100644 src/shared/domain/libraryEntry/activatableSkill/units.ts delete mode 100644 src/shared/domain/libraryEntry/responsiveText.ts delete mode 100644 src/shared/domain/libraryEntry/unknown.ts delete mode 100644 src/shared/domain/rated/activatableSkill.ts delete mode 100644 src/shared/domain/sources/pages.test.ts delete mode 100644 src/shared/domain/sources/pages.ts diff --git a/package-lock.json b/package-lock.json index 19dc1f498..e2fb17b6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "2.0.0-alpha.1", "license": "MPL-2.0", "dependencies": { + "@optolith/entity-descriptions": "^0.2.1", "@reduxjs/toolkit": "^2.2.7", "adm-zip": "^0.5.16", "debug": "^4.3.7", @@ -1658,6 +1659,21 @@ "node": ">=10" } }, + "node_modules/@optolith/entity-descriptions": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@optolith/entity-descriptions/-/entity-descriptions-0.2.1.tgz", + "integrity": "sha512-mtUCQXd2OfXUo0NT1mIFdcwioq/2d9l9S9alC918x8rzamWOB8RFAbVJqknVbZCKmLUNBbSOpOs34QlXNaHsng==", + "license": "MPL-2.0", + "dependencies": { + "@optolith/helpers": "^0.2.0" + } + }, + "node_modules/@optolith/helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@optolith/helpers/-/helpers-0.2.1.tgz", + "integrity": "sha512-i2mg5ro2XaY0sFT3vwW+BBuMYVuJpHRMt27X9/sBfXmgY6L16aKG3UXEcP5jjX+/gjH2AM7Uiz6xgtV31/rFLw==", + "license": "MPL-2.0" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -17974,6 +17990,19 @@ } } }, + "@optolith/entity-descriptions": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@optolith/entity-descriptions/-/entity-descriptions-0.2.1.tgz", + "integrity": "sha512-mtUCQXd2OfXUo0NT1mIFdcwioq/2d9l9S9alC918x8rzamWOB8RFAbVJqknVbZCKmLUNBbSOpOs34QlXNaHsng==", + "requires": { + "@optolith/helpers": "^0.2.0" + } + }, + "@optolith/helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@optolith/helpers/-/helpers-0.2.1.tgz", + "integrity": "sha512-i2mg5ro2XaY0sFT3vwW+BBuMYVuJpHRMt27X9/sBfXmgY6L16aKG3UXEcP5jjX+/gjH2AM7Uiz6xgtV31/rFLw==" + }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", diff --git a/package.json b/package.json index 25a9100f7..2c82b1617 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "MPL-2.0", "main": ".webpack/main.cjs", "dependencies": { + "@optolith/entity-descriptions": "^0.2.1", "@reduxjs/toolkit": "^2.2.7", "adm-zip": "^0.5.16", "debug": "^4.3.7", diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryBlessing.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryBlessing.tsx index 3765975ad..182a0111b 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryBlessing.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryBlessing.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getBlessingLibraryEntry } from "../../../shared/domain/rated/liturgicalChant.ts" +import { getBlessingEntityDescription } from "@optolith/entity-descriptions/entities/liturgicalChant" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -15,11 +18,18 @@ export const InlineLibraryBlessing: FC = ({ id }) => { const getTargetCategoryById = useAppSelector(SelectGetById.Static.TargetCategory) const entry = useAppSelector(SelectGetById.Static.Blessing)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getBlessingEntityDescription( + { + ...defaultDatabaseAccessors, + getTargetCategoryById, + }, + locale, + entry, + ), + [entry, getTargetCategoryById], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryCantrip.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryCantrip.tsx index 0e1ad7bf9..bddbba848 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryCantrip.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryCantrip.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getCantripLibraryEntry } from "../../../shared/domain/rated/spell.ts" +import { getCantripEntityDescription } from "@optolith/entity-descriptions/entities/spell" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -18,14 +21,21 @@ export const InlineLibraryCantrip: FC = ({ id }) => { const getMagicalTraditionById = useAppSelector(SelectGetById.Static.MagicalTradition) const entry = useAppSelector(SelectGetById.Static.Cantrip)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getCantripEntityDescription( + { + ...defaultDatabaseAccessors, + getTargetCategoryById, + getPropertyById, + getCurriculumById, + getMagicalTraditionById, + }, + locale, + entry, + ), + [entry, getCurriculumById, getMagicalTraditionById, getPropertyById, getTargetCategoryById], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryCeremony.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryCeremony.tsx index 082af23a8..1d83a527a 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryCeremony.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryCeremony.tsx @@ -1,6 +1,10 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getCeremonyLibraryEntry } from "../../../shared/domain/rated/liturgicalChant.ts" +import { getCeremonyEntityDescription } from "@optolith/entity-descriptions/entities/liturgicalChant" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" +import { DerivedCharacteristicIdentifier } from "../../../shared/domain/identifier.ts" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -14,22 +18,41 @@ type Props = { export const InlineLibraryCeremony: FC = ({ id }) => { const getAttributeById = useAppSelector(SelectGetById.Static.Attribute) const getDerivedCharacteristicById = useAppSelector(SelectGetById.Static.DerivedCharacteristic) + const spirit = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Spirit) + const toughness = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Toughness) const getSkillModificationLevelById = useAppSelector(SelectGetById.Static.SkillModificationLevel) const getTargetCategoryById = useAppSelector(SelectGetById.Static.TargetCategory) const getBlessedTraditionById = useAppSelector(SelectGetById.Static.BlessedTradition) const getAspectById = useAppSelector(SelectGetById.Static.Aspect) const entry = useAppSelector(SelectGetById.Static.Ceremony)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getCeremonyEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + getSpirit: () => spirit, + getToughness: () => toughness, + getSkillModificationLevelById, + getTargetCategoryById, + getBlessedTraditionById, + getAspectById, + }, + locale, + entry, + ), + [ + entry, + getAspectById, + getAttributeById, + getBlessedTraditionById, + getSkillModificationLevelById, + getTargetCategoryById, + spirit, + toughness, + ], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryCloseCombatTechnique.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryCloseCombatTechnique.tsx index e97b6be41..4a988a077 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryCloseCombatTechnique.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryCloseCombatTechnique.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getCloseCombatTechniqueLibraryEntry } from "../../../shared/domain/rated/combatTechnique.ts" +import { getCloseCombatTechniqueEntityDescription } from "@optolith/entity-descriptions/entities/combatTechnique" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -15,11 +18,18 @@ export const InlineLibraryCloseCombatTechnique: FC = ({ id }) => { const getAttributeById = useAppSelector(SelectGetById.Static.Attribute) const entry = useAppSelector(SelectGetById.Static.CloseCombatTechnique)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getCloseCombatTechniqueEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + }, + locale, + entry, + ), + [entry, getAttributeById], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryExperienceLevel.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryExperienceLevel.tsx index fe208dc4f..ca977cc4f 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryExperienceLevel.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryExperienceLevel.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getExperienceLevelLibraryEntry } from "../../../shared/domain/experienceLevel.ts" +import { getExperienceLevelEntityDescription } from "@optolith/entity-descriptions/entities/experienceLevel" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { selectStaticExperienceLevels } from "../../slices/databaseSlice.ts" @@ -14,5 +17,11 @@ type Props = { export const InlineLibraryExperienceLevel: FC = ({ id }) => { const entry = useAppSelector(selectStaticExperienceLevels)[id] - return + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getExperienceLevelEntityDescription(defaultDatabaseAccessors, locale, entry), + [entry], + ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryFocusRule.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryFocusRule.tsx index 48190e555..8e028aa2b 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryFocusRule.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryFocusRule.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getFocusRuleLibraryEntry } from "../../../shared/domain/rules/focusRule.ts" +import { getFocusRuleEntityDescription } from "@optolith/entity-descriptions/entities/focusRule" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" import { selectStaticFocusRules } from "../../slices/databaseSlice.ts" @@ -16,5 +19,18 @@ export const InlineLibraryFocusRule: FC = ({ id }) => { const entry = useAppSelector(selectStaticFocusRules)[id] const getSubjectById = useAppSelector(SelectGetById.Static.Subject) - return + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getFocusRuleEntityDescription( + { + ...defaultDatabaseAccessors, + getSubjectById, + }, + locale, + entry, + ), + [entry, getSubjectById], + ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryLiturgicalChant.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryLiturgicalChant.tsx index 84e654491..c3fd2b51f 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryLiturgicalChant.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryLiturgicalChant.tsx @@ -1,6 +1,10 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getLiturgicalChantLibraryEntry } from "../../../shared/domain/rated/liturgicalChant.ts" +import { getLiturgicalChantEntityDescription } from "@optolith/entity-descriptions/entities/liturgicalChant" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" +import { DerivedCharacteristicIdentifier } from "../../../shared/domain/identifier.ts" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -14,22 +18,41 @@ type Props = { export const InlineLibraryLiturgicalChant: FC = ({ id }) => { const getAttributeById = useAppSelector(SelectGetById.Static.Attribute) const getDerivedCharacteristicById = useAppSelector(SelectGetById.Static.DerivedCharacteristic) + const spirit = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Spirit) + const toughness = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Toughness) const getSkillModificationLevelById = useAppSelector(SelectGetById.Static.SkillModificationLevel) const getTargetCategoryById = useAppSelector(SelectGetById.Static.TargetCategory) const getBlessedTraditionById = useAppSelector(SelectGetById.Static.BlessedTradition) const getAspectById = useAppSelector(SelectGetById.Static.Aspect) const entry = useAppSelector(SelectGetById.Static.LiturgicalChant)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getLiturgicalChantEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + getSpirit: () => spirit, + getToughness: () => toughness, + getSkillModificationLevelById, + getTargetCategoryById, + getBlessedTraditionById, + getAspectById, + }, + locale, + entry, + ), + [ + entry, + getAspectById, + getAttributeById, + getBlessedTraditionById, + getSkillModificationLevelById, + getTargetCategoryById, + spirit, + toughness, + ], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryOptionalRule.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryOptionalRule.tsx index aac8a15b6..ef5d94122 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryOptionalRule.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryOptionalRule.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getOptionalRuleLibraryEntry } from "../../../shared/domain/rules/optionalRule.ts" +import { getOptionalRuleEntityDescription } from "@optolith/entity-descriptions/entities/optionalRule" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { selectStaticOptionalRules } from "../../slices/databaseSlice.ts" @@ -14,5 +17,11 @@ type Props = { export const InlineLibraryOptionalRule: FC = ({ id }) => { const entry = useAppSelector(selectStaticOptionalRules)[id] - return + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getOptionalRuleEntityDescription(defaultDatabaseAccessors, locale, entry), + [entry], + ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryRangedCombatTechnique.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryRangedCombatTechnique.tsx index 5db69a37c..29393e9ad 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryRangedCombatTechnique.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryRangedCombatTechnique.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getRangedCombatTechniqueLibraryEntry } from "../../../shared/domain/rated/combatTechnique.ts" +import { getRangedCombatTechniqueEntityDescription } from "@optolith/entity-descriptions/entities/combatTechnique" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -15,11 +18,18 @@ export const InlineLibraryRangedCombatTechnique: FC = ({ id }) => { const getAttributeById = useAppSelector(SelectGetById.Static.Attribute) const entry = useAppSelector(SelectGetById.Static.RangedCombatTechnique)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getRangedCombatTechniqueEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + }, + locale, + entry, + ), + [entry, getAttributeById], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibraryRitual.tsx b/src/main_window/inlineLibrary/entities/InlineLibraryRitual.tsx index bc6f61b50..12b3e6893 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibraryRitual.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibraryRitual.tsx @@ -1,6 +1,10 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getRitualLibraryEntry } from "../../../shared/domain/rated/spell.ts" +import { getRitualEntityDescription } from "@optolith/entity-descriptions/entities/spell" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" +import { DerivedCharacteristicIdentifier } from "../../../shared/domain/identifier.ts" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -14,22 +18,41 @@ type Props = { export const InlineLibraryRitual: FC = ({ id }) => { const getAttributeById = useAppSelector(SelectGetById.Static.Attribute) const getDerivedCharacteristicById = useAppSelector(SelectGetById.Static.DerivedCharacteristic) + const spirit = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Spirit) + const toughness = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Toughness) const getSkillModificationLevelById = useAppSelector(SelectGetById.Static.SkillModificationLevel) const getTargetCategoryById = useAppSelector(SelectGetById.Static.TargetCategory) const getPropertyById = useAppSelector(SelectGetById.Static.Property) const getMagicalTraditionById = useAppSelector(SelectGetById.Static.MagicalTradition) const entry = useAppSelector(SelectGetById.Static.Ritual)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getRitualEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + getSpirit: () => spirit, + getToughness: () => toughness, + getSkillModificationLevelById, + getTargetCategoryById, + getPropertyById, + getMagicalTraditionById, + }, + locale, + entry, + ), + [ + entry, + getAttributeById, + getMagicalTraditionById, + getPropertyById, + getSkillModificationLevelById, + getTargetCategoryById, + spirit, + toughness, + ], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibrarySkill.tsx b/src/main_window/inlineLibrary/entities/InlineLibrarySkill.tsx index 6d27d3ba4..becc5b8dd 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibrarySkill.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibrarySkill.tsx @@ -1,6 +1,9 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getSkillLibraryEntry } from "../../../shared/domain/rated/skill.ts" +import { getSkillEntityDescription } from "@optolith/entity-descriptions/entities/skill" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" import { useAppSelector } from "../../hooks/redux.ts" import { SelectAll, SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" import { selectNewApplicationsAndUsesCache } from "../../slices/databaseSlice.ts" @@ -20,15 +23,22 @@ export const InlineLibrarySkill: FC = ({ id }) => { const entry = useAppSelector(SelectGetById.Static.Skill)(id) const cache = useAppSelector(selectNewApplicationsAndUsesCache) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getSkillEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + blessedTraditions, + diseases, + regions, + cache, + }, + locale, + entry, + ), + [blessedTraditions, cache, diseases, entry, getAttributeById, regions], ) + + return } diff --git a/src/main_window/inlineLibrary/entities/InlineLibrarySpell.tsx b/src/main_window/inlineLibrary/entities/InlineLibrarySpell.tsx index baff13a32..9290c3ac3 100644 --- a/src/main_window/inlineLibrary/entities/InlineLibrarySpell.tsx +++ b/src/main_window/inlineLibrary/entities/InlineLibrarySpell.tsx @@ -1,6 +1,10 @@ -import { FC } from "react" -import { LibraryEntry } from "../../../shared/components/libraryEntry/LibraryEntry.tsx" -import { getSpellLibraryEntry } from "../../../shared/domain/rated/spell.ts" +import { getSpellEntityDescription } from "@optolith/entity-descriptions/entities/spell" +import { FC, useCallback } from "react" +import { + LibraryEntry, + PartialEntityDescriptionCreator, +} from "../../../shared/components/libraryEntry/LibraryEntry.tsx" +import { DerivedCharacteristicIdentifier } from "../../../shared/domain/identifier.ts" import { useAppSelector } from "../../hooks/redux.ts" import { SelectGetById } from "../../selectors/basicCapabilitySelectors.ts" @@ -14,22 +18,41 @@ type Props = { export const InlineLibrarySpell: FC = ({ id }) => { const getAttributeById = useAppSelector(SelectGetById.Static.Attribute) const getDerivedCharacteristicById = useAppSelector(SelectGetById.Static.DerivedCharacteristic) + const spirit = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Spirit) + const toughness = getDerivedCharacteristicById(DerivedCharacteristicIdentifier.Toughness) const getSkillModificationLevelById = useAppSelector(SelectGetById.Static.SkillModificationLevel) const getTargetCategoryById = useAppSelector(SelectGetById.Static.TargetCategory) const getPropertyById = useAppSelector(SelectGetById.Static.Property) const getMagicalTraditionById = useAppSelector(SelectGetById.Static.MagicalTradition) const entry = useAppSelector(SelectGetById.Static.Spell)(id) - return ( - + const createEntityDescription: PartialEntityDescriptionCreator = useCallback( + (defaultDatabaseAccessors, locale) => + getSpellEntityDescription( + { + ...defaultDatabaseAccessors, + getAttributeById, + getSpirit: () => spirit, + getToughness: () => toughness, + getSkillModificationLevelById, + getTargetCategoryById, + getPropertyById, + getMagicalTraditionById, + }, + locale, + entry, + ), + [ + entry, + getAttributeById, + getMagicalTraditionById, + getPropertyById, + getSkillModificationLevelById, + getTargetCategoryById, + spirit, + toughness, + ], ) + + return } diff --git a/src/shared/components/libraryEntry/LibraryEntry.tsx b/src/shared/components/libraryEntry/LibraryEntry.tsx index 45ef3412e..5139fc492 100644 --- a/src/shared/components/libraryEntry/LibraryEntry.tsx +++ b/src/shared/components/libraryEntry/LibraryEntry.tsx @@ -1,32 +1,57 @@ +import { EntityDescription } from "@optolith/entity-descriptions" import { FC } from "react" +import { useAppSelector } from "../../../main_window/hooks/redux.ts" import { InlineLibraryPlaceholder } from "../../../main_window/inlineLibrary/InlineLibraryPlaceholder.tsx" -import { LibraryEntryConfiguredCreator } from "../../domain/libraryEntry.ts" +import { SelectGetById } from "../../../main_window/selectors/basicCapabilitySelectors.ts" +import { selectLocale } from "../../../main_window/slices/settingsSlice.ts" +import { GetById } from "../../domain/getTypes.ts" import { useLocaleCompare } from "../../hooks/localeCompare.ts" import { useTranslate } from "../../hooks/translate.ts" import { useTranslateMap } from "../../hooks/translateMap.ts" +import { Compare } from "../../utils/compare.ts" +import { Translate, TranslateMap } from "../../utils/translate.ts" import "./LibraryEntry.scss" import { LibraryEntryContents } from "./LibraryEntryContents.tsx" import { LibraryEntryReferences } from "./LibraryEntryReferences.tsx" +/** + * A function that provides common parameters to every entity desciption + * creator. + */ +export type PartialEntityDescriptionCreator = ( + defaultDatabaseAccessors: { getPublicationById: GetById.Static.Publication }, + locale: { + id: string + translate: Translate + translateMap: TranslateMap + compare: Compare + }, +) => EntityDescription | undefined + type Props = { - createLibraryEntry: LibraryEntryConfiguredCreator + createEntityDescription: PartialEntityDescriptionCreator } /** * Displays all information about a library entry. */ -export const LibraryEntry: FC = ({ createLibraryEntry }) => { +export const LibraryEntry: FC = ({ createEntityDescription }) => { const translate = useTranslate() const translateMap = useTranslateMap() const localeCompare = useLocaleCompare() + const getPublicationById = useAppSelector(SelectGetById.Static.Publication) + const localeId = useAppSelector(selectLocale) - const libraryEntry = createLibraryEntry({ translate, translateMap, localeCompare }) + const libraryEntry = createEntityDescription( + { getPublicationById }, + { id: localeId ?? "en-US", translate, translateMap, compare: localeCompare }, + ) if (libraryEntry === undefined) { return } - const { title, subtitle, className, content, src } = libraryEntry + const { title, subtitle, className, body, references } = libraryEntry return (
@@ -34,8 +59,8 @@ export const LibraryEntry: FC = ({ createLibraryEntry }) => {

{title}

{subtitle === undefined ? null :

{subtitle}

}
- - {src === undefined ? null : } + + {references === undefined ? null : } ) } diff --git a/src/shared/components/libraryEntry/LibraryEntryContents.tsx b/src/shared/components/libraryEntry/LibraryEntryContents.tsx index f47ef2b99..b5ef182fb 100644 --- a/src/shared/components/libraryEntry/LibraryEntryContents.tsx +++ b/src/shared/components/libraryEntry/LibraryEntryContents.tsx @@ -1,12 +1,12 @@ +import { EntityDescriptionSection } from "@optolith/entity-descriptions" import { FC, Fragment } from "react" -import { LibraryEntryContent } from "../../domain/libraryEntry.ts" import { groupBy } from "../../utils/array.ts" import { classList } from "../../utils/classList.ts" import { Markdown } from "../markdown/Markdown.tsx" import { LibraryEntryLabelList } from "./LibraryEntryLabelList.tsx" type Props = { - contents: LibraryEntryContent[] + contents: EntityDescriptionSection[] } /** diff --git a/src/shared/components/libraryEntry/LibraryEntryLabelList.tsx b/src/shared/components/libraryEntry/LibraryEntryLabelList.tsx index 764d065ab..7a2d84d80 100644 --- a/src/shared/components/libraryEntry/LibraryEntryLabelList.tsx +++ b/src/shared/components/libraryEntry/LibraryEntryLabelList.tsx @@ -1,10 +1,10 @@ +import { EntityDescriptionSection } from "@optolith/entity-descriptions" import { FC } from "react" -import { LibraryEntryContent } from "../../domain/libraryEntry.ts" import { Markdown } from "../markdown/Markdown.tsx" import "./LibraryEntryLabelList.scss" type Props = { - contents: LibraryEntryContent[] + contents: EntityDescriptionSection[] } /** diff --git a/src/shared/components/libraryEntry/LibraryEntryReferences.tsx b/src/shared/components/libraryEntry/LibraryEntryReferences.tsx index 85b99bbef..5a421d8b9 100644 --- a/src/shared/components/libraryEntry/LibraryEntryReferences.tsx +++ b/src/shared/components/libraryEntry/LibraryEntryReferences.tsx @@ -1,136 +1,12 @@ -import { - Occurrence, - Page, - PublicationRefs, - SimpleOccurrence, - SimpleOccurrences, - VersionedOccurrence, -} from "optolith-database-schema/types/source/_PublicationRef" import { FC } from "react" -import { useAppSelector } from "../../../main_window/hooks/redux.ts" -import { selectStaticPublications } from "../../../main_window/slices/databaseSlice.ts" -import { - PageRange, - fromRawPageRange, - normalizePageRanges, - numberRangeToPageRange, -} from "../../domain/sources/pages.ts" -import { useTranslate } from "../../hooks/translate.ts" -import { useTranslateMap } from "../../hooks/translateMap.ts" -import { isNotNullish } from "../../utils/nullable.ts" -import { Translate } from "../../utils/translate.ts" -import { assertExhaustive } from "../../utils/typeSafety.ts" type Props = { - sources: PublicationRefs + sources: string } -const isSimpleOccurrence = (occurrence: Occurrence): occurrence is SimpleOccurrence => - Object.hasOwn(occurrence, "first_page") - -const isSimpleOccurrences = (occurrence: Occurrence): occurrence is SimpleOccurrences => - Array.isArray(occurrence) - -const isVersionedOccurrence = (occurrence: Occurrence): occurrence is VersionedOccurrence => - Object.hasOwn(occurrence, "initial") - -const printPage = (translate: Translate, page: Page) => { - switch (page.tag) { - case "InsideCoverFront": - return translate("Front Cover Inside") - case "InsideCoverBack": - return translate("Back Cover Inside") - case "Numbered": - return page.numbered.toString() - default: - return assertExhaustive(page) - } -} - -const printPageRange = (translate: Translate, pageRange: PageRange) => - pageRange.lastPage === undefined - ? printPage(translate, pageRange.firstPage) - : `${printPage(translate, pageRange.firstPage)}–${printPage(translate, pageRange.lastPage)}` - -const printPageRanges = (translate: Translate, pageRanges: PageRange[]) => - pageRanges.map(pageRange => printPageRange(translate, pageRange)).join(", ") - /** * Displays the sources of an entry. */ -export const LibraryEntryReferences: FC = ({ sources }) => { - const translate = useTranslate() - const translateMap = useTranslateMap() - const publications = useAppSelector(selectStaticPublications) - - return ( -

- {sources - .map(source => { - const publication = publications[source.id.publication] - const publicationTranslations = translateMap(publication?.translations) - const occurrences = translateMap(source.occurrences) - - if ( - publication === undefined || - publicationTranslations === undefined || - occurrences === undefined - ) { - return undefined - } - - if (isSimpleOccurrence(occurrences)) { - return `${publicationTranslations.name} ${printPageRange( - translate, - numberRangeToPageRange(occurrences), - )}` - } - - if (isSimpleOccurrences(occurrences)) { - const ranges = normalizePageRanges(occurrences.map(numberRangeToPageRange)) - return `${publicationTranslations.name} ${printPageRanges(translate, ranges)}` - } - - if (isVersionedOccurrence(occurrences)) { - const initialPageRanges = normalizePageRanges( - occurrences.initial.pages.map(fromRawPageRange), - ) - - const initial = - occurrences.initial.printing === undefined - ? printPageRanges(translate, initialPageRanges) - : `${printPageRanges(translate, initialPageRanges)} (${translate( - "since the {0}. printing", - occurrences.initial.printing, - )})` - - const revisions = - occurrences.revisions?.map(rev => { - switch (rev.tag) { - case "Since": { - const pageRanges = normalizePageRanges(rev.since.pages.map(fromRawPageRange)) - return `${printPageRanges(translate, pageRanges)} (${translate( - "since the {0}. printing", - rev.since.printing, - )})` - } - case "Deprecated": { - return translate("removed in {0}. printing", rev.deprecated.printing) - } - default: - return assertExhaustive(rev) - } - }) ?? [] - - const allPageRanges = [initial, ...revisions].join("; ") - - return `${publicationTranslations.name} ${allPageRanges}` - } - - return assertExhaustive(occurrences) - }) - .filter(isNotNullish) - .join("; ")} -

- ) -} +export const LibraryEntryReferences: FC = ({ sources }) => ( +

{sources}

+) diff --git a/src/shared/domain/adventurePoints/improvementCost.ts b/src/shared/domain/adventurePoints/improvementCost.ts index c4112df5b..e227ac5f1 100644 --- a/src/shared/domain/adventurePoints/improvementCost.ts +++ b/src/shared/domain/adventurePoints/improvementCost.ts @@ -1,9 +1,7 @@ import { ImprovementCost as RawImprovementCost } from "optolith-database-schema/types/_ImprovementCost" import { rangeSafe, sum } from "../../utils/array.ts" import { Nullish } from "../../utils/nullable.ts" -import { Translate } from "../../utils/translate.ts" import { assertExhaustive } from "../../utils/typeSafety.ts" -import { LibraryEntryContent } from "../libraryEntry.ts" /** * The cost category of an improvement. @@ -184,14 +182,3 @@ export const fromRaw = ( return assertExhaustive(ic) } } - -/** - * Returns the improvement cost as an inline library property. - */ -export const createImprovementCost = ( - translate: Translate, - improvementCost: RawImprovementCost, -): LibraryEntryContent => ({ - label: translate("Improvement Cost"), - value: toString(fromRaw(improvementCost)), -}) diff --git a/src/shared/domain/experienceLevel.ts b/src/shared/domain/experienceLevel.ts index 3ac83aed6..7e44f71e8 100644 --- a/src/shared/domain/experienceLevel.ts +++ b/src/shared/domain/experienceLevel.ts @@ -1,5 +1,4 @@ import { ExperienceLevel } from "optolith-database-schema/types/ExperienceLevel" -import { createLibraryEntryCreator } from "./libraryEntry.ts" /** * Returns the current experience level based on the total adventure points. @@ -15,52 +14,3 @@ export const getCurrentExperienceLevel = ( experienceLevel.adventure_points <= totalAdventurePoints ? experienceLevel : acc, experienceLevels[0], ) - -/** - * Get a JSON representation of the rules text for an experience level. - */ -export const getExperienceLevelLibraryEntry = createLibraryEntryCreator( - entry => - ({ translate, translateMap }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - return { - title: translation.name, - className: "experience-level", - content: [ - { - label: translate("Adventure Points"), - value: entry.adventure_points, - }, - { - label: translate("Maximum Attribute Value"), - value: entry.max_attribute_value, - }, - { - label: translate("Maximum Skill Value"), - value: entry.max_skill_rating, - }, - { - label: translate("Maximum Combat Technique"), - value: entry.max_combat_technique_rating, - }, - { - label: translate("Maximum Attribute Total"), - value: entry.max_attribute_total, - }, - { - label: translate("Number of Spells/Liturgical Chants"), - value: entry.max_number_of_spells_liturgical_chants, - }, - { - label: translate("Number from other Traditions"), - value: entry.max_number_of_unfamiliar_spells, - }, - ], - } - }, -) diff --git a/src/shared/domain/libraryEntry.ts b/src/shared/domain/libraryEntry.ts deleted file mode 100644 index 62f2d54a4..000000000 --- a/src/shared/domain/libraryEntry.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { PublicationRefs } from "optolith-database-schema/types/source/_PublicationRef" -import { filterNonNullable } from "../utils/array.ts" -import { Compare } from "../utils/compare.ts" -import { Translate, TranslateMap } from "../utils/translate.ts" - -/** - * Creates a function that creates the JSON representation of the rules text for - * a library entry. - */ -export const createLibraryEntryCreator = - ( - fn: LibraryEntryCreator, - ): LibraryEntryCreator => - (entry, ...args) => { - if (entry === undefined) { - return () => undefined - } - - return params => { - const rawEntry = fn(entry, ...args)(params) - - if (rawEntry === undefined) { - return undefined - } - - return { ...rawEntry, content: filterNonNullable(rawEntry.content) } - } - } - -/** - * A function that creates the JSON representation of the rules text for a - * library entry if given further params to the returned function. - */ -export type LibraryEntryCreator = ( - entry: T, - ...args: A extends undefined ? [] : [A] -) => LibraryEntryConfiguredCreator - -/** - * A function that is already configures for a specific entity and returns the - * JSON representation of the rules text for a ibrary entry. - */ -export type LibraryEntryConfiguredCreator = (params: { - translate: Translate - translateMap: TranslateMap - localeCompare: Compare -}) => R | undefined - -/** - * A JSON representation of the rules text for a library entry. - */ -export type LibraryEntry = { - title: string - subtitle?: string - className: string - content: LibraryEntryContent[] - src?: PublicationRefs -} - -/** - * A JSON representation of the rules text for a library entry that has not been - * cleaned up. - */ -export type RawLibraryEntry = { - title: string - subtitle?: string - className: string - content: (LibraryEntryContent | undefined)[] - src?: PublicationRefs -} - -/** - * A slice of the content of a library entry text. - */ -export type LibraryEntryContent = { - label?: string - value: string | number - noIndent?: boolean - className?: string -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/castingTime.ts b/src/shared/domain/libraryEntry/activatableSkill/castingTime.ts deleted file mode 100644 index 38f75d6f8..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/castingTime.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - CastingTime, - CastingTimeDuringLovemaking, - FastCastingTime, - FastSkillNonModifiableCastingTime, - ModifiableCastingTime, - SlowCastingTime, - SlowSkillNonModifiableCastingTime, -} from "optolith-database-schema/types/_ActivatableSkillCastingTime" -import { isNotNullish, mapNullable } from "../../../utils/nullable.ts" -import { Translate } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { GetById } from "../../getTypes.ts" -import { ResponsiveTextSize } from "../responsiveText.ts" -import { MISSING_VALUE } from "../unknown.ts" -import { Entity } from "./entity.ts" -import { ModifiableParameter } from "./modifiableParameter.ts" -import { getTextForNonModifiableSuffix } from "./nonModifiable.ts" -import { Speed } from "./speed.ts" -import { formatTimeSpan } from "./units.ts" - -const getTextForModifiableCastingTime = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - }, - value: ModifiableCastingTime, - env: { - speed: Speed - responsiveText: ResponsiveTextSize - }, -): string => - mapNullable( - deps.getSkillModificationLevelById(value.initial_modification_level), - ({ fast: { casting_time: fastTime }, slow: { casting_time: slowTime } }) => { - switch (env.speed) { - case Speed.Fast: - return formatTimeSpan(deps.translate, env.responsiveText, "Actions", fastTime) - case Speed.Slow: - return formatTimeSpan(deps.translate, env.responsiveText, slowTime.unit, slowTime.value) - default: - return assertExhaustive(env.speed) - } - }, - ) ?? MISSING_VALUE - -const getTextForFastSkillNonModifiableCastingTime = ( - deps: { - translate: Translate - }, - value: FastSkillNonModifiableCastingTime, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): string => - formatTimeSpan(deps.translate, env.responsiveText, "Actions", value.actions) + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.CastingTime, - env.responsiveText, - ) - -const getTextForSlowSkillNonModifiableCastingTime = ( - deps: { - translate: Translate - }, - value: SlowSkillNonModifiableCastingTime, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): string => - formatTimeSpan(deps.translate, env.responsiveText, value.unit, value.value) + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.CastingTime, - env.responsiveText, - ) - -const getTextForCastingTime = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - }, - value: CastingTime, - env: { - speed: Speed - responsiveText: ResponsiveTextSize - }, - getTextForNonModifiableCastingTime: (value: NonModifiable) => string, -): string => { - switch (value.tag) { - case "Modifiable": - return getTextForModifiableCastingTime(deps, value.modifiable, env) - case "NonModifiable": - return getTextForNonModifiableCastingTime(value.non_modifiable) - default: - return assertExhaustive(value) - } -} - -const getTextForCastingTimeDuringLovemaking = ( - deps: { - translate: Translate - }, - value: CastingTimeDuringLovemaking, - env: { - responsiveText: ResponsiveTextSize - }, -): string => formatTimeSpan(deps.translate, env.responsiveText, value.unit, value.value) - -/** - * Get the text for the casting time of a fast activatable skill. - */ -export const getTextForFastCastingTime = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - }, - value: FastCastingTime, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): string => - [ - mapNullable(value.default, def => - getTextForCastingTime(deps, def, { ...env, speed: Speed.Fast }, nonModifiableValue => - getTextForFastSkillNonModifiableCastingTime(deps, nonModifiableValue, env), - ), - ), - mapNullable(value.during_lovemaking, duringLovemaking => - getTextForCastingTimeDuringLovemaking(deps, duringLovemaking, env), - ), - ] - .filter(isNotNullish) - .join(" / ") - -/** - * Get the text for the casting time of a slow activatable skill. - */ -export const getTextForSlowCastingTime = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - }, - value: SlowCastingTime, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): string => - [ - mapNullable(value.default, def => - getTextForCastingTime(deps, def, { ...env, speed: Speed.Slow }, nonModifiableValue => - getTextForSlowSkillNonModifiableCastingTime(deps, nonModifiableValue, env), - ), - ), - mapNullable(value.during_lovemaking, duringLovemaking => - getTextForCastingTimeDuringLovemaking(deps, duringLovemaking, env), - ), - ] - .filter(isNotNullish) - .join(" / ") diff --git a/src/shared/domain/libraryEntry/activatableSkill/checkResultBased.test.ts b/src/shared/domain/libraryEntry/activatableSkill/checkResultBased.test.ts deleted file mode 100644 index 62c607d74..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/checkResultBased.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import assert from "node:assert/strict" -import { describe, it } from "node:test" -import { translateMock } from "../../../utils/translate.ts" -import { getTextForCheckResultBased } from "./checkResultBased.ts" - -describe("getTextForCheckResultBased", () => { - it("should return the value text for a check-result-based parameter of an activatable skill", () => { - assert.equal(getTextForCheckResultBased({ base: "QualityLevels" }, translateMock), "QL") - assert.equal(getTextForCheckResultBased({ base: "SkillPoints" }, translateMock), "SP") - assert.equal( - getTextForCheckResultBased( - { base: "QualityLevels", modifier: { arithmetic: "Divide", value: 2 } }, - translateMock, - ), - "QL / 2", - ) - assert.equal( - getTextForCheckResultBased( - { base: "SkillPoints", modifier: { arithmetic: "Multiply", value: 3 } }, - translateMock, - ), - "SP × 3", - ) - }) -}) diff --git a/src/shared/domain/libraryEntry/activatableSkill/checkResultBased.ts b/src/shared/domain/libraryEntry/activatableSkill/checkResultBased.ts deleted file mode 100644 index e1f190cf6..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/checkResultBased.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - CheckResultArithmetic, - CheckResultBased, - CheckResultValue, -} from "optolith-database-schema/types/_ActivatableSkillCheckResultBased" -import { mapNullableDefault } from "../../../utils/nullable.ts" -import { Translate } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" - -const getCheckResultBaseValue = (baseValue: CheckResultValue, translate: Translate) => { - switch (baseValue) { - case "QualityLevels": - return translate("QL") - case "SkillPoints": - return translate("SP") - default: - return assertExhaustive(baseValue) - } -} - -const getArithmeticSymbol = (arithmetic: CheckResultArithmetic) => { - switch (arithmetic) { - case "Divide": - return ` / ` - case "Multiply": - return ` × ` - default: - return assertExhaustive(arithmetic) - } -} - -/** - * Returns the value text for a check-result-based parameter of an activatable - * skill. - */ -export const getTextForCheckResultBased = (value: CheckResultBased, translate: Translate): string => - getCheckResultBaseValue(value.base, translate) + - mapNullableDefault( - value.modifier, - modifier => getArithmeticSymbol(modifier.arithmetic) + modifier.value, - "", - ) diff --git a/src/shared/domain/libraryEntry/activatableSkill/cost.ts b/src/shared/domain/libraryEntry/activatableSkill/cost.ts deleted file mode 100644 index a5649aabe..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/cost.ts +++ /dev/null @@ -1,456 +0,0 @@ -import { - CostMap, - IndefiniteOneTimeCost, - ModifiableOneTimeCost, - MultipleOneTimeCosts, - NonModifiableOneTimeCost, - NonModifiableOneTimeCostPerCountable, - OneTimeCost, - SingleOneTimeCost, - SustainedCost, -} from "optolith-database-schema/types/_ActivatableSkillCost" -import { mapNullable, mapNullableDefault } from "../../../utils/nullable.ts" -import { Translate, TranslateMap } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { GetById } from "../../getTypes.ts" -import { - getResponsiveText, - getResponsiveTextOptional, - replaceTextIfRequested, - responsive, - ResponsiveTextSize, -} from "../responsiveText.ts" -import { MISSING_VALUE } from "../unknown.ts" -import { Entity } from "./entity.ts" -import { ModifiableParameter } from "./modifiableParameter.ts" -import { getTextForNonModifiableSuffix } from "./nonModifiable.ts" -import { getModifiableBySpeed, Speed } from "./speed.ts" -import { formatCost, formatTimeSpan } from "./units.ts" - -const getTextForModifiableOneTimeCost = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: ModifiableOneTimeCost, - env: { - speed: Speed - entity: Entity - responsiveText: ResponsiveTextSize - }, -): string => - mapNullable( - deps.getSkillModificationLevelById(value.initial_modification_level), - modificationLevel => { - const cost = getModifiableBySpeed( - modificationLevel, - env.speed, - config => config.cost, - config => config.cost, - ) - - return replaceTextIfRequested( - value.translations, - formatCost(deps.translate, env.entity, cost), - deps.translateMap, - env.responsiveText, - ) - }, - ) ?? MISSING_VALUE - -const getMinimumText = ( - isMinimum: boolean | undefined, - translate: Translate, - responsiveText: ResponsiveTextSize, -) => - isMinimum !== true - ? "" - : responsive( - responsiveText, - () => translate("at least "), - () => translate("min. "), - ) - -const getTextForNonModifiableOneTimeCostPerCountable = ( - deps: { - translate: Translate - translateMap: TranslateMap - formatCost: (x: number | string) => string - }, - value: NonModifiableOneTimeCostPerCountable | undefined, - env: { - responsiveText: ResponsiveTextSize - }, -) => - mapNullable(value, perCountable => { - const countableText = responsive( - env.responsiveText, - entity => deps.translate(" per {0}", entity), - entity => deps.translate("/{0}", entity), - getResponsiveText( - deps.translateMap(perCountable.translations)?.countable, - env.responsiveText, - ), - ) - - const minimumTotalText = - mapNullable(perCountable.minimum_total, minimumTotal => - deps.translate(", minimum of {0}", deps.formatCost(minimumTotal)), - ) ?? "" - - return countableText + minimumTotalText - }) ?? "" - -const getTextForPermanentValue = ( - value: number | undefined, - responsiveText: ResponsiveTextSize, - translate: Translate, -) => - value === undefined - ? "" - : responsive( - responsiveText, - perm => translate(", {0} of which are permanent", perm), - perm => translate(" ({0} perm.)", perm), - value, - ) - -const getTextForNonModifiableOneTimeCost = ( - deps: { translate: Translate; translateMap: TranslateMap }, - value: NonModifiableOneTimeCost, - env: { - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - const isMinimum = getMinimumText(value.is_minimum, deps.translate, env.responsiveText) - const formatCostP = formatCost.bind(this, deps.translate, env.entity) - const per = getTextForNonModifiableOneTimeCostPerCountable( - { ...deps, formatCost: formatCostP }, - value.per, - env, - ) - const permanent = getTextForPermanentValue( - value.permanent_value, - env.responsiveText, - deps.translate, - ) - const translation = deps.translateMap(value.translations) - const note = mapNullableDefault( - translation === undefined || translation.note === undefined - ? undefined - : getResponsiveTextOptional(translation.note, env.responsiveText), - noteIfPresent => ` (${noteIfPresent})`, - "", - ) - - const cannotModify = getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Cost, - env.responsiveText, - ) - - return isMinimum + formatCostP(value.value) + per + permanent + note + cannotModify -} - -const getTextForIndefiniteOneTimeCost = ( - deps: { translate: Translate; translateMap: TranslateMap }, - value: IndefiniteOneTimeCost, - env: { - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => - (getResponsiveText(deps.translateMap(value.translations)?.description, env.responsiveText) ?? - "") + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Cost, - env.responsiveText, - ) - -const getTextForSingleOneTimeCost = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: SingleOneTimeCost, - env: { - speed: Speed - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - switch (value.tag) { - case "Modifiable": - return getTextForModifiableOneTimeCost(deps, value.modifiable, env) - case "NonModifiable": - return getTextForNonModifiableOneTimeCost(deps, value.non_modifiable, env) - case "Indefinite": - return getTextForIndefiniteOneTimeCost(deps, value.indefinite, env) - default: - return assertExhaustive(value) - } -} - -const getTextForMultipleOneTimeCosts = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: MultipleOneTimeCosts, - type: "conjunction" | "disjunction", - env: { - speed: Speed - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - const modifiable = !value.every(part => part.tag === "Modifiable") - ? getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Cost, - env.responsiveText, - ) - : "" - - return ( - value - .map(part => getTextForSingleOneTimeCost(deps, part, env)) - .join( - (() => { - switch (type) { - case "conjunction": - return responsive( - env.responsiveText, - () => deps.translate(" and "), - () => deps.translate(" + "), - ) - case "disjunction": - return responsive( - env.responsiveText, - () => deps.translate(" or "), - () => deps.translate(" / "), - ) - default: - return assertExhaustive(type) - } - })(), - ) + modifiable - ) -} - -const getTextForCostMap = ( - deps: { - translate: Translate - translateMap: TranslateMap - }, - value: CostMap, - env: { - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - const translation = deps.translateMap(value.translations) - - if (value.translations !== undefined && translation === undefined) { - return MISSING_VALUE - } - - if (translation?.replacement !== undefined) { - return translation.replacement - } - - const costs = value.options.map(option => option.value).join("/") - const labels = value.options - .map(option => deps.translateMap(option.translations)?.label ?? MISSING_VALUE) - .join("/") - const permanentCosts = value.options.every(option => option.permanent_value !== undefined) - ? value.options.map(option => option.permanent_value!).join("/") - : undefined - - const formatCostP = formatCost.bind(this, deps.translate, env.entity) - const notModifiable = getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Cost, - env.responsiveText, - ) - - return ( - formatCostP(costs) + - deps.translate(" for ") + - mapNullableDefault(translation?.list_prepend, listPrepend => `${listPrepend} `, "") + - labels + - (translation?.list_append ?? "") + - (permanentCosts !== undefined - ? deps.translate(", {0} of which are permanent", formatCostP(permanentCosts)) - : "") + - notModifiable - ) -} - -/** - * Returns the text for the cost of a one-time activatable skill. - */ -export const getTextForOneTimeCost = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: OneTimeCost, - env: { - speed: Speed - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - switch (value.tag) { - case "Single": - return getTextForSingleOneTimeCost(deps, value.single, env) - case "Conjunction": - return getTextForMultipleOneTimeCosts(deps, value.conjunction, "conjunction", env) - case "Disjunction": - return getTextForMultipleOneTimeCosts(deps, value.disjunction, "disjunction", env) - case "Map": - return getTextForCostMap(deps, value.map, env) - default: - return assertExhaustive(value) - } -} - -/** - * Returns the text for the cost of a sustained activatable skill. - */ -export const getTextForSustainedCost = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: SustainedCost, - env: { - speed: Speed - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - switch (value.tag) { - case "Modifiable": { - const modificationLevel = deps.getSkillModificationLevelById( - value.modifiable.initial_modification_level, - ) - - if (modificationLevel === undefined) { - return MISSING_VALUE - } - - const cost = (() => { - switch (env.speed) { - case Speed.Fast: - return modificationLevel.fast.cost - case Speed.Slow: - return modificationLevel.slow.cost - default: - return assertExhaustive(env.speed) - } - })() - - const formatCostP = formatCost.bind(this, deps.translate, env.entity) - const interval = formatTimeSpan( - deps.translate, - env.responsiveText, - value.modifiable.interval.unit, - value.modifiable.interval.value, - ) - - return responsive( - env.responsiveText, - () => - `${formatCostP(cost) + deps.translate(" (casting)")} + ${ - formatCostP(cost / 2) + deps.translate(" per {0}", interval) - }`, - () => `${formatCostP(cost)} + ${formatCostP(cost / 2) + deps.translate("/{0}", interval)}`, - ) - } - case "NonModifiable": { - const isMinimum = getMinimumText( - value.non_modifiable.is_minimum, - deps.translate, - env.responsiveText, - ) - const cost = value.non_modifiable.value - const formatCostP = formatCost.bind(this, deps.translate, env.entity) - - const per = (() => { - if (value.non_modifiable.per === undefined) { - return { countable: "", minimumTotal: "" } - } - - const countable = responsive( - env.responsiveText, - entity => deps.translate(" per {0}", entity), - entity => deps.translate("/{0}", entity), - getResponsiveText( - deps.translateMap(value.non_modifiable.per.translations)?.countable, - env.responsiveText, - ), - ) - - const minimumTotal = - value.non_modifiable.per.minimum_total !== undefined - ? deps.translate( - ", minimum of {0}", - formatCostP(value.non_modifiable.per.minimum_total), - ) - : "" - - return { countable, minimumTotal } - })() - - const interval = formatTimeSpan( - deps.translate, - env.responsiveText, - value.non_modifiable.interval.unit, - value.non_modifiable.interval.value, - ) - - return ( - isMinimum + - responsive( - env.responsiveText, - () => - `${formatCostP(cost) + deps.translate(" (casting)")} + ${ - (value.non_modifiable.is_minimum === true - ? deps.translate("half of the activation cost") - : formatCostP(cost / 2)) + - per.countable + - deps.translate(" per {0}", interval) - }`, - () => - `${formatCostP(cost)} + ${ - (value.non_modifiable.is_minimum === true ? "50%" : formatCostP(cost / 2)) + - per.countable + - deps.translate("/{0}", interval) - }`, - ) + - per.minimumTotal + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Cost, - env.responsiveText, - ) - ) - } - default: - return assertExhaustive(value) - } -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/duration.ts b/src/shared/domain/libraryEntry/activatableSkill/duration.ts deleted file mode 100644 index bee892175..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/duration.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { - CheckResultBasedDuration, - DurationForOneTime, - DurationForSustained, - FixedDuration, - Immediate, - PermanentDuration, -} from "optolith-database-schema/types/_ActivatableSkillDuration" -import { BlessingDuration } from "optolith-database-schema/types/Blessing" -import { CantripDuration } from "optolith-database-schema/types/Cantrip" -import { mapNullableDefault } from "../../../utils/nullable.ts" -import { Translate, TranslateMap } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { - ResponsiveTextSize, - getResponsiveText, - replaceTextIfRequested, - responsive, -} from "../responsiveText.ts" -import { getTextForCheckResultBased } from "./checkResultBased.ts" -import { getTextForIsMaximum } from "./isMaximum.ts" -import { formatTimeSpan } from "./units.ts" - -const getTextForImmediateDuration = ( - deps: { - translate: Translate - translateMap: TranslateMap - }, - value: Immediate, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - const text = - deps.translate("Immediate") + - mapNullableDefault( - value.maximum, - max => { - const maxText = formatTimeSpan(deps.translate, env.responsiveText, max.unit, max.value) - - return responsive( - env.responsiveText, - () => deps.translate(" (no more than {0})", maxText), - () => deps.translate(" (max. {0})", maxText), - ) - }, - "", - ) - - return replaceTextIfRequested(value.translations, text, deps.translateMap, env.responsiveText) -} - -const getTextForPermanentDuration = ( - deps: { - translate: Translate - translateMap: TranslateMap - }, - value: PermanentDuration, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - const translation = deps.translateMap(value.translations) - const text = deps.translate("Permanent") - - if (translation?.replacement !== undefined) { - return getResponsiveText(translation.replacement, env.responsiveText).replace("$1", text) - } else { - return text - } -} - -const getTextForFixedDuration = ( - deps: { - translate: Translate - translateMap: TranslateMap - }, - value: FixedDuration, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - const isMaximum = getTextForIsMaximum(value.is_maximum, deps.translate, env.responsiveText) - const unitValue = formatTimeSpan(deps.translate, env.responsiveText, value.unit, value.value) - const text = isMaximum + unitValue - const translation = deps.translateMap(value.translations) - - if (translation?.replacement !== undefined) { - return getResponsiveText(translation.replacement, env.responsiveText).replace("$1", text) - } else { - return text - } -} - -const getTextForCheckResultBasedDuration = ( - deps: { - translate: Translate - translateMap: TranslateMap - }, - value: CheckResultBasedDuration, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - const isMaximum = getTextForIsMaximum(value.is_maximum, deps.translate, env.responsiveText) - - return formatTimeSpan( - deps.translate, - env.responsiveText, - value.unit, - isMaximum + getTextForCheckResultBased(value, deps.translate), - ) -} - -/** - * Returns the text for the duration of a one-time activatable skill. - */ -export const getTextForDurationForOneTime = ( - deps: { - translate: Translate - translateMap: TranslateMap - }, - value: DurationForOneTime, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - switch (value.tag) { - case "Immediate": - return getTextForImmediateDuration(deps, value.immediate, env) - case "Permanent": - return getTextForPermanentDuration(deps, value.permanent, env) - case "Fixed": - return getTextForFixedDuration(deps, value.fixed, env) - case "CheckResultBased": - return getTextForCheckResultBasedDuration(deps, value.check_result_based, env) - case "Indefinite": - return getResponsiveText( - deps.translateMap(value.indefinite.translations)?.description, - env.responsiveText, - ) - default: - return assertExhaustive(value) - } -} - -/** - * Returns the text for the duration of a sustained activatable skill. - */ -export const getTextForDurationForSustained = ( - deps: { translate: Translate }, - value: DurationForSustained | undefined, - env: { - responsiveText: ResponsiveTextSize - }, -): string => - value === undefined - ? responsive( - env.responsiveText, - () => deps.translate("Sustained"), - () => deps.translate("(S)"), - ) - : responsive( - env.responsiveText, - () => deps.translate("no more than "), - () => deps.translate("max. "), - ) + - formatTimeSpan(deps.translate, env.responsiveText, value.maximum.unit, value.maximum.value) - -/** - * Returns the text for the duration of a cantrip. - */ -export const getTextForCantripDuration = ( - deps: { translate: Translate; translateMap: TranslateMap }, - value: CantripDuration, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - switch (value.tag) { - case "Immediate": - return getTextForImmediateDuration(deps, value.immediate, env) - case "Fixed": - return getTextForFixedDuration(deps, value.fixed, env) - case "Indefinite": - return getResponsiveText( - deps.translateMap(value.indefinite.translations)?.description, - env.responsiveText, - ) - case "DuringLovemaking": { - const { value: lovemakingValue, unit: lovemakingUnit } = value.during_lovemaking - return formatTimeSpan(deps.translate, env.responsiveText, lovemakingUnit, lovemakingValue) - } - default: - return assertExhaustive(value) - } -} - -/** - * Returns the text for the duration of a blessing. - */ -export const getTextForBlessingDuration = ( - deps: { translate: Translate; translateMap: TranslateMap }, - value: BlessingDuration, - env: { - responsiveText: ResponsiveTextSize - }, -): string => { - switch (value.tag) { - case "Immediate": - return getTextForImmediateDuration(deps, value.immediate, env) - case "Fixed": - return getTextForFixedDuration(deps, value.fixed, env) - case "Indefinite": - return getResponsiveText( - deps.translateMap(value.indefinite.translations)?.description, - env.responsiveText, - ) - default: - return assertExhaustive(value) - } -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/effect.ts b/src/shared/domain/libraryEntry/activatableSkill/effect.ts deleted file mode 100644 index be57bd4f5..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/effect.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Effect } from "optolith-database-schema/types/_ActivatableSkillEffect" -import { filterNonNullable } from "../../../utils/array.ts" -import { mapNullable } from "../../../utils/nullable.ts" -import { Translate } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { LibraryEntryContent } from "../../libraryEntry.ts" - -const getContentPartsForQualityLevels = ( - source: { - text_before: string - quality_levels: string[] - text_after?: string - }, - getQualityLevelString: (index: number) => string | number, - translate: Translate, -): LibraryEntryContent[] => - filterNonNullable([ - { - label: translate("Effect"), - value: source.text_before, - }, - ...source.quality_levels.map((text, index) => ({ - value: text, - label: translate("QL {0}", getQualityLevelString(index)), - })), - mapNullable(source.text_after, textAfter => ({ - value: textAfter, - className: "effect-after", - })), - ]) - -/** - * Gets the text for the effect of an activatable skill. - */ -export const getTextForEffect = (effect: Effect, translate: Translate): LibraryEntryContent[] => { - switch (effect.tag) { - case "Plain": - return [ - { - label: translate("Effect"), - value: effect.plain.text, - }, - ] - case "ForEachQualityLevel": - return getContentPartsForQualityLevels( - effect.for_each_quality_level, - index => index + 1, - translate, - ) - case "ForEachTwoQualityLevels": - return getContentPartsForQualityLevels( - effect.for_each_two_quality_levels, - index => `${index * 2 + 1}–${index * 2 + 2}`, - translate, - ) - default: - return assertExhaustive(effect) - } -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/entity.ts b/src/shared/domain/libraryEntry/activatableSkill/entity.ts deleted file mode 100644 index ad3a0499a..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/entity.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * The entity type of an activatable skill. - */ -export enum Entity { - Cantrip, - Spell, - Ritual, - Blessing, - LiturgicalChant, - Ceremony, -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/isMaximum.ts b/src/shared/domain/libraryEntry/activatableSkill/isMaximum.ts deleted file mode 100644 index 6eb8731ff..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/isMaximum.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Translate } from "../../../utils/translate.ts" -import { ResponsiveTextSize, responsive } from "../responsiveText.ts" - -/** - * Returns the text to prepend for the `is_maximum` property. - */ -export const getTextForIsMaximum = ( - is_maximum: boolean | undefined, - translate: Translate, - responsiveText: ResponsiveTextSize, -): string => { - if (is_maximum !== true) { - return "" - } - - return responsive( - responsiveText, - () => translate("no more than "), - () => translate("max. "), - ) -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/modifiableParameter.ts b/src/shared/domain/libraryEntry/activatableSkill/modifiableParameter.ts deleted file mode 100644 index 1245a79c1..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/modifiableParameter.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * A parameter that is designed to be modifiable. - */ -export enum ModifiableParameter { - CastingTime, - Cost, - Range, -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/nonModifiable.ts b/src/shared/domain/libraryEntry/activatableSkill/nonModifiable.ts deleted file mode 100644 index 9284cdcb1..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/nonModifiable.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Translate } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { ResponsiveTextSize } from "../responsiveText.ts" -import { Entity } from "./entity.ts" -import { ModifiableParameter } from "./modifiableParameter.ts" - -/** - * Returns the suffix for the text of a non-modifiable parameter that indicates - * that the parameter cannot be modified. - */ -export const getTextForNonModifiableSuffix = ( - translate: Translate, - entity: Entity, - param: ModifiableParameter, - responsiveText: ResponsiveTextSize, -): string => { - if (responsiveText === ResponsiveTextSize.Compressed) { - switch (entity) { - case Entity.Spell: - case Entity.Ritual: - case Entity.LiturgicalChant: - case Entity.Ceremony: - return translate(" (cannot modify)") - case Entity.Cantrip: - case Entity.Blessing: - return "" - default: - return assertExhaustive(entity) - } - } - - switch (entity) { - case Entity.Spell: - switch (param) { - case ModifiableParameter.CastingTime: - return translate(" (you cannot use a modification on this spell’s casting time)") - case ModifiableParameter.Cost: - return translate(" (you cannot use a modification on this spell’s cost)") - case ModifiableParameter.Range: - return translate(" (you cannot use a modification on this spell’s range)") - default: - return assertExhaustive(param) - } - case Entity.Ritual: - switch (param) { - case ModifiableParameter.CastingTime: - return translate(" (you cannot use a modification on this ritual’s ritual time)") - case ModifiableParameter.Cost: - return translate(" (you cannot use a modification on this ritual’s cost)") - case ModifiableParameter.Range: - return translate(" (you cannot use a modification on this ritual’s range)") - default: - return assertExhaustive(param) - } - case Entity.LiturgicalChant: - switch (param) { - case ModifiableParameter.CastingTime: - return translate(" (you cannot use a modification on this chant’s liturgical time)") - case ModifiableParameter.Cost: - return translate(" (you cannot use a modification on this chant’s cost)") - case ModifiableParameter.Range: - return translate(" (you cannot use a modification on this chant’s range)") - default: - return assertExhaustive(param) - } - case Entity.Ceremony: - switch (param) { - case ModifiableParameter.CastingTime: - return translate(" (you cannot use a modification on this ceremony’s ceremonial time)") - case ModifiableParameter.Cost: - return translate(" (you cannot use a modification on this ceremony’s cost)") - case ModifiableParameter.Range: - return translate(" (you cannot use a modification on this ceremony’s range)") - default: - return assertExhaustive(param) - } - case Entity.Cantrip: - case Entity.Blessing: - return "" - default: - return assertExhaustive(entity) - } -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/parensIf.ts b/src/shared/domain/libraryEntry/activatableSkill/parensIf.ts deleted file mode 100644 index 67f02a719..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/parensIf.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Wraps a string in parentheses with a leading space if it is not empty or - * `undefined`. - */ -export const parensIf = (text: string | undefined): string => - text === undefined || text === "" ? "" : ` (${text})` - -/** - * Appends a string in parentheses with a leading space if it is not empty or - * `undefined`. - */ -export const appendInParens = (text: string, append: string | undefined): string => - append === undefined || append === "" ? text : `${text} (${append})` diff --git a/src/shared/domain/libraryEntry/activatableSkill/range.ts b/src/shared/domain/libraryEntry/activatableSkill/range.ts deleted file mode 100644 index d97db0ff9..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/range.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { Range, RangeUnit } from "optolith-database-schema/types/_ActivatableSkillRange" -import { BlessingRange } from "optolith-database-schema/types/Blessing" -import { CantripRange } from "optolith-database-schema/types/Cantrip" -import { Translate, TranslateMap } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { GetById } from "../../getTypes.ts" -import { - getResponsiveText, - getResponsiveTextOptional, - responsive, - ResponsiveTextSize, -} from "../responsiveText.ts" -import { MISSING_VALUE } from "../unknown.ts" -import { getTextForCheckResultBased } from "./checkResultBased.ts" -import { Entity } from "./entity.ts" -import { getTextForIsMaximum } from "./isMaximum.ts" -import { ModifiableParameter } from "./modifiableParameter.ts" -import { getTextForNonModifiableSuffix } from "./nonModifiable.ts" -import { Speed } from "./speed.ts" - -const toRangeUnit = ( - unit: RangeUnit, - value: number | string, - translate: Translate, - responsiveText: ResponsiveTextSize, -) => { - switch (unit) { - case "Steps": - return responsive( - responsiveText, - () => translate("{0} yards", value), - () => translate("{0} yd", value), - ) - case "Miles": - return responsive( - responsiveText, - () => translate("{0} miles", value), - () => translate("{0} mi.", value), - ) - default: - return assertExhaustive(unit) - } -} - -/** - * Returns the text for the range of an activatable skill. - */ -export const getTextForActivatableSkillRange = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: Range, - env: { - speed: Speed - responsiveText: ResponsiveTextSize - entity: Entity - }, -): string => { - const translation = deps.translateMap(value.translations) - - if (value.translations !== undefined && translation === undefined) { - return MISSING_VALUE - } - - const rangeValue = (() => { - switch (value.value.tag) { - case "Modifiable": { - const modificationLevel = deps.getSkillModificationLevelById( - value.value.modifiable.initial_modification_level, - ) - - if (modificationLevel === undefined) { - return MISSING_VALUE - } - - const range = (() => { - switch (env.speed) { - case Speed.Fast: - return modificationLevel.fast.range - case Speed.Slow: - return modificationLevel.slow.range - default: - return assertExhaustive(env.speed) - } - })() - - if (range === 1) { - return deps.translate("Touch") - } - - return toRangeUnit("Steps", range, deps.translate, env.responsiveText) - } - case "Sight": - return deps.translate("Sight") - case "Self": - return deps.translate("Self") - case "Global": - return deps.translate("Global") - case "Touch": - return ( - deps.translate("Touch") + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Range, - env.responsiveText, - ) - ) - case "Fixed": { - return ( - toRangeUnit( - value.value.fixed.unit, - value.value.fixed.value, - deps.translate, - env.responsiveText, - ) + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Range, - env.responsiveText, - ) - ) - } - case "CheckResultBased": { - const isMaximum = getTextForIsMaximum( - value.value.check_result_based.is_maximum, - deps.translate, - env.responsiveText, - ) - - const isRadius = - value.value.check_result_based.is_radius === true ? ` ${deps.translate("Radius")}` : "" - - return ( - isMaximum + - toRangeUnit( - value.value.check_result_based.unit, - getTextForCheckResultBased(value.value.check_result_based, deps.translate), - deps.translate, - env.responsiveText, - ) + - isRadius + - getTextForNonModifiableSuffix( - deps.translate, - env.entity, - ModifiableParameter.Range, - env.responsiveText, - ) - ) - } - default: - return assertExhaustive(value.value) - } - })() - - const withReplacement = - translation?.replacement !== undefined - ? getResponsiveText(translation.replacement, env.responsiveText).replace("$1", rangeValue) - : rangeValue - - const withNote = (() => { - if (translation?.note === undefined) { - return withReplacement - } - - const note = getResponsiveTextOptional(translation.note, env.responsiveText) - - if (note === undefined) { - return withReplacement - } - - return `${withReplacement} (${note})` - })() - - return withNote -} - -/** - * Returns the text for the range of a cantrip. - */ -export const getTextForCantripRange = ( - deps: { translate: Translate }, - value: CantripRange, - env: { responsiveText: ResponsiveTextSize }, -): string => { - switch (value.tag) { - case "Self": - return deps.translate("Self") - case "Touch": - return deps.translate("Touch") - case "Fixed": { - return toRangeUnit(value.fixed.unit, value.fixed.value, deps.translate, env.responsiveText) - } - default: - return assertExhaustive(value) - } -} - -/** - * Returns the text for the range of a blessing. - */ -export const getTextForBlessingRange = ( - deps: { translate: Translate }, - value: BlessingRange, - env: { responsiveText: ResponsiveTextSize }, -): string => { - switch (value.tag) { - case "Self": - return deps.translate("Self") - case "Touch": - return deps.translate("Touch") - case "Fixed": { - return toRangeUnit(value.fixed.unit, value.fixed.value, deps.translate, env.responsiveText) - } - default: - return assertExhaustive(value) - } -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/speed.ts b/src/shared/domain/libraryEntry/activatableSkill/speed.ts deleted file mode 100644 index 3dfacd75c..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/speed.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { - FastSkillModificationLevelConfig, - SkillModificationLevel, - SlowSkillModificationLevelConfig, -} from "optolith-database-schema/types/SkillModificationLevel" -import { assertExhaustive } from "../../../utils/typeSafety.ts" - -/** - * The speed of an activatable skill. - */ -export enum Speed { - Fast, - Slow, -} - -/** - * Returns a common value for a skill modification level depending on the speed. - */ -export const getModifiableBySpeed = ( - level: SkillModificationLevel, - speed: Speed, - fast: (config: FastSkillModificationLevelConfig) => T, - slow: (config: SlowSkillModificationLevelConfig) => T, -): T => { - switch (speed) { - case Speed.Fast: - return fast(level.fast) - case Speed.Slow: - return slow(level.slow) - default: - return assertExhaustive(speed) - } -} diff --git a/src/shared/domain/libraryEntry/activatableSkill/targetCategory.ts b/src/shared/domain/libraryEntry/activatableSkill/targetCategory.ts deleted file mode 100644 index 965877468..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/targetCategory.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { TargetCategory } from "optolith-database-schema/types/_ActivatableSkillTargetCategory" -import { mapNullable } from "../../../utils/nullable.ts" -import { Translate, TranslateMap } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { GetById } from "../../getTypes.ts" -import { LibraryEntryContent } from "../../libraryEntry.ts" -import { MISSING_VALUE } from "../unknown.ts" -import { appendInParens } from "./parensIf.ts" - -/** - * Get the text for the target category. - */ -export const getTextForTargetCategory = ( - deps: { - translate: Translate - translateMap: TranslateMap - getTargetCategoryById: GetById.Static.TargetCategory - }, - values: TargetCategory, -): LibraryEntryContent => ({ - label: deps.translate("Target Category"), - value: - values.length === 0 - ? deps.translate("all") - : values - .map(({ id, translations }) => { - const mainName = (() => { - switch (id.tag) { - case "Self": - return deps.translate("Self") - case "Zone": - return deps.translate("Zone") - case "LiturgicalChantsAndCeremonies": - return deps.translate("Liturgical Chants and Ceremonies") - case "Cantrips": - return deps.translate("Cantrips") - case "Predefined": { - const numericId = id.predefined.id.target_category - const specificTargetCategory = deps.getTargetCategoryById(numericId) - return ( - mapNullable( - deps.translateMap(specificTargetCategory?.translations), - translation => translation.name, - ) ?? MISSING_VALUE - ) - } - default: - return assertExhaustive(id) - } - })() - - return appendInParens(mainName, deps.translateMap(translations)?.note) - }) - .join(", "), -}) diff --git a/src/shared/domain/libraryEntry/activatableSkill/units.ts b/src/shared/domain/libraryEntry/activatableSkill/units.ts deleted file mode 100644 index 801867c5d..000000000 --- a/src/shared/domain/libraryEntry/activatableSkill/units.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Translate } from "../../../utils/translate.ts" -import { assertExhaustive } from "../../../utils/typeSafety.ts" -import { responsive, ResponsiveTextSize } from "../responsiveText.ts" -import { Entity } from "./entity.ts" - -type TimeSpanUnit = - | "Seconds" - | "Minutes" - | "Hours" - | "Days" - | "Weeks" - | "Months" - | "Years" - | "Centuries" - | "Actions" - | "CombatRounds" - | "SeductionActions" - | "Rounds" - -/** - * Returns the text for a time span unit. - */ -export const formatTimeSpan = ( - translate: Translate, - responsiveTextSize: ResponsiveTextSize, - unit: TimeSpanUnit, - value: number | string, -): string => { - switch (unit) { - case "Seconds": - return responsive( - responsiveTextSize, - () => translate("{0} seconds", value), - () => translate("{0} s", value), - ) - case "Minutes": - return responsive( - responsiveTextSize, - () => translate("{0} minutes", value), - () => translate("{0} min", value), - ) - case "Hours": - return responsive( - responsiveTextSize, - () => translate("{0} hours", value), - () => translate("{0} h", value), - ) - case "Days": - return responsive( - responsiveTextSize, - () => translate("{0} days", value), - () => translate("{0} d", value), - ) - case "Weeks": - return responsive( - responsiveTextSize, - () => translate("{0} weeks", value), - () => translate("{0} wks.", value), - ) - case "Months": - return responsive( - responsiveTextSize, - () => translate("{0} months", value), - () => translate("{0} mos.", value), - ) - case "Years": - return responsive( - responsiveTextSize, - () => translate("{0} years", value), - () => translate("{0} yrs.", value), - ) - case "Centuries": - return responsive( - responsiveTextSize, - () => translate("{0} centuries", value), - () => translate("{0} cent.", value), - ) - case "Actions": - return responsive( - responsiveTextSize, - () => translate("{0} actions", value), - () => translate("{0} act", value), - ) - case "CombatRounds": - return responsive( - responsiveTextSize, - () => translate("{0} combat rounds", value), - () => translate("{0} CR", value), - ) - case "SeductionActions": - return responsive( - responsiveTextSize, - () => translate("{0} seduction actions", value), - () => translate("{0} SA", value), - ) - case "Rounds": - return responsive( - responsiveTextSize, - () => translate("{0} rounds", value), - () => translate("{0} rnds", value), - ) - default: - return assertExhaustive(unit) - } -} - -/** - * Returns the text for a cost unit that is based on the entity type. - */ -export const formatCost = (translate: Translate, entity: Entity, value: number | string) => { - switch (entity) { - case Entity.Cantrip: - case Entity.Spell: - case Entity.Ritual: - return translate("{0} AE", value) - case Entity.Blessing: - case Entity.LiturgicalChant: - case Entity.Ceremony: - return translate("{0} KP", value) - default: - return assertExhaustive(entity) - } -} diff --git a/src/shared/domain/libraryEntry/responsiveText.ts b/src/shared/domain/libraryEntry/responsiveText.ts deleted file mode 100644 index 9f649a914..000000000 --- a/src/shared/domain/libraryEntry/responsiveText.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { LocaleMap } from "optolith-database-schema/types/_LocaleMap" -import { - ResponsiveText, - ResponsiveTextOptional, - ResponsiveTextReplace, -} from "optolith-database-schema/types/_ResponsiveText" -import { mapNullable } from "../../utils/nullable.ts" -import { TranslateMap } from "../../utils/translate.ts" -import { assertExhaustive } from "../../utils/typeSafety.ts" -import { MISSING_VALUE } from "./unknown.ts" - -/** - * Whether the entry is displayed in a normal or compressed setting. Normal/full - * usually means a full library entry display, whether compressed usually means - * the character sheet. - */ -export enum ResponsiveTextSize { - Compressed, - Full, -} - -/** - * Executes one of two functions depending on the responsive text size. - */ -export const responsive = ( - size: ResponsiveTextSize, - full: (...args: A) => T, - compressed: (...args: A) => T, - ...args: A -): T => { - switch (size) { - case ResponsiveTextSize.Compressed: - return compressed(...args) - case ResponsiveTextSize.Full: - return full(...args) - default: - return assertExhaustive(size) - } -} - -/** - * Returns the responsive text for a given size. - */ -export const getResponsiveText = ( - value: ResponsiveText | undefined, - size: ResponsiveTextSize, -): string => { - if (value === undefined) { - return MISSING_VALUE - } - - return responsive( - size, - () => value.full, - () => value.compressed, - ) -} - -/** - * Returns the responsive text for a given size if it is defined. - */ -export const getResponsiveTextOptional = ( - value: ResponsiveTextOptional | undefined, - size: ResponsiveTextSize, -): string | undefined => { - if (value === undefined) { - return MISSING_VALUE - } - - return responsive( - size, - () => value.full, - () => value.compressed, - ) -} - -/** - * Replaces a text with a given value if a replacement is requested, otherwise - * just return the . - */ -export const replaceTextIfRequested = ( - translation: LocaleMap<{ replacement?: ResponsiveTextReplace }> | undefined, - valueToReplace: string, - translateMap: TranslateMap, - responsiveText: ResponsiveTextSize, -) => - mapNullable(translateMap(translation)?.replacement, replacement => - getResponsiveText(replacement, responsiveText).replace("$1", valueToReplace), - ) ?? valueToReplace diff --git a/src/shared/domain/libraryEntry/unknown.ts b/src/shared/domain/libraryEntry/unknown.ts deleted file mode 100644 index f5274f212..000000000 --- a/src/shared/domain/libraryEntry/unknown.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * String to display when a translation that should be present is missing. - */ -export const MISSING_VALUE = "???" diff --git a/src/shared/domain/rated/activatableSkill.ts b/src/shared/domain/rated/activatableSkill.ts deleted file mode 100644 index 327298332..000000000 --- a/src/shared/domain/rated/activatableSkill.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { - FastOneTimePerformanceParameters, - FastSustainedPerformanceParameters, - SlowOneTimePerformanceParameters, - SlowSustainedPerformanceParameters, -} from "optolith-database-schema/types/_ActivatableSkill" -import { Translate, TranslateMap } from "../../utils/translate.ts" -import { GetById } from "../getTypes.ts" -import { - getTextForFastCastingTime, - getTextForSlowCastingTime, -} from "../libraryEntry/activatableSkill/castingTime.ts" -import { - getTextForOneTimeCost, - getTextForSustainedCost, -} from "../libraryEntry/activatableSkill/cost.ts" -import { - getTextForDurationForOneTime, - getTextForDurationForSustained, -} from "../libraryEntry/activatableSkill/duration.ts" -import { Entity } from "../libraryEntry/activatableSkill/entity.ts" -import { getTextForActivatableSkillRange } from "../libraryEntry/activatableSkill/range.ts" -import { Speed } from "../libraryEntry/activatableSkill/speed.ts" -import { ResponsiveTextSize } from "../libraryEntry/responsiveText.ts" - -/** - * Get the texts for all fast one-time performance parameters. - */ -export const getTextForFastOneTimePerformanceParameters = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: FastOneTimePerformanceParameters, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): { - castingTime: string - cost: string - range: string - duration: string -} => ({ - castingTime: getTextForFastCastingTime(deps, value.casting_time, env), - cost: getTextForOneTimeCost(deps, value.cost, { speed: Speed.Fast, ...env }), - range: getTextForActivatableSkillRange(deps, value.range, { speed: Speed.Fast, ...env }), - duration: getTextForDurationForOneTime(deps, value.duration, env), -}) - -/** - * Get the texts for all fast sustained performance parameters. - */ -export const getTextForFastSustainedPerformanceParameters = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: FastSustainedPerformanceParameters, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): { - castingTime: string - cost: string - range: string - duration: string -} => ({ - castingTime: getTextForFastCastingTime(deps, value.casting_time, env), - cost: getTextForSustainedCost(deps, value.cost, { speed: Speed.Fast, ...env }), - range: getTextForActivatableSkillRange(deps, value.range, { speed: Speed.Fast, ...env }), - duration: getTextForDurationForSustained(deps, value.duration, env), -}) - -/** - * Get the texts for all slow one-time performance parameters. - */ -export const getTextForSlowOneTimePerformanceParameters = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: SlowOneTimePerformanceParameters, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): { - castingTime: string - cost: string - range: string - duration: string -} => ({ - castingTime: getTextForSlowCastingTime(deps, value.casting_time, env), - cost: getTextForOneTimeCost(deps, value.cost, { speed: Speed.Slow, ...env }), - range: getTextForActivatableSkillRange(deps, value.range, { speed: Speed.Slow, ...env }), - duration: getTextForDurationForOneTime(deps, value.duration, env), -}) - -/** - * Get the texts for all slow sustained performance parameters. - */ -export const getTextForSlowSustainedPerformanceParameters = ( - deps: { - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - translate: Translate - translateMap: TranslateMap - }, - value: SlowSustainedPerformanceParameters, - env: { - entity: Entity - responsiveText: ResponsiveTextSize - }, -): { - castingTime: string - cost: string - range: string - duration: string -} => ({ - castingTime: getTextForSlowCastingTime(deps, value.casting_time, env), - cost: getTextForSustainedCost(deps, value.cost, { speed: Speed.Slow, ...env }), - range: getTextForActivatableSkillRange(deps, value.range, { speed: Speed.Slow, ...env }), - duration: getTextForDurationForSustained(deps, value.duration, env), -}) diff --git a/src/shared/domain/rated/combatTechnique.ts b/src/shared/domain/rated/combatTechnique.ts index 6b1aa0b34..fe8e2bb0b 100644 --- a/src/shared/domain/rated/combatTechnique.ts +++ b/src/shared/domain/rated/combatTechnique.ts @@ -2,12 +2,8 @@ import { CloseCombatTechnique } from "optolith-database-schema/types/CombatTechn import { RangedCombatTechnique } from "optolith-database-schema/types/CombatTechnique_Ranged" import { CombatTechniqueIdentifier } from "optolith-database-schema/types/_IdentifierGroup" import { AttributeReference } from "optolith-database-schema/types/_SimpleReferences" -import { mapNullable } from "../../utils/nullable.ts" import { Activatable, countOptions } from "../activatable/activatableEntry.ts" -import { createImprovementCost } from "../adventurePoints/improvementCost.ts" -import { GetById } from "../getTypes.ts" import { AttributeIdentifier } from "../identifier.ts" -import { createLibraryEntryCreator } from "../libraryEntry.ts" import { getAttributeValue } from "./attribute.ts" import { Rated } from "./ratedEntry.ts" @@ -157,73 +153,3 @@ export const getHighestRequiredAttributeForCombatTechnique = ( value: dynamicCombatTechnique.value - 2 - exceptionalCombatTechniqueBonus, } } - -/** - * Get a JSON representation of the rules text for a close combat technique. - */ -export const getCloseCombatTechniqueLibraryEntry = createLibraryEntryCreator< - CloseCombatTechnique, - { - getAttributeById: GetById.Static.Attribute - } ->((entry, { getAttributeById }) => ({ translate, translateMap }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - return { - title: translation.name, - className: "combat-technique close-combat-technique", - content: [ - mapNullable(translation.special, value => ({ - label: translate("Special"), - value, - })), - { - label: translate("Primary Attribute"), - value: entry.primary_attribute - .map(attr => translateMap(getAttributeById(attr.id.attribute)?.translations)?.name) - .join("/"), - }, - createImprovementCost(translate, entry.improvement_cost), - ], - src: entry.src, - } -}) - -/** - * Get a JSON representation of the rules text for a ranged combat technique. - */ -export const getRangedCombatTechniqueLibraryEntry = createLibraryEntryCreator< - RangedCombatTechnique, - { - getAttributeById: GetById.Static.Attribute - } ->((entry, { getAttributeById }) => ({ translate, translateMap }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - return { - title: translation.name, - className: "combat-technique ranged-combat-technique", - content: [ - mapNullable(translation.special, value => ({ - label: translate("Special"), - value, - })), - { - label: translate("Primary Attribute"), - value: entry.primary_attribute - .map(attr => translateMap(getAttributeById(attr.id.attribute)?.translations)?.name) - .join("/"), - }, - createImprovementCost(translate, entry.improvement_cost), - ], - src: entry.src, - } -}) diff --git a/src/shared/domain/rated/liturgicalChant.ts b/src/shared/domain/rated/liturgicalChant.ts index 3983ebf54..54347092a 100644 --- a/src/shared/domain/rated/liturgicalChant.ts +++ b/src/shared/domain/rated/liturgicalChant.ts @@ -1,41 +1,20 @@ import { Aspect } from "optolith-database-schema/types/Aspect" -import { Blessing } from "optolith-database-schema/types/Blessing" -import { Ceremony } from "optolith-database-schema/types/Ceremony" import { ExperienceLevel } from "optolith-database-schema/types/ExperienceLevel" -import { LiturgicalChant } from "optolith-database-schema/types/LiturgicalChant" import { SkillTradition } from "optolith-database-schema/types/_Blessed" import { LiturgyIdentifier } from "optolith-database-schema/types/_IdentifierGroup" import { ImprovementCost } from "optolith-database-schema/types/_ImprovementCost" -import { AspectReference } from "optolith-database-schema/types/_SimpleReferences" import { SkillCheck } from "optolith-database-schema/types/_SkillCheck" import { BlessedTradition } from "optolith-database-schema/types/specialAbility/BlessedTradition" import { count, countByMany } from "../../utils/array.ts" import { Compare, compareAt, compareNullish, numAsc, reduceCompare } from "../../utils/compare.ts" import { isNotNullish } from "../../utils/nullable.ts" -import { Translate, TranslateMap } from "../../utils/translate.ts" +import { TranslateMap } from "../../utils/translate.ts" import { assertExhaustive } from "../../utils/typeSafety.ts" import { Activatable, countOptions } from "../activatable/activatableEntry.ts" -import { - compareImprovementCost, - createImprovementCost, - fromRaw, -} from "../adventurePoints/improvementCost.ts" -import { All, GetById } from "../getTypes.ts" +import { compareImprovementCost, fromRaw } from "../adventurePoints/improvementCost.ts" +import { All } from "../getTypes.ts" import { AspectIdentifier, createIdentifierObject } from "../identifier.ts" -import { createLibraryEntryCreator, LibraryEntryContent } from "../libraryEntry.ts" -import { getTextForBlessingDuration } from "../libraryEntry/activatableSkill/duration.ts" -import { getTextForEffect } from "../libraryEntry/activatableSkill/effect.ts" -import { Entity } from "../libraryEntry/activatableSkill/entity.ts" -import { getTextForBlessingRange } from "../libraryEntry/activatableSkill/range.ts" -import { getTextForTargetCategory } from "../libraryEntry/activatableSkill/targetCategory.ts" -import { ResponsiveTextSize } from "../libraryEntry/responsiveText.ts" import { LiturgiesSortOrder } from "../sortOrders.ts" -import { - getTextForFastOneTimePerformanceParameters, - getTextForFastSustainedPerformanceParameters, - getTextForSlowOneTimePerformanceParameters, - getTextForSlowSustainedPerformanceParameters, -} from "./activatableSkill.ts" import { DisplayedActiveLiturgy } from "./liturgicalChantActive.ts" import { DisplayedInactiveLiturgy } from "./liturgicalChantInactive.ts" import { @@ -47,7 +26,6 @@ import { isRatedActive, isRatedWithEnhancementsActive, } from "./ratedEntry.ts" -import { getTextForCheck } from "./skillCheck.ts" /** * Returns the value for a dynamic liturgical chant entry that might not exist @@ -555,345 +533,3 @@ export const filterAndSortDisplayed = `${aspects_str} / ${gr_str}`)) // ) - -const getTextForTraditions = ( - deps: { - translate: Translate - translateMap: TranslateMap - localeCompare: Compare - getBlessedTraditionById: GetById.Static.BlessedTradition - getAspectById: GetById.Static.Aspect - }, - values: SkillTradition[], -): LibraryEntryContent => { - const getAspectName = (ref: AspectReference) => - deps.translateMap(deps.getAspectById(ref.id.aspect)?.translations)?.name - - const text = values - .map(trad => { - switch (trad.tag) { - case "GeneralAspect": - return getAspectName(trad.general_aspect) - case "Tradition": { - const traditionTranslation = deps.translateMap( - deps.getBlessedTraditionById(trad.tradition.tradition.id.blessed_tradition) - ?.translations, - ) - const name = traditionTranslation?.name_compressed ?? traditionTranslation?.name - - if (name === undefined) { - return undefined - } - - const aspects = - trad.tradition.aspects - ?.map(getAspectName) - .filter(isNotNullish) - .sort(deps.localeCompare) ?? [] - - if (aspects.length === 0) { - return name - } - - return `${name} (${aspects.join(" and ")})` - } - default: - return assertExhaustive(trad) - } - }) - .filter(isNotNullish) - .join(", ") - - return { - label: deps.translate("Traditions"), - value: text, - } -} - -/** - * Get a JSON representation of the rules text for a blessing. - */ -export const getBlessingLibraryEntry = createLibraryEntryCreator< - Blessing, - { - getTargetCategoryById: GetById.Static.TargetCategory - } ->((entry, { getTargetCategoryById }) => ({ translate, translateMap }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const range = getTextForBlessingRange({ translate }, entry.parameters.range, { - responsiveText: ResponsiveTextSize.Full, - }) - - const duration = getTextForBlessingDuration( - { translate, translateMap }, - entry.parameters.duration, - { - responsiveText: ResponsiveTextSize.Full, - }, - ) - - return { - title: translation.name, - className: "blessing", - content: [ - { - label: translate("Effect"), - value: translation.effect, - }, - { - label: translate("Range"), - value: range !== translation.range ? `***${range}*** (${translation.range})` : range, - }, - { - label: translate("Duration"), - value: - duration !== translation.duration - ? `***${duration}*** (${translation.duration})` - : duration, - }, - getTextForTargetCategory({ translate, translateMap, getTargetCategoryById }, entry.target), - ], - src: entry.src, - } -}) - -/** - * Get a JSON representation of the rules text for a liturgical chant. - */ -export const getLiturgicalChantLibraryEntry = createLibraryEntryCreator< - LiturgicalChant, - { - getAttributeById: GetById.Static.Attribute - getDerivedCharacteristicById: GetById.Static.DerivedCharacteristic - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - getTargetCategoryById: GetById.Static.TargetCategory - getBlessedTraditionById: GetById.Static.BlessedTradition - getAspectById: GetById.Static.Aspect - } ->( - ( - entry, - { - getAttributeById, - getDerivedCharacteristicById, - getSkillModificationLevelById, - getTargetCategoryById, - getBlessedTraditionById, - getAspectById, - }, - ) => - ({ translate, translateMap, localeCompare }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const { castingTime, cost, range, duration } = (() => { - switch (entry.parameters.tag) { - case "OneTime": - return getTextForFastOneTimePerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.one_time, - { - entity: Entity.LiturgicalChant, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - case "Sustained": - return getTextForFastSustainedPerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.sustained, - { - entity: Entity.LiturgicalChant, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - default: - return assertExhaustive(entry.parameters) - } - })() - - return { - title: translation.name, - className: "liturgical-chant", - content: [ - getTextForCheck({ translate, translateMap, getAttributeById }, entry.check, { - value: entry.check_penalty, - responsiveText: ResponsiveTextSize.Full, - getDerivedCharacteristicById, - }), - ...getTextForEffect(translation.effect, translate), - { - label: translate("Liturgical Time"), - value: - castingTime !== translation.casting_time.full - ? `***${castingTime}*** (${translation.casting_time.full})` - : castingTime, - }, - { - label: translate("KP Cost"), - value: - cost !== translation.cost.full ? `***${cost}*** (${translation.cost.full})` : cost, - }, - { - label: translate("Range"), - value: - range !== translation.range.full - ? `***${range}*** (${translation.range.full})` - : range, - }, - { - label: translate("Duration"), - value: - duration !== translation.duration.full - ? `***${duration}*** (${translation.duration.full})` - : duration, - }, - getTextForTargetCategory( - { translate, translateMap, getTargetCategoryById }, - entry.target, - ), - getTextForTraditions( - { translate, translateMap, localeCompare, getBlessedTraditionById, getAspectById }, - entry.traditions, - ), - createImprovementCost(translate, entry.improvement_cost), - ], - src: entry.src, - } - }, -) - -/** - * Get a JSON representation of the rules text for a ceremony. - */ -export const getCeremonyLibraryEntry = createLibraryEntryCreator< - Ceremony, - { - getAttributeById: GetById.Static.Attribute - getDerivedCharacteristicById: GetById.Static.DerivedCharacteristic - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - getTargetCategoryById: GetById.Static.TargetCategory - getBlessedTraditionById: GetById.Static.BlessedTradition - getAspectById: GetById.Static.Aspect - } ->( - ( - entry, - { - getAttributeById, - getDerivedCharacteristicById, - getSkillModificationLevelById, - getTargetCategoryById, - getBlessedTraditionById, - getAspectById, - }, - ) => - ({ translate, translateMap, localeCompare }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const { castingTime, cost, range, duration } = (() => { - switch (entry.parameters.tag) { - case "OneTime": - return getTextForSlowOneTimePerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.one_time, - { - entity: Entity.Ceremony, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - case "Sustained": - return getTextForSlowSustainedPerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.sustained, - { - entity: Entity.Ceremony, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - default: - return assertExhaustive(entry.parameters) - } - })() - - return { - title: translation.name, - className: "ceremony", - content: [ - getTextForCheck({ translate, translateMap, getAttributeById }, entry.check, { - value: entry.check_penalty, - responsiveText: ResponsiveTextSize.Full, - getDerivedCharacteristicById, - }), - ...getTextForEffect(translation.effect, translate), - { - label: translate("Ceremonial Time"), - value: - castingTime !== translation.casting_time.full - ? `***${castingTime}*** (${translation.casting_time.full})` - : castingTime, - }, - { - label: translate("KP Cost"), - value: - cost !== translation.cost.full ? `***${cost}*** (${translation.cost.full})` : cost, - }, - { - label: translate("Range"), - value: - range !== translation.range.full - ? `***${range}*** (${translation.range.full})` - : range, - }, - { - label: translate("Duration"), - value: - duration !== translation.duration.full - ? `***${duration}*** (${translation.duration.full})` - : duration, - }, - getTextForTargetCategory( - { translate, translateMap, getTargetCategoryById }, - entry.target, - ), - getTextForTraditions( - { translate, translateMap, localeCompare, getBlessedTraditionById, getAspectById }, - entry.traditions, - ), - createImprovementCost(translate, entry.improvement_cost), - ], - src: entry.src, - } - }, -) diff --git a/src/shared/domain/rated/skill.ts b/src/shared/domain/rated/skill.ts index 2ef2f1926..3cd281840 100644 --- a/src/shared/domain/rated/skill.ts +++ b/src/shared/domain/rated/skill.ts @@ -1,15 +1,8 @@ -import { NewApplicationsAndUsesCache } from "optolith-database-schema/cache/newApplicationsAndUses" import { Culture } from "optolith-database-schema/types/Culture" import { Skill } from "optolith-database-schema/types/Skill" import { SkillCheck } from "optolith-database-schema/types/_SkillCheck" -import { isNotNullish } from "../../utils/nullable.ts" -import { assertExhaustive } from "../../utils/typeSafety.ts" import { Activatable, countOptions } from "../activatable/activatableEntry.ts" -import { createImprovementCost } from "../adventurePoints/improvementCost.ts" -import { All, GetById } from "../getTypes.ts" -import { createLibraryEntryCreator } from "../libraryEntry.ts" import { Rated } from "./ratedEntry.ts" -import { getTextForCheck } from "./skillCheck.ts" /** * The minimum value of a skill. @@ -74,126 +67,3 @@ export const getSkillCommonness = ( : culture.uncommon_skills?.some(({ id: { skill: id } }) => skill.id === id) ?? false ? "uncommon" : undefined - -/** - * Get a JSON representation of the rules text for a skill. - */ -export const getSkillLibraryEntry = createLibraryEntryCreator< - Skill, - { - getAttributeById: GetById.Static.Attribute - blessedTraditions: All.Static.BlessedTraditions - diseases: All.Static.Diseases - regions: All.Static.Regions - cache: NewApplicationsAndUsesCache - } ->( - (entry, { getAttributeById, blessedTraditions, diseases, regions, cache }) => - ({ translate, translateMap, localeCompare }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const newApplications = (entry === undefined ? [] : cache.newApplications[entry.id] ?? []) - .map(x => translateMap(x.data.translations)?.name) - .filter(isNotNullish) - .sort(localeCompare) - - const uses = (entry === undefined ? [] : cache.uses[entry.id] ?? []) - .map(x => translateMap(x.data.translations)?.name) - .filter(isNotNullish) - .sort(localeCompare) - - const applications = (() => { - switch (entry.applications.tag) { - case "Derived": - return (() => { - switch (entry.applications.derived) { - case "BlessedTraditions": - return Object.values(blessedTraditions) - .map(x => translateMap(x.translations)?.name) - .filter(isNotNullish) - .sort(localeCompare) - case "Diseases": - return Object.values(diseases) - .map(x => translateMap(x.translations)?.name) - .filter(isNotNullish) - .sort(localeCompare) - case "Regions": - return Object.values(regions) - .map(x => translateMap(x.translations)?.name) - .filter(isNotNullish) - .sort(localeCompare) - default: - return assertExhaustive(entry.applications.derived) - } - })() - case "Explicit": - return entry.applications.explicit - .map(x => translateMap(x.translations)?.name) - .filter(isNotNullish) - .sort(localeCompare) - default: - return assertExhaustive(entry.applications) - } - })() - - return { - title: translation.name, - className: "skill", - content: [ - newApplications.length === 0 - ? undefined - : { - label: translate("New Applications"), - value: newApplications.join(", "), - }, - uses.length === 0 - ? undefined - : { - label: translate("Uses"), - value: uses.join(", "), - }, - getTextForCheck({ translate, translateMap, getAttributeById }, entry.check), - { - label: translate("Applications"), - value: applications.join(", "), - }, - { - label: translate("Encumbrance"), - value: - entry.encumbrance === "True" - ? translate("Yes") - : entry.encumbrance === "False" - ? translate("No") - : translation.encumbrance_description ?? translate("Maybe"), - }, - translation?.tools === undefined - ? undefined - : { - label: translate("Tools"), - value: translation.tools, - }, - { - label: translate("Quality"), - value: translation.quality, - }, - { - label: translate("Failed Check"), - value: translation.failed, - }, - { - label: translate("Critical Success"), - value: translation.critical, - }, - { - label: translate("Botch"), - value: translation.botch, - }, - createImprovementCost(translate, entry.improvement_cost), - ], - } - }, -) diff --git a/src/shared/domain/rated/skillCheck.ts b/src/shared/domain/rated/skillCheck.ts index 3c46198d6..4509e8a14 100644 --- a/src/shared/domain/rated/skillCheck.ts +++ b/src/shared/domain/rated/skillCheck.ts @@ -1,12 +1,6 @@ import { Attribute } from "optolith-database-schema/types/Attribute" -import { SkillCheck, SkillCheckPenalty } from "optolith-database-schema/types/_SkillCheck" -import { Translate, TranslateMap } from "../../utils/translate.ts" -import { assertExhaustive } from "../../utils/typeSafety.ts" -import { GetById } from "../getTypes.ts" +import { SkillCheck } from "optolith-database-schema/types/_SkillCheck" import { getSingleHighestPair, IdValuePair } from "../idValue.ts" -import { DerivedCharacteristicIdentifier } from "../identifier.ts" -import { LibraryEntryContent } from "../libraryEntry.ts" -import { responsive, ResponsiveTextSize } from "../libraryEntry/responsiveText.ts" import { getAttributeValue } from "./attribute.ts" import { Rated } from "./ratedEntry.ts" @@ -89,122 +83,3 @@ export const getHighestCheckAttributeValue = ( getDynamicAttribute: (id: number) => Rated | undefined, check: SkillCheck, ): number => Math.max(8, ...getSkillCheckValues(getDynamicAttribute, check)) - -/** - * Returns the skill check as an inline library property. - */ -export const getTextForCheck = ( - deps: { - translate: Translate - translateMap: TranslateMap - getAttributeById: GetById.Static.Attribute - }, - check: SkillCheck, - checkPenalty?: { - value: SkillCheckPenalty | undefined - responsiveText: ResponsiveTextSize - getDerivedCharacteristicById: GetById.Static.DerivedCharacteristic - }, -): LibraryEntryContent => ({ - label: deps.translate("Check"), - value: - check - .map( - ({ id: { attribute: id } }) => - deps.translateMap(deps.getAttributeById(id)?.translations)?.abbreviation ?? "??", - ) - .join("/") + - (() => { - if (checkPenalty?.value === undefined) { - return "" - } - - const { responsiveText, getDerivedCharacteristicById } = checkPenalty - - const getDerivedCharacteristicTranslation = (id: DerivedCharacteristicIdentifier) => - deps.translateMap(getDerivedCharacteristicById(id)?.translations) - - const getSpiritTranslation = () => - getDerivedCharacteristicTranslation(DerivedCharacteristicIdentifier.Spirit) - - const getToughnessTranslation = () => - getDerivedCharacteristicTranslation(DerivedCharacteristicIdentifier.Toughness) - - const penalty = (() => { - switch (checkPenalty.value) { - case "Spirit": { - const translation = getSpiritTranslation() - return translation === undefined - ? "" - : responsive( - responsiveText, - () => translation.name, - () => translation.abbreviation, - ) - } - - case "HalfOfSpirit": { - const translation = getSpiritTranslation() - return translation === undefined - ? "" - : responsive( - responsiveText, - () => `${translation.name}/2`, - () => `${translation.abbreviation}/2`, - ) - } - - case "Toughness": { - const translation = getToughnessTranslation() - return translation === undefined - ? "" - : responsive( - responsiveText, - () => `${translation.name}/2`, - () => `${translation.abbreviation}/2`, - ) - } - - case "HigherOfSpiritAndToughness": { - const spiritTranslation = getSpiritTranslation() - const toughnessTranslation = getToughnessTranslation() - return spiritTranslation === undefined || toughnessTranslation === undefined - ? "" - : responsive( - responsiveText, - () => - deps.translate( - "{0} or {1}, depending on which value is higher", - spiritTranslation.abbreviation, - toughnessTranslation.abbreviation, - ), - () => `${spiritTranslation.abbreviation}/${toughnessTranslation.abbreviation}`, - ) - } - - case "SummoningDifficulty": - return responsive( - responsiveText, - () => deps.translate("Invocation Difficulty"), - () => deps.translate("ID"), - ) - - case "CreationDifficulty": - return responsive( - responsiveText, - () => deps.translate("Creation Difficulty"), - () => deps.translate("CD"), - ) - - default: - return assertExhaustive(checkPenalty.value) - } - })() - - return responsive( - responsiveText, - () => deps.translate(" (modified by {0})", penalty), - () => deps.translate(" (− {0})", penalty), - ) - })(), -}) diff --git a/src/shared/domain/rated/spell.ts b/src/shared/domain/rated/spell.ts index 8e2177fe5..22be9f229 100644 --- a/src/shared/domain/rated/spell.ts +++ b/src/shared/domain/rated/spell.ts @@ -9,23 +9,18 @@ import { SpellworkIdentifier, } from "optolith-database-schema/types/_IdentifierGroup" import { ImprovementCost as RawImprovementCost } from "optolith-database-schema/types/_ImprovementCost" -import { - MagicalTraditionReference, - PropertyReference, -} from "optolith-database-schema/types/_SimpleReferences" import { SkillCheck } from "optolith-database-schema/types/_SkillCheck" import { Traditions } from "optolith-database-schema/types/_Spellwork" import { count, countBy, sum } from "../../utils/array.ts" import { Compare, compareAt, compareNullish, numAsc, reduceCompare } from "../../utils/compare.ts" -import { isNotNullish, mapNullable, mapNullableDefault } from "../../utils/nullable.ts" -import { Translate, TranslateMap } from "../../utils/translate.ts" +import { isNotNullish, mapNullableDefault } from "../../utils/nullable.ts" +import { TranslateMap } from "../../utils/translate.ts" import { assertExhaustive } from "../../utils/typeSafety.ts" import { Activatable, countOptions } from "../activatable/activatableEntry.ts" import { CombinedActiveMagicalTradition } from "../activatable/magicalTradition.ts" import { ImprovementCost, compareImprovementCost, - createImprovementCost, fromRaw, } from "../adventurePoints/improvementCost.ts" import { All, GetById } from "../getTypes.ts" @@ -34,21 +29,7 @@ import { createIdentifierObject, getCreateIdentifierObject, } from "../identifier.ts" -import { LibraryEntryContent, createLibraryEntryCreator } from "../libraryEntry.ts" -import { getTextForCantripDuration } from "../libraryEntry/activatableSkill/duration.ts" -import { getTextForEffect } from "../libraryEntry/activatableSkill/effect.ts" -import { Entity } from "../libraryEntry/activatableSkill/entity.ts" -import { parensIf } from "../libraryEntry/activatableSkill/parensIf.ts" -import { getTextForCantripRange } from "../libraryEntry/activatableSkill/range.ts" -import { getTextForTargetCategory } from "../libraryEntry/activatableSkill/targetCategory.ts" -import { ResponsiveTextSize } from "../libraryEntry/responsiveText.ts" import { SpellsSortOrder } from "../sortOrders.ts" -import { - getTextForFastOneTimePerformanceParameters, - getTextForFastSustainedPerformanceParameters, - getTextForSlowOneTimePerformanceParameters, - getTextForSlowSustainedPerformanceParameters, -} from "./activatableSkill.ts" import { cursesImprovementCost, dominationRitualsImprovementCost, @@ -64,7 +45,6 @@ import { isRatedActive, isRatedWithEnhancementsActive, } from "./ratedEntry.ts" -import { getTextForCheck } from "./skillCheck.ts" import { DisplayedActiveSpellwork } from "./spellActive.ts" import { DisplayedInactiveSpellwork } from "./spellInactive.ts" @@ -720,423 +700,3 @@ export const filterAndSortDisplayed = < })(), ) } - -const getTextForProperty = ( - deps: { - translate: Translate - translateMap: TranslateMap - getPropertyById: GetById.Static.Property - }, - value: PropertyReference, -): LibraryEntryContent => { - const text = (() => { - const staticEntry = deps.getPropertyById(value.id.property) - const staticEntryTranslation = deps.translateMap(staticEntry?.translations) - - if (staticEntryTranslation === undefined) { - return "" - } - - return staticEntryTranslation.name - })() - - return { - label: deps.translate("Property"), - value: text, - } -} - -const getTextForTraditions = ( - deps: { - translate: Translate - translateMap: TranslateMap - localeCompare: Compare - getMagicalTraditionById: GetById.Static.MagicalTradition - }, - value: Traditions, -): LibraryEntryContent => { - const text = (() => { - switch (value.tag) { - case "General": - return deps.translate("General") - case "Specific": - return value.specific - .map(trad => - deps.translateMap(deps.getMagicalTraditionById(trad.magical_tradition)?.translations), - ) - .filter(isNotNullish) - .map(trad => trad.name_for_arcane_spellworks ?? trad.name) - .sort(deps.localeCompare) - .join(", ") - default: - return assertExhaustive(value) - } - })() - - return { - label: deps.translate("Traditions"), - value: text, - } -} - -const getTraditionNameForArcaneSpellworksById = ( - ref: MagicalTraditionReference, - getMagicalTraditionById: GetById.Static.MagicalTradition, - translateMap: TranslateMap, -) => { - const translation = translateMap(getMagicalTraditionById(ref.id.magical_tradition)?.translations) - return translation?.name_for_arcane_spellworks ?? translation?.name -} - -/** - * Get a JSON representation of the rules text for a cantrip. - */ -export const getCantripLibraryEntry = createLibraryEntryCreator< - Cantrip, - { - getTargetCategoryById: GetById.Static.TargetCategory - getPropertyById: GetById.Static.Property - getMagicalTraditionById: GetById.Static.MagicalTradition - getCurriculumById: GetById.Static.Curriculum - } ->( - (entry, { getTargetCategoryById, getPropertyById, getMagicalTraditionById, getCurriculumById }) => - ({ translate, translateMap, localeCompare }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const range = getTextForCantripRange({ translate }, entry.parameters.range, { - responsiveText: ResponsiveTextSize.Full, - }) - - const duration = getTextForCantripDuration( - { translate, translateMap }, - entry.parameters.duration, - { - responsiveText: ResponsiveTextSize.Full, - }, - ) - - return { - title: translation.name, - className: "cantrip", - content: [ - { - label: translate("Effect"), - value: translation.effect, - }, - { - label: translate("Range"), - value: range !== translation.range ? `***${range}*** (${translation.range})` : range, - }, - { - label: translate("Duration"), - value: - duration !== translation.duration - ? `***${duration}*** (${translation.duration})` - : duration, - }, - getTextForTargetCategory( - { translate, translateMap, getTargetCategoryById }, - entry.target, - ), - getTextForProperty({ translate, translateMap, getPropertyById }, entry.property), - mapNullable(entry.note, note => ({ - label: translate("Note"), - value: (() => { - switch (note.tag) { - case "Common": - return note.common.list - .map(academyOrTradition => { - switch (academyOrTradition.tag) { - case "Academy": - return translateMap( - getCurriculumById(academyOrTradition.academy.id.curriculum) - ?.translations, - )?.name - case "Tradition": { - return mapNullable( - getTraditionNameForArcaneSpellworksById( - academyOrTradition.tradition, - getMagicalTraditionById, - translateMap, - ), - name => - name + - parensIf( - translateMap(academyOrTradition.tradition.translations)?.note, - ), - ) - } - default: - return assertExhaustive(academyOrTradition) - } - }) - .filter(isNotNullish) - .sort(localeCompare) - .join(", ") - - case "Exclusive": - return note.exclusive.traditions - .map(tradition => - getTraditionNameForArcaneSpellworksById( - tradition, - getMagicalTraditionById, - translateMap, - ), - ) - .filter(isNotNullish) - .sort(localeCompare) - .join(", ") - - default: - return assertExhaustive(note) - } - })(), - })), - ], - src: entry.src, - } - }, -) - -/** - * Get a JSON representation of the rules text for a skill. - */ -export const getSpellLibraryEntry = createLibraryEntryCreator< - Spell, - { - getAttributeById: GetById.Static.Attribute - getDerivedCharacteristicById: GetById.Static.DerivedCharacteristic - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - getTargetCategoryById: GetById.Static.TargetCategory - getPropertyById: GetById.Static.Property - getMagicalTraditionById: GetById.Static.MagicalTradition - } ->( - ( - entry, - { - getAttributeById, - getDerivedCharacteristicById, - getSkillModificationLevelById, - getTargetCategoryById, - getPropertyById, - getMagicalTraditionById, - }, - ) => - ({ translate, translateMap, localeCompare }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const { castingTime, cost, range, duration } = (() => { - switch (entry.parameters.tag) { - case "OneTime": - return getTextForFastOneTimePerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.one_time, - { - entity: Entity.Spell, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - case "Sustained": - return getTextForFastSustainedPerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.sustained, - { - entity: Entity.Spell, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - default: - return assertExhaustive(entry.parameters) - } - })() - - return { - title: translation.name, - className: "spell", - content: [ - getTextForCheck({ translate, translateMap, getAttributeById }, entry.check, { - value: entry.check_penalty, - responsiveText: ResponsiveTextSize.Full, - getDerivedCharacteristicById, - }), - ...getTextForEffect(translation.effect, translate), - { - label: translate("Casting Time"), - value: - castingTime !== translation.casting_time.full - ? `***${castingTime}*** (${translation.casting_time.full})` - : castingTime, - }, - { - label: translate("AE Cost"), - value: - cost !== translation.cost.full ? `***${cost}*** (${translation.cost.full})` : cost, - }, - { - label: translate("Range"), - value: - range !== translation.range.full - ? `***${range}*** (${translation.range.full})` - : range, - }, - { - label: translate("Duration"), - value: - duration !== translation.duration.full - ? `***${duration}*** (${translation.duration.full})` - : duration, - }, - getTextForTargetCategory( - { translate, translateMap, getTargetCategoryById }, - entry.target, - ), - getTextForProperty({ translate, translateMap, getPropertyById }, entry.property), - getTextForTraditions( - { translate, translateMap, localeCompare, getMagicalTraditionById }, - entry.traditions, - ), - createImprovementCost(translate, entry.improvement_cost), - ], - src: entry.src, - } - }, -) - -/** - * Get a JSON representation of the rules text for a ritual. - */ -export const getRitualLibraryEntry = createLibraryEntryCreator< - Ritual, - { - getAttributeById: GetById.Static.Attribute - getDerivedCharacteristicById: GetById.Static.DerivedCharacteristic - getSkillModificationLevelById: GetById.Static.SkillModificationLevel - getTargetCategoryById: GetById.Static.TargetCategory - getPropertyById: GetById.Static.Property - getMagicalTraditionById: GetById.Static.MagicalTradition - } ->( - ( - entry, - { - getAttributeById, - getDerivedCharacteristicById, - getSkillModificationLevelById, - getTargetCategoryById, - getPropertyById, - getMagicalTraditionById, - }, - ) => - ({ translate, translateMap, localeCompare }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - const { castingTime, cost, range, duration } = (() => { - switch (entry.parameters.tag) { - case "OneTime": - return getTextForSlowOneTimePerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.one_time, - { - entity: Entity.Ritual, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - case "Sustained": - return getTextForSlowSustainedPerformanceParameters( - { - getSkillModificationLevelById, - translate, - translateMap, - }, - entry.parameters.sustained, - { - entity: Entity.Ritual, - responsiveText: ResponsiveTextSize.Full, - }, - ) - - default: - return assertExhaustive(entry.parameters) - } - })() - - return { - title: translation.name, - className: "ritual", - content: [ - getTextForCheck({ translate, translateMap, getAttributeById }, entry.check, { - value: entry.check_penalty, - responsiveText: ResponsiveTextSize.Full, - getDerivedCharacteristicById, - }), - ...getTextForEffect(translation.effect, translate), - { - label: translate("Ritual Time"), - value: - castingTime !== translation.casting_time.full - ? `***${castingTime}*** (${translation.casting_time.full})` - : castingTime, - }, - { - label: translate("AE Cost"), - value: - cost !== translation.cost.full ? `***${cost}*** (${translation.cost.full})` : cost, - }, - { - label: translate("Range"), - value: - range !== translation.range.full - ? `***${range}*** (${translation.range.full})` - : range, - }, - { - label: translate("Duration"), - value: - duration !== translation.duration.full - ? `***${duration}*** (${translation.duration.full})` - : duration, - }, - getTextForTargetCategory( - { translate, translateMap, getTargetCategoryById }, - entry.target, - ), - getTextForProperty({ translate, translateMap, getPropertyById }, entry.property), - getTextForTraditions( - { translate, translateMap, localeCompare, getMagicalTraditionById }, - entry.traditions, - ), - createImprovementCost(translate, entry.improvement_cost), - ], - src: entry.src, - } - }, -) diff --git a/src/shared/domain/rules/focusRule.ts b/src/shared/domain/rules/focusRule.ts index ca5abbc85..a3a6a8175 100644 --- a/src/shared/domain/rules/focusRule.ts +++ b/src/shared/domain/rules/focusRule.ts @@ -1,7 +1,4 @@ -import { FocusRule } from "optolith-database-schema/types/rule/FocusRule" -import { romanize } from "../../utils/roman.ts" import { GetById } from "../getTypes.ts" -import { createLibraryEntryCreator } from "../libraryEntry.ts" import { RuleDependency } from "./ruleDependency.ts" /** @@ -37,25 +34,3 @@ export const isFocusRuleActive = ( getDynamicFocusRuleById: GetById.Dynamic.FocusRule, focusRuleId: number, ): boolean => getDynamicFocusRuleById(focusRuleId)?.active ?? false - -/** - * Get a JSON representation of the rules text for a focus rule. - */ -export const getFocusRuleLibraryEntry = createLibraryEntryCreator< - FocusRule, - { getSubjectById: GetById.Static.Subject } ->((entry, { getSubjectById }) => ({ translateMap }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - return { - title: `${translation.name} (${romanize(entry.level)})`, - subtitle: translateMap(getSubjectById(entry.subject.id.subject)?.translations)?.name, - className: "focus-rule", - content: [{ value: translation.description }], - src: entry.src, - } -}) diff --git a/src/shared/domain/rules/optionalRule.ts b/src/shared/domain/rules/optionalRule.ts index 9fab85e3a..5a1eabcbc 100644 --- a/src/shared/domain/rules/optionalRule.ts +++ b/src/shared/domain/rules/optionalRule.ts @@ -1,6 +1,4 @@ -import { OptionalRule } from "optolith-database-schema/types/rule/OptionalRule" import { GetById } from "../getTypes.ts" -import { createLibraryEntryCreator } from "../libraryEntry.ts" import { RuleDependency } from "./ruleDependency.ts" /** @@ -41,24 +39,3 @@ export const isOptionalRuleActive = ( getDynamicOptionalRuleById: GetById.Dynamic.OptionalRule, optionalRuleId: number, ): boolean => getDynamicOptionalRuleById(optionalRuleId)?.active ?? false - -/** - * Get a JSON representation of the rules text for an optional rule. - */ -export const getOptionalRuleLibraryEntry = createLibraryEntryCreator( - entry => - ({ translateMap }) => { - const translation = translateMap(entry.translations) - - if (translation === undefined) { - return undefined - } - - return { - title: translation.name, - className: "optional-rule", - content: [{ value: translation.description }], - src: entry.src, - } - }, -) diff --git a/src/shared/domain/sources/pages.test.ts b/src/shared/domain/sources/pages.test.ts deleted file mode 100644 index 7fb98ee63..000000000 --- a/src/shared/domain/sources/pages.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import assert from "node:assert/strict" -import { describe, it } from "node:test" -import { comparePage } from "./pages.ts" - -describe("comparePage", () => { - it("returns 0 if the pages are equal", () => { - assert.equal( - comparePage( - { tag: "InsideCoverBack", inside_cover_back: {} }, - { tag: "InsideCoverBack", inside_cover_back: {} }, - ), - 0, - ) - assert.equal( - comparePage( - { tag: "InsideCoverFront", inside_cover_front: {} }, - { tag: "InsideCoverFront", inside_cover_front: {} }, - ), - 0, - ) - assert.equal( - comparePage({ tag: "Numbered", numbered: 42 }, { tag: "Numbered", numbered: 42 }), - 0, - ) - }) - - it("returns a negative number if first should be sorted before the second", () => { - assert.equal( - comparePage( - { tag: "InsideCoverFront", inside_cover_front: {} }, - { tag: "InsideCoverBack", inside_cover_back: {} }, - ), - -1, - ) - assert.equal( - comparePage( - { tag: "InsideCoverFront", inside_cover_front: {} }, - { tag: "Numbered", numbered: 42 }, - ), - -1, - ) - assert.equal( - comparePage( - { tag: "Numbered", numbered: 42 }, - { tag: "InsideCoverBack", inside_cover_back: {} }, - ), - -1, - ) - assert.equal( - comparePage({ tag: "Numbered", numbered: 24 }, { tag: "Numbered", numbered: 42 }), - -18, - ) - }) - - it("returns a positive number if first should be sorted after the second", () => { - assert.equal( - comparePage( - { tag: "InsideCoverBack", inside_cover_back: {} }, - { tag: "InsideCoverFront", inside_cover_front: {} }, - ), - 1, - ) - assert.equal( - comparePage( - { tag: "Numbered", numbered: 42 }, - { tag: "InsideCoverFront", inside_cover_front: {} }, - ), - 1, - ) - assert.equal( - comparePage( - { tag: "InsideCoverBack", inside_cover_back: {} }, - { tag: "Numbered", numbered: 42 }, - ), - 1, - ) - assert.equal( - comparePage({ tag: "Numbered", numbered: 42 }, { tag: "Numbered", numbered: 24 }), - 18, - ) - }) -}) diff --git a/src/shared/domain/sources/pages.ts b/src/shared/domain/sources/pages.ts deleted file mode 100644 index 37425674d..000000000 --- a/src/shared/domain/sources/pages.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { - Page, - PageRange as RawPageRange, - SimpleOccurrence, -} from "optolith-database-schema/types/source/_PublicationRef" -import { range } from "../../utils/array.ts" -import { Compare } from "../../utils/compare.ts" -import { assertExhaustive } from "../../utils/typeSafety.ts" - -/** - * A comparison function for two pages. - */ -export const comparePage: Compare = (a, b) => { - switch (a.tag) { - case "InsideCoverFront": - return b.tag === "InsideCoverFront" ? 0 : -1 - case "InsideCoverBack": - return b.tag === "InsideCoverBack" ? 0 : 1 - case "Numbered": - return b.tag === "Numbered" ? a.numbered - b.numbered : b.tag === "InsideCoverFront" ? 1 : -1 - default: - return assertExhaustive(a) - } -} - -/** - * Checks if two pages are equal. - */ -export const equalsPage = (a: Page, b: Page): boolean => comparePage(a, b) === 0 - -/** - * Returns the successor of a page. - */ -export const succ = (page: Page): Page => { - switch (page.tag) { - case "InsideCoverFront": - return { tag: "Numbered", numbered: 1 } - case "InsideCoverBack": - return { tag: "InsideCoverFront", inside_cover_front: {} } - case "Numbered": - return { tag: "Numbered", numbered: page.numbered + 1 } - default: - return assertExhaustive(page) - } -} - -/** - * Creates a page object for a page number. - */ -export const numberToPage = (number: number): Page => ({ tag: "Numbered", numbered: number }) - -/** - * A range of pages, including the first and last page, if the range includes - * more than on page. - */ -export type PageRange = { - firstPage: Page - lastPage?: Page -} - -/** - * Converts a numeric range to a page object range. - */ -export const numberRangeToPageRange = (numberRange: SimpleOccurrence): PageRange => - numberRange.last_page === undefined - ? { firstPage: numberToPage(numberRange.first_page) } - : { - firstPage: numberToPage(numberRange.first_page), - lastPage: numberToPage(numberRange.last_page), - } - -/** - * Converts a page object range from the database to a local page object range. - */ -export const fromRawPageRange = (pageRange: RawPageRange): PageRange => - pageRange.last_page === undefined - ? { firstPage: pageRange.first_page } - : { - firstPage: pageRange.first_page, - lastPage: pageRange.last_page, - } - -/** - * Sorts and combines page ranges while removing duplicates. - */ -export const normalizePageRanges = (ranges: PageRange[]): PageRange[] => - ranges - .flatMap(({ firstPage, lastPage = firstPage }): Page[] => { - if (firstPage.tag === "Numbered" && lastPage.tag === "Numbered") { - return range(firstPage.numbered, lastPage.numbered).map(numbered => ({ - tag: "Numbered", - numbered, - })) - } else { - return [firstPage, lastPage] - } - }) - .filter((page, i, pages) => pages.findIndex(p => equalsPage(page, p)) === i) - .sort(comparePage) - .reduce((acc, page): PageRange[] => { - const lastRange = acc[acc.length - 1] - - if (lastRange === undefined) { - return [{ firstPage: page }] - } - - const { firstPage, lastPage = firstPage } = lastRange - - if (equalsPage(page, succ(lastPage))) { - return [...acc.slice(0, -1), { firstPage, lastPage: page }] - } - - return [...acc, { firstPage: page }] - }, [])