Migrate files from @elyukai/optolith-client

This commit is contained in:
Lukas Obermann
2024-10-04 22:58:18 +02:00
parent 35c719c702
commit b6fbdb9c53
28 changed files with 3894 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
import { mapNullable } from "@optolith/helpers/nullable"
import { CloseCombatTechnique } from "optolith-database-schema/types/CombatTechnique_Close"
import { RangedCombatTechnique } from "optolith-database-schema/types/CombatTechnique_Ranged"
import { GetById } from "../helpers/getTypes.js"
import { createLibraryEntryCreator } from "../libraryEntry.js"
import { createImprovementCost } from "./partial/rated/improvementCost.js"
/**
* 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,
}
})
+52
View File
@@ -0,0 +1,52 @@
import { ExperienceLevel } from "optolith-database-schema/types/ExperienceLevel"
import { createLibraryEntryCreator } from "../libraryEntry.js"
/**
* Get a JSON representation of the rules text for an experience level.
*/
export const getExperienceLevelLibraryEntry =
createLibraryEntryCreator<ExperienceLevel>(
(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,
},
],
}
}
)
+409
View File
@@ -0,0 +1,409 @@
import { Compare } from "@optolith/helpers/compare"
import { isNotNullish } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Blessing } from "optolith-database-schema/types/Blessing"
import { Ceremony } from "optolith-database-schema/types/Ceremony"
import { DerivedCharacteristic } from "optolith-database-schema/types/DerivedCharacteristic"
import { LiturgicalChant } from "optolith-database-schema/types/LiturgicalChant"
import { SkillTradition } from "optolith-database-schema/types/_Blessed"
import { AspectReference } from "optolith-database-schema/types/_SimpleReferences"
import { GetById } from "../helpers/getTypes.js"
import { Translate, TranslateMap } from "../helpers/translate.js"
import {
createLibraryEntryCreator,
LibraryEntryContent,
} from "../libraryEntry.js"
import { getTextForBlessingDuration } from "./partial/rated/activatable/duration.js"
import { getTextForEffect } from "./partial/rated/activatable/effect.js"
import { Entity } from "./partial/rated/activatable/entity.js"
import {
getTextForFastOneTimePerformanceParameters,
getTextForFastSustainedPerformanceParameters,
getTextForSlowOneTimePerformanceParameters,
getTextForSlowSustainedPerformanceParameters,
} from "./partial/rated/activatable/index.js"
import { getTextForBlessingRange } from "./partial/rated/activatable/range.js"
import { getTextForTargetCategory } from "./partial/rated/activatable/targetCategory.js"
import { createImprovementCost } from "./partial/rated/improvementCost.js"
import { getTextForCheck } from "./partial/rated/skillCheck.js"
import { ResponsiveTextSize } from "./partial/responsiveText.js"
const getTextForTraditions = (
deps: {
translate: Translate
translateMap: TranslateMap
localeCompare: Compare<string>
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
getSpirit: () => DerivedCharacteristic | undefined
getToughness: () => DerivedCharacteristic | undefined
getSkillModificationLevelById: GetById.Static.SkillModificationLevel
getTargetCategoryById: GetById.Static.TargetCategory
getBlessedTraditionById: GetById.Static.BlessedTradition
getAspectById: GetById.Static.Aspect
}
>(
(
entry,
{
getAttributeById,
getSpirit,
getToughness,
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,
getSpirit,
getToughness,
}
),
...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
getSpirit: () => DerivedCharacteristic | undefined
getToughness: () => DerivedCharacteristic | undefined
getSkillModificationLevelById: GetById.Static.SkillModificationLevel
getTargetCategoryById: GetById.Static.TargetCategory
getBlessedTraditionById: GetById.Static.BlessedTradition
getAspectById: GetById.Static.Aspect
}
>(
(
entry,
{
getAttributeById,
getSpirit,
getToughness,
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,
getSpirit,
getToughness,
}
),
...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,
}
}
)
@@ -0,0 +1,197 @@
import { isNotNullish, mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
CastingTime,
CastingTimeDuringLovemaking,
FastCastingTime,
FastSkillNonModifiableCastingTime,
ModifiableCastingTime,
SlowCastingTime,
SlowSkillNonModifiableCastingTime,
} from "optolith-database-schema/types/_ActivatableSkillCastingTime"
import { GetById } from "../../../../helpers/getTypes.js"
import { Translate } from "../../../../helpers/translate.js"
import { ResponsiveTextSize } from "../../responsiveText.js"
import { MISSING_VALUE } from "../../unknown.js"
import { Entity } from "./entity.js"
import { ModifiableParameter } from "./modifiableParameter.js"
import { getTextForNonModifiableSuffix } from "./nonModifiable.js"
import { Speed } from "./speed.js"
import { formatTimeSpan } from "./units.js"
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 = <NonModifiable extends object>(
deps: {
getSkillModificationLevelById: GetById.Static.SkillModificationLevel
translate: Translate
},
value: CastingTime<NonModifiable>,
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(" / ")
@@ -0,0 +1,31 @@
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { translateMock } from "../../../../helpers/translate.js"
import { getTextForCheckResultBased } from "./checkResultBased.js"
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"
)
})
})
@@ -0,0 +1,48 @@
import { mapNullableDefault } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
CheckResultArithmetic,
CheckResultBased,
CheckResultValue,
} from "optolith-database-schema/types/_ActivatableSkillCheckResultBased"
import { Translate } from "../../../../helpers/translate.js"
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,
""
)
@@ -0,0 +1,490 @@
import { mapNullable, mapNullableDefault } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
CostMap,
IndefiniteOneTimeCost,
ModifiableOneTimeCost,
MultipleOneTimeCosts,
NonModifiableOneTimeCost,
NonModifiableOneTimeCostPerCountable,
OneTimeCost,
SingleOneTimeCost,
SustainedCost,
} from "optolith-database-schema/types/_ActivatableSkillCost"
import { GetById } from "../../../../helpers/getTypes.js"
import { Translate, TranslateMap } from "../../../../helpers/translate.js"
import {
getResponsiveText,
getResponsiveTextOptional,
replaceTextIfRequested,
responsive,
ResponsiveTextSize,
} from "../../responsiveText.js"
import { MISSING_VALUE } from "../../unknown.js"
import { Entity } from "./entity.js"
import { ModifiableParameter } from "./modifiableParameter.js"
import { getTextForNonModifiableSuffix } from "./nonModifiable.js"
import { getModifiableBySpeed, Speed } from "./speed.js"
import { formatCost, formatTimeSpan } from "./units.js"
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)
}
}
@@ -0,0 +1,266 @@
import { mapNullableDefault } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
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 { Translate, TranslateMap } from "../../../../helpers/translate.js"
import {
getResponsiveText,
replaceTextIfRequested,
responsive,
ResponsiveTextSize,
} from "../../responsiveText.js"
import { getTextForCheckResultBased } from "./checkResultBased.js"
import { getTextForIsMaximum } from "./isMaximum.js"
import { formatTimeSpan } from "./units.js"
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)
}
}
@@ -0,0 +1,62 @@
import { filterNonNullable } from "@optolith/helpers/array"
import { mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Effect } from "optolith-database-schema/types/_ActivatableSkillEffect"
import { Translate } from "../../../../helpers/translate.js"
import { LibraryEntryContent } from "../../../../libraryEntry.js"
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)
}
}
@@ -0,0 +1,11 @@
/**
* The entity type of an activatable skill.
*/
export enum Entity {
Cantrip,
Spell,
Ritual,
Blessing,
LiturgicalChant,
Ceremony,
}
@@ -0,0 +1,143 @@
import {
FastOneTimePerformanceParameters,
FastSustainedPerformanceParameters,
SlowOneTimePerformanceParameters,
SlowSustainedPerformanceParameters,
} from "optolith-database-schema/types/_ActivatableSkill"
import { GetById } from "../../../../helpers/getTypes.js"
import { Translate, TranslateMap } from "../../../../helpers/translate.js"
import { ResponsiveTextSize } from "../../responsiveText.js"
import {
getTextForFastCastingTime,
getTextForSlowCastingTime,
} from "./castingTime.js"
import { getTextForOneTimeCost, getTextForSustainedCost } from "./cost.js"
import {
getTextForDurationForOneTime,
getTextForDurationForSustained,
} from "./duration.js"
import { Entity } from "./entity.js"
import { getTextForActivatableSkillRange } from "./range.js"
import { Speed } from "./speed.js"
/**
* 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),
})
@@ -0,0 +1,21 @@
import { Translate } from "../../../../helpers/translate.js"
import { responsive, ResponsiveTextSize } from "../../responsiveText.js"
/**
* 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. ")
)
}
@@ -0,0 +1,8 @@
/**
* A parameter that is designed to be modifiable.
*/
export enum ModifiableParameter {
CastingTime,
Cost,
Range,
}
@@ -0,0 +1,107 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Translate } from "../../../../helpers/translate.js"
import { ResponsiveTextSize } from "../../responsiveText.js"
import { Entity } from "./entity.js"
import { ModifiableParameter } from "./modifiableParameter.js"
/**
* 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 spells casting time)"
)
case ModifiableParameter.Cost:
return translate(
" (you cannot use a modification on this spells cost)"
)
case ModifiableParameter.Range:
return translate(
" (you cannot use a modification on this spells range)"
)
default:
return assertExhaustive(param)
}
case Entity.Ritual:
switch (param) {
case ModifiableParameter.CastingTime:
return translate(
" (you cannot use a modification on this rituals ritual time)"
)
case ModifiableParameter.Cost:
return translate(
" (you cannot use a modification on this rituals cost)"
)
case ModifiableParameter.Range:
return translate(
" (you cannot use a modification on this rituals range)"
)
default:
return assertExhaustive(param)
}
case Entity.LiturgicalChant:
switch (param) {
case ModifiableParameter.CastingTime:
return translate(
" (you cannot use a modification on this chants liturgical time)"
)
case ModifiableParameter.Cost:
return translate(
" (you cannot use a modification on this chants cost)"
)
case ModifiableParameter.Range:
return translate(
" (you cannot use a modification on this chants range)"
)
default:
return assertExhaustive(param)
}
case Entity.Ceremony:
switch (param) {
case ModifiableParameter.CastingTime:
return translate(
" (you cannot use a modification on this ceremonys ceremonial time)"
)
case ModifiableParameter.Cost:
return translate(
" (you cannot use a modification on this ceremonys cost)"
)
case ModifiableParameter.Range:
return translate(
" (you cannot use a modification on this ceremonys range)"
)
default:
return assertExhaustive(param)
}
case Entity.Cantrip:
case Entity.Blessing:
return ""
default:
return assertExhaustive(entity)
}
}
@@ -0,0 +1,16 @@
/**
* 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})`
@@ -0,0 +1,242 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
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 { GetById } from "../../../../helpers/getTypes.js"
import { Translate, TranslateMap } from "../../../../helpers/translate.js"
import {
getResponsiveText,
getResponsiveTextOptional,
responsive,
ResponsiveTextSize,
} from "../../responsiveText.js"
import { MISSING_VALUE } from "../../unknown.js"
import { getTextForCheckResultBased } from "./checkResultBased.js"
import { Entity } from "./entity.js"
import { getTextForIsMaximum } from "./isMaximum.js"
import { ModifiableParameter } from "./modifiableParameter.js"
import { getTextForNonModifiableSuffix } from "./nonModifiable.js"
import { Speed } from "./speed.js"
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)
}
}
@@ -0,0 +1,33 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
FastSkillModificationLevelConfig,
SkillModificationLevel,
SlowSkillModificationLevelConfig,
} from "optolith-database-schema/types/SkillModificationLevel"
/**
* 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 = <T>(
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)
}
}
@@ -0,0 +1,59 @@
import { mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { TargetCategory } from "optolith-database-schema/types/_ActivatableSkillTargetCategory"
import { GetById } from "../../../../helpers/getTypes.js"
import { Translate, TranslateMap } from "../../../../helpers/translate.js"
import { LibraryEntryContent } from "../../../../libraryEntry.js"
import { MISSING_VALUE } from "../../unknown.js"
import { appendInParens } from "./parensIf.js"
/**
* 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(", "),
})
@@ -0,0 +1,127 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Translate } from "../../../../helpers/translate.js"
import { responsive, ResponsiveTextSize } from "../../responsiveText.js"
import { Entity } from "./entity.js"
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)
}
}
@@ -0,0 +1,14 @@
import { ImprovementCost as RawImprovementCost } from "optolith-database-schema/types/_ImprovementCost"
import { Translate } from "../../../helpers/translate.js"
import { LibraryEntryContent } from "../../../libraryEntry.js"
/**
* Returns the improvement cost as an inline library property.
*/
export const createImprovementCost = (
translate: Translate,
improvementCost: RawImprovementCost
): LibraryEntryContent => ({
label: translate("Improvement Cost"),
value: improvementCost,
})
+134
View File
@@ -0,0 +1,134 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
SkillCheck,
SkillCheckPenalty,
} from "optolith-database-schema/types/_SkillCheck"
import { DerivedCharacteristic } from "optolith-database-schema/types/DerivedCharacteristic"
import { GetById } from "../../../helpers/getTypes.js"
import { Translate, TranslateMap } from "../../../helpers/translate.js"
import { LibraryEntryContent } from "../../../libraryEntry.js"
import { responsive, ResponsiveTextSize } from "../responsiveText.js"
/**
* 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
getSpirit: () => DerivedCharacteristic | undefined
getToughness: () => DerivedCharacteristic | undefined
}
): 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 } = checkPenalty
const getDerivedCharacteristicTranslation = (
getDerivedCharacteristic: () => DerivedCharacteristic | undefined
) => deps.translateMap(getDerivedCharacteristic()?.translations)
const getSpiritTranslation = () =>
getDerivedCharacteristicTranslation(checkPenalty.getSpirit)
const getToughnessTranslation = () =>
getDerivedCharacteristicTranslation(checkPenalty.getToughness)
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)
)
})(),
})
+89
View File
@@ -0,0 +1,89 @@
import { mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { LocaleMap } from "optolith-database-schema/types/_LocaleMap"
import {
ResponsiveText,
ResponsiveTextOptional,
ResponsiveTextReplace,
} from "optolith-database-schema/types/_ResponsiveText"
import { TranslateMap } from "../../helpers/translate.js"
import { MISSING_VALUE } from "./unknown.js"
/**
* 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 = <T, A extends unknown[]>(
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
+4
View File
@@ -0,0 +1,4 @@
/**
* String to display when a translation that should be present is missing.
*/
export const MISSING_VALUE = "???"
+136
View File
@@ -0,0 +1,136 @@
import { isNotNullish } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { NewApplicationsAndUsesCache } from "optolith-database-schema/cache/newApplicationsAndUses"
import { Skill } from "optolith-database-schema/types/Skill"
import { All, GetById } from "../helpers/getTypes.js"
import { createLibraryEntryCreator } from "../libraryEntry.js"
import { createImprovementCost } from "./partial/rated/improvementCost.js"
import { getTextForCheck } from "./partial/rated/skillCheck.js"
/**
* 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),
],
}
}
)
+502
View File
@@ -0,0 +1,502 @@
import { Compare } from "@optolith/helpers/compare"
import { isNotNullish, mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Cantrip } from "optolith-database-schema/types/Cantrip"
import { DerivedCharacteristic } from "optolith-database-schema/types/DerivedCharacteristic"
import { Ritual } from "optolith-database-schema/types/Ritual"
import { Spell } from "optolith-database-schema/types/Spell"
import {
MagicalTraditionReference,
PropertyReference,
} from "optolith-database-schema/types/_SimpleReferences"
import { Traditions } from "optolith-database-schema/types/_Spellwork"
import { GetById } from "../helpers/getTypes.js"
import { Translate, TranslateMap } from "../helpers/translate.js"
import {
createLibraryEntryCreator,
LibraryEntryContent,
} from "../libraryEntry.js"
import { getTextForCantripDuration } from "./partial/rated/activatable/duration.js"
import { getTextForEffect } from "./partial/rated/activatable/effect.js"
import { Entity } from "./partial/rated/activatable/entity.js"
import {
getTextForFastOneTimePerformanceParameters,
getTextForFastSustainedPerformanceParameters,
getTextForSlowOneTimePerformanceParameters,
getTextForSlowSustainedPerformanceParameters,
} from "./partial/rated/activatable/index.js"
import { parensIf } from "./partial/rated/activatable/parensIf.js"
import { getTextForCantripRange } from "./partial/rated/activatable/range.js"
import { getTextForTargetCategory } from "./partial/rated/activatable/targetCategory.js"
import { createImprovementCost } from "./partial/rated/improvementCost.js"
import { getTextForCheck } from "./partial/rated/skillCheck.js"
import { ResponsiveTextSize } from "./partial/responsiveText.js"
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<string>
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
getSpirit: () => DerivedCharacteristic | undefined
getToughness: () => DerivedCharacteristic | undefined
getSkillModificationLevelById: GetById.Static.SkillModificationLevel
getTargetCategoryById: GetById.Static.TargetCategory
getPropertyById: GetById.Static.Property
getMagicalTraditionById: GetById.Static.MagicalTradition
}
>(
(
entry,
{
getAttributeById,
getSpirit,
getToughness,
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,
getSpirit,
getToughness,
}
),
...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
getSpirit: () => DerivedCharacteristic | undefined
getToughness: () => DerivedCharacteristic | undefined
getSkillModificationLevelById: GetById.Static.SkillModificationLevel
getTargetCategoryById: GetById.Static.TargetCategory
getPropertyById: GetById.Static.Property
getMagicalTraditionById: GetById.Static.MagicalTradition
}
>(
(
entry,
{
getAttributeById,
getSpirit,
getToughness,
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,
getSpirit,
getToughness,
}
),
...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,
}
}
)
+497
View File
@@ -0,0 +1,497 @@
/* eslint-disable jsdoc/require-jsdoc */
/* eslint-disable @typescript-eslint/no-namespace */
import { config as advantageConfig } from "optolith-database-schema/types/Advantage"
import { config as animalDiseaseConfig } from "optolith-database-schema/types/AnimalDisease"
import { config as animalTypeConfig } from "optolith-database-schema/types/AnimalType"
import { config as arcaneBardTraditionConfig } from "optolith-database-schema/types/ArcaneBardTradition"
import { config as arcaneDancerTraditionConfig } from "optolith-database-schema/types/ArcaneDancerTradition"
import { config as aspectConfig } from "optolith-database-schema/types/Aspect"
import { config as attributeConfig } from "optolith-database-schema/types/Attribute"
import { config as blessingConfig } from "optolith-database-schema/types/Blessing"
import { config as cantripConfig } from "optolith-database-schema/types/Cantrip"
import { config as ceremonyConfig } from "optolith-database-schema/types/Ceremony"
import { config as closeCombatTechniqueConfig } from "optolith-database-schema/types/CombatTechnique_Close"
import { config as rangedCombatTechniqueConfig } from "optolith-database-schema/types/CombatTechnique_Ranged"
import { config as conditionConfig } from "optolith-database-schema/types/Condition"
import { config as continentConfig } from "optolith-database-schema/types/Continent"
import { config as cultureConfig } from "optolith-database-schema/types/Culture"
import { config as derivedCharacteristicConfig } from "optolith-database-schema/types/DerivedCharacteristic"
import { config as disadvantageConfig } from "optolith-database-schema/types/Disadvantage"
import { config as diseaseConfig } from "optolith-database-schema/types/Disease"
import { config as elementConfig } from "optolith-database-schema/types/Element"
import { config as experienceLevelConfig } from "optolith-database-schema/types/ExperienceLevel"
import { config as eyeColorConfig } from "optolith-database-schema/types/EyeColor"
import { config as familiarsTrickConfig } from "optolith-database-schema/types/FamiliarsTrick"
import { config as hairColorConfig } from "optolith-database-schema/types/HairColor"
import { config as kirchenpraegungConfig } from "optolith-database-schema/types/Kirchenpraegung"
import { config as curriculumConfig } from "optolith-database-schema/types/Lessons_Curriculum"
import { config as guidelineConfig } from "optolith-database-schema/types/Lessons_Guideline"
import { config as liturgicalChantConfig } from "optolith-database-schema/types/LiturgicalChant"
import { config as localeConfig } from "optolith-database-schema/types/Locale"
import { config as metaConditionConfig } from "optolith-database-schema/types/MetaCondition"
import { config as pactCategoryConfig } from "optolith-database-schema/types/PactCategory"
import { config as patronConfig } from "optolith-database-schema/types/Patron"
import { config as patronCategoryConfig } from "optolith-database-schema/types/PatronCategory"
import { config as personalityTraitConfig } from "optolith-database-schema/types/PersonalityTrait"
import { config as professionConfig } from "optolith-database-schema/types/Profession"
import { config as propertyConfig } from "optolith-database-schema/types/Property"
import { config as raceConfig } from "optolith-database-schema/types/Race"
import { config as regionConfig } from "optolith-database-schema/types/Region"
import { config as ritualConfig } from "optolith-database-schema/types/Ritual"
import { config as serviceConfig } from "optolith-database-schema/types/Service"
import { config as sexPracticeConfig } from "optolith-database-schema/types/SexPractice"
import { config as skillConfig } from "optolith-database-schema/types/Skill"
import { config as skillGroupConfig } from "optolith-database-schema/types/SkillGroup"
import { config as skillModificationLevelConfig } from "optolith-database-schema/types/SkillModificationLevel"
import { config as socialStatusConfig } from "optolith-database-schema/types/SocialStatus"
import { config as spellConfig } from "optolith-database-schema/types/Spell"
import { config as stateConfig } from "optolith-database-schema/types/State"
import { config as talismanConfig } from "optolith-database-schema/types/Talisman"
import { config as targetCategoryConfig } from "optolith-database-schema/types/TargetCategory"
import { config as uIConfig } from "optolith-database-schema/types/UI"
import { config as equipmentPackageConfig } from "optolith-database-schema/types/equipment/EquipmentPackage"
import { config as ammunitionConfig } from "optolith-database-schema/types/equipment/item/Ammunition"
import { config as animalConfig } from "optolith-database-schema/types/equipment/item/Animal"
import { config as animalCareConfig } from "optolith-database-schema/types/equipment/item/AnimalCare"
import { config as armorConfig } from "optolith-database-schema/types/equipment/item/Armor"
import { config as bandageOrRemedyConfig } from "optolith-database-schema/types/equipment/item/BandageOrRemedy"
import { config as bookConfig } from "optolith-database-schema/types/equipment/item/Book"
import { config as ceremonialItemConfig } from "optolith-database-schema/types/equipment/item/CeremonialItem"
import { config as clothesConfig } from "optolith-database-schema/types/equipment/item/Clothes"
import { config as containerConfig } from "optolith-database-schema/types/equipment/item/Container"
import { config as elixirConfig } from "optolith-database-schema/types/equipment/item/Elixir"
import { config as equipmentOfBlessedOnesConfig } from "optolith-database-schema/types/equipment/item/EquipmentOfBlessedOnes"
import { config as gemOrPreciousStoneConfig } from "optolith-database-schema/types/equipment/item/GemOrPreciousStone"
import { config as illuminationLightSourceConfig } from "optolith-database-schema/types/equipment/item/IlluminationLightSource"
import { config as illuminationRefillsOrSuppliesConfig } from "optolith-database-schema/types/equipment/item/IlluminationRefillsOrSupplies"
import { config as jewelryConfig } from "optolith-database-schema/types/equipment/item/Jewelry"
import { config as liebesspielzeugConfig } from "optolith-database-schema/types/equipment/item/Liebesspielzeug"
import { config as luxuryGoodConfig } from "optolith-database-schema/types/equipment/item/LuxuryGood"
import { config as magicalArtifactConfig } from "optolith-database-schema/types/equipment/item/MagicalArtifact"
import { config as musicalInstrumentConfig } from "optolith-database-schema/types/equipment/item/MusicalInstrument"
import { config as orienteeringAidConfig } from "optolith-database-schema/types/equipment/item/OrienteeringAid"
import { config as poisonConfig } from "optolith-database-schema/types/equipment/item/Poison"
import { config as ropeOrChainConfig } from "optolith-database-schema/types/equipment/item/RopeOrChain"
import { config as stationaryConfig } from "optolith-database-schema/types/equipment/item/Stationary"
import { config as thievesToolConfig } from "optolith-database-schema/types/equipment/item/ThievesTool"
import { config as toolOfTheTradeConfig } from "optolith-database-schema/types/equipment/item/ToolOfTheTrade"
import { config as travelGearOrToolConfig } from "optolith-database-schema/types/equipment/item/TravelGearOrTool"
import { config as vehicleConfig } from "optolith-database-schema/types/equipment/item/Vehicle"
import { config as weaponConfig } from "optolith-database-schema/types/equipment/item/Weapon"
import { config as weaponAccessoryConfig } from "optolith-database-schema/types/equipment/item/WeaponAccessory"
import { config as armorTypeConfig } from "optolith-database-schema/types/equipment/item/sub/ArmorType"
import { config as reachConfig } from "optolith-database-schema/types/equipment/item/sub/Reach"
import { config as animistPowerConfig } from "optolith-database-schema/types/magicalActions/AnimistPower"
import { config as tribeConfig } from "optolith-database-schema/types/magicalActions/AnimistPower_Tribe"
import { config as curseConfig } from "optolith-database-schema/types/magicalActions/Curse"
import { config as dominationRitualConfig } from "optolith-database-schema/types/magicalActions/DominationRitual"
import { config as elvenMagicalSongConfig } from "optolith-database-schema/types/magicalActions/ElvenMagicalSong"
import { config as geodeRitualConfig } from "optolith-database-schema/types/magicalActions/GeodeRitual"
import { config as jesterTrickConfig } from "optolith-database-schema/types/magicalActions/JesterTrick"
import { config as magicalDanceConfig } from "optolith-database-schema/types/magicalActions/MagicalDance"
import { config as magicalMelodyConfig } from "optolith-database-schema/types/magicalActions/MagicalMelody"
import { config as magicalRuneConfig } from "optolith-database-schema/types/magicalActions/MagicalRune"
import { config as zibiljaRitualConfig } from "optolith-database-schema/types/magicalActions/ZibiljaRitual"
import { config as coreRuleConfig } from "optolith-database-schema/types/rule/CoreRule"
import { config as focusRuleConfig } from "optolith-database-schema/types/rule/FocusRule"
import { config as subjectConfig } from "optolith-database-schema/types/rule/FocusRule_Subject"
import { config as optionalRuleConfig } from "optolith-database-schema/types/rule/OptionalRule"
import { config as publicationConfig } from "optolith-database-schema/types/source/Publication"
import { config as advancedCombatSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/AdvancedCombatSpecialAbility"
import { config as advancedKarmaSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/AdvancedKarmaSpecialAbility"
import { config as advancedMagicalSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/AdvancedMagicalSpecialAbility"
import { config as advancedSkillSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/AdvancedSkillSpecialAbility"
import { config as ancestorGlyphConfig } from "optolith-database-schema/types/specialAbility/AncestorGlyph"
import { config as blessedTraditionConfig } from "optolith-database-schema/types/specialAbility/BlessedTradition"
import { config as brawlingSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/BrawlingSpecialAbility"
import { config as ceremonialItemSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/CeremonialItemSpecialAbility"
import { config as combatSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/CombatSpecialAbility"
import { config as combatStyleSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/CombatStyleSpecialAbility"
import { config as commandSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/CommandSpecialAbility"
import { config as familiarSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/FamiliarSpecialAbility"
import { config as fatePointSexSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/FatePointSexSpecialAbility"
import { config as fatePointSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/FatePointSpecialAbility"
import { config as generalSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/GeneralSpecialAbility"
import { config as karmaSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/KarmaSpecialAbility"
import { config as liturgicalStyleSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/LiturgicalStyleSpecialAbility"
import { config as lycantropicGiftConfig } from "optolith-database-schema/types/specialAbility/LycantropicGift"
import { config as magicStyleSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/MagicStyleSpecialAbility"
import { config as magicalSignConfig } from "optolith-database-schema/types/specialAbility/MagicalSign"
import { config as magicalSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/MagicalSpecialAbility"
import { config as magicalTraditionConfig } from "optolith-database-schema/types/specialAbility/MagicalTradition"
import { config as pactGiftConfig } from "optolith-database-schema/types/specialAbility/PactGift"
import { config as protectiveWardingCircleSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/ProtectiveWardingCircleSpecialAbility"
import { config as sermonConfig } from "optolith-database-schema/types/specialAbility/Sermon"
import { config as sexSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/SexSpecialAbility"
import { config as sikaryanDrainSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/SikaryanDrainSpecialAbility"
import { config as skillStyleSpecialAbilityConfig } from "optolith-database-schema/types/specialAbility/SkillStyleSpecialAbility"
import { config as vampiricGiftConfig } from "optolith-database-schema/types/specialAbility/VampiricGift"
import { config as visionConfig } from "optolith-database-schema/types/specialAbility/Vision"
import { config as languageConfig } from "optolith-database-schema/types/specialAbility/sub/Language"
import { config as scriptConfig } from "optolith-database-schema/types/specialAbility/sub/Script"
import { config as tradeSecretConfig } from "optolith-database-schema/types/specialAbility/sub/TradeSecret"
import { config as arcaneOrbEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/ArcaneOrbEnchantment"
import { config as attireEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/AttireEnchantment"
import { config as bowlEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/BowlEnchantment"
import { config as cauldronEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/CauldronEnchantment"
import { config as chronicleEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/ChronicleEnchantment"
import { config as daggerRitualConfig } from "optolith-database-schema/types/traditionArtifacts/DaggerRitual"
import { config as foolsHatEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/FoolsHatEnchantment"
import { config as instrumentEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/InstrumentEnchantment"
import { config as krallenkettenzauberConfig } from "optolith-database-schema/types/traditionArtifacts/Krallenkettenzauber"
import { config as orbEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/OrbEnchantment"
import { config as ringEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/RingEnchantment"
import { config as sickleRitualConfig } from "optolith-database-schema/types/traditionArtifacts/SickleRitual"
import { config as spellSwordEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/SpellSwordEnchantment"
import { config as staffEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/StaffEnchantment"
import { config as toyEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/ToyEnchantment"
import { config as trinkhornzauberConfig } from "optolith-database-schema/types/traditionArtifacts/Trinkhornzauber"
import { config as wandEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/WandEnchantment"
import { config as weaponEnchantmentConfig } from "optolith-database-schema/types/traditionArtifacts/WeaponEnchantment"
import { config as animalShapeConfig } from "optolith-database-schema/types/traditionArtifacts/sub/AnimalShape"
import { config as animalShapePathConfig } from "optolith-database-schema/types/traditionArtifacts/sub/AnimalShapePath"
import { config as animalShapeSizeConfig } from "optolith-database-schema/types/traditionArtifacts/sub/AnimalShapeSize"
import { config as brewConfig } from "optolith-database-schema/types/traditionArtifacts/sub/Brew"
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type IdFromConfig<
Config extends { id: (data: any, filePath: string) => string | number }
> = ReturnType<Config["id"]>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type TypeFromConfig<
Config extends { id: (data: any, filePath: string) => string | number }
> = Parameters<Config["id"]>[0]
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type GetByIdFromConfig<
Config extends { id: (data: any, filePath: string) => string | number }
> = (id: IdFromConfig<Config>) => TypeFromConfig<Config> | undefined
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AllFromConfig<
Config extends { id: (data: any, filePath: string) => string | number }
> = TypeFromConfig<Config>[]
// prettier-ignore
export namespace GetById {
export namespace Static {
export type AdvancedCombatSpecialAbility = GetByIdFromConfig<typeof advancedCombatSpecialAbilityConfig>
export type AdvancedKarmaSpecialAbility = GetByIdFromConfig<typeof advancedKarmaSpecialAbilityConfig>
export type AdvancedMagicalSpecialAbility = GetByIdFromConfig<typeof advancedMagicalSpecialAbilityConfig>
export type AdvancedSkillSpecialAbility = GetByIdFromConfig<typeof advancedSkillSpecialAbilityConfig>
export type Advantage = GetByIdFromConfig<typeof advantageConfig>
export type Ammunition = GetByIdFromConfig<typeof ammunitionConfig>
export type AncestorGlyph = GetByIdFromConfig<typeof ancestorGlyphConfig>
export type AnimalCare = GetByIdFromConfig<typeof animalCareConfig>
export type AnimalDisease = GetByIdFromConfig<typeof animalDiseaseConfig>
export type Animal = GetByIdFromConfig<typeof animalConfig>
export type AnimalShapePath = GetByIdFromConfig<typeof animalShapePathConfig>
export type AnimalShape = GetByIdFromConfig<typeof animalShapeConfig>
export type AnimalShapeSize = GetByIdFromConfig<typeof animalShapeSizeConfig>
export type AnimalType = GetByIdFromConfig<typeof animalTypeConfig>
export type AnimistPower = GetByIdFromConfig<typeof animistPowerConfig>
export type Tribe = GetByIdFromConfig<typeof tribeConfig>
export type ArcaneBardTradition = GetByIdFromConfig<typeof arcaneBardTraditionConfig>
export type ArcaneDancerTradition = GetByIdFromConfig<typeof arcaneDancerTraditionConfig>
export type ArcaneOrbEnchantment = GetByIdFromConfig<typeof arcaneOrbEnchantmentConfig>
export type Armor = GetByIdFromConfig<typeof armorConfig>
export type ArmorType = GetByIdFromConfig<typeof armorTypeConfig>
export type Aspect = GetByIdFromConfig<typeof aspectConfig>
export type AttireEnchantment = GetByIdFromConfig<typeof attireEnchantmentConfig>
export type Attribute = GetByIdFromConfig<typeof attributeConfig>
export type BandageOrRemedy = GetByIdFromConfig<typeof bandageOrRemedyConfig>
export type BlessedTradition = GetByIdFromConfig<typeof blessedTraditionConfig>
export type Blessing = GetByIdFromConfig<typeof blessingConfig>
export type Book = GetByIdFromConfig<typeof bookConfig>
export type BowlEnchantment = GetByIdFromConfig<typeof bowlEnchantmentConfig>
export type BrawlingSpecialAbility = GetByIdFromConfig<typeof brawlingSpecialAbilityConfig>
export type Brew = GetByIdFromConfig<typeof brewConfig>
export type Cantrip = GetByIdFromConfig<typeof cantripConfig>
export type CauldronEnchantment = GetByIdFromConfig<typeof cauldronEnchantmentConfig>
export type CeremonialItem = GetByIdFromConfig<typeof ceremonialItemConfig>
export type CeremonialItemSpecialAbility = GetByIdFromConfig<typeof ceremonialItemSpecialAbilityConfig>
export type Ceremony = GetByIdFromConfig<typeof ceremonyConfig>
export type ChronicleEnchantment = GetByIdFromConfig<typeof chronicleEnchantmentConfig>
export type CloseCombatTechnique = GetByIdFromConfig<typeof closeCombatTechniqueConfig>
export type Clothes = GetByIdFromConfig<typeof clothesConfig>
export type CombatSpecialAbility = GetByIdFromConfig<typeof combatSpecialAbilityConfig>
export type CombatStyleSpecialAbility = GetByIdFromConfig<typeof combatStyleSpecialAbilityConfig>
export type CommandSpecialAbility = GetByIdFromConfig<typeof commandSpecialAbilityConfig>
export type Condition = GetByIdFromConfig<typeof conditionConfig>
export type Container = GetByIdFromConfig<typeof containerConfig>
export type Continent = GetByIdFromConfig<typeof continentConfig>
export type CoreRule = GetByIdFromConfig<typeof coreRuleConfig>
export type Culture = GetByIdFromConfig<typeof cultureConfig>
export type Curse = GetByIdFromConfig<typeof curseConfig>
export type DaggerRitual = GetByIdFromConfig<typeof daggerRitualConfig>
export type DerivedCharacteristic = GetByIdFromConfig<typeof derivedCharacteristicConfig>
export type Disadvantage = GetByIdFromConfig<typeof disadvantageConfig>
export type Disease = GetByIdFromConfig<typeof diseaseConfig>
export type DominationRitual = GetByIdFromConfig<typeof dominationRitualConfig>
export type Element = GetByIdFromConfig<typeof elementConfig>
export type Elixir = GetByIdFromConfig<typeof elixirConfig>
export type ElvenMagicalSong = GetByIdFromConfig<typeof elvenMagicalSongConfig>
export type EquipmentOfBlessedOnes = GetByIdFromConfig<typeof equipmentOfBlessedOnesConfig>
export type EquipmentPackage = GetByIdFromConfig<typeof equipmentPackageConfig>
export type ExperienceLevel = GetByIdFromConfig<typeof experienceLevelConfig>
export type EyeColor = GetByIdFromConfig<typeof eyeColorConfig>
export type FamiliarSpecialAbility = GetByIdFromConfig<typeof familiarSpecialAbilityConfig>
export type FamiliarsTrick = GetByIdFromConfig<typeof familiarsTrickConfig>
export type FatePointSexSpecialAbility = GetByIdFromConfig<typeof fatePointSexSpecialAbilityConfig>
export type FatePointSpecialAbility = GetByIdFromConfig<typeof fatePointSpecialAbilityConfig>
export type FocusRule = GetByIdFromConfig<typeof focusRuleConfig>
export type Subject = GetByIdFromConfig<typeof subjectConfig>
export type FoolsHatEnchantment = GetByIdFromConfig<typeof foolsHatEnchantmentConfig>
export type GemOrPreciousStone = GetByIdFromConfig<typeof gemOrPreciousStoneConfig>
export type GeneralSpecialAbility = GetByIdFromConfig<typeof generalSpecialAbilityConfig>
export type GeodeRitual = GetByIdFromConfig<typeof geodeRitualConfig>
export type HairColor = GetByIdFromConfig<typeof hairColorConfig>
export type IlluminationLightSource = GetByIdFromConfig<typeof illuminationLightSourceConfig>
export type IlluminationRefillsOrSupplies = GetByIdFromConfig<typeof illuminationRefillsOrSuppliesConfig>
export type InstrumentEnchantment = GetByIdFromConfig<typeof instrumentEnchantmentConfig>
export type JesterTrick = GetByIdFromConfig<typeof jesterTrickConfig>
export type Jewelry = GetByIdFromConfig<typeof jewelryConfig>
export type KarmaSpecialAbility = GetByIdFromConfig<typeof karmaSpecialAbilityConfig>
export type Kirchenpraegung = GetByIdFromConfig<typeof kirchenpraegungConfig>
export type Krallenkettenzauber = GetByIdFromConfig<typeof krallenkettenzauberConfig>
export type Language = GetByIdFromConfig<typeof languageConfig>
export type Curriculum = GetByIdFromConfig<typeof curriculumConfig>
export type Guideline = GetByIdFromConfig<typeof guidelineConfig>
export type Liebesspielzeug = GetByIdFromConfig<typeof liebesspielzeugConfig>
export type LiturgicalChant = GetByIdFromConfig<typeof liturgicalChantConfig>
export type LiturgicalStyleSpecialAbility = GetByIdFromConfig<typeof liturgicalStyleSpecialAbilityConfig>
export type Locale = GetByIdFromConfig<typeof localeConfig>
export type LuxuryGood = GetByIdFromConfig<typeof luxuryGoodConfig>
export type LycantropicGift = GetByIdFromConfig<typeof lycantropicGiftConfig>
export type MagicalArtifact = GetByIdFromConfig<typeof magicalArtifactConfig>
export type MagicalDance = GetByIdFromConfig<typeof magicalDanceConfig>
export type MagicalMelody = GetByIdFromConfig<typeof magicalMelodyConfig>
export type MagicalRune = GetByIdFromConfig<typeof magicalRuneConfig>
export type MagicalSign = GetByIdFromConfig<typeof magicalSignConfig>
export type MagicalSpecialAbility = GetByIdFromConfig<typeof magicalSpecialAbilityConfig>
export type MagicalTradition = GetByIdFromConfig<typeof magicalTraditionConfig>
export type MagicStyleSpecialAbility = GetByIdFromConfig<typeof magicStyleSpecialAbilityConfig>
export type MetaCondition = GetByIdFromConfig<typeof metaConditionConfig>
export type MusicalInstrument = GetByIdFromConfig<typeof musicalInstrumentConfig>
export type OptionalRule = GetByIdFromConfig<typeof optionalRuleConfig>
export type OrbEnchantment = GetByIdFromConfig<typeof orbEnchantmentConfig>
export type OrienteeringAid = GetByIdFromConfig<typeof orienteeringAidConfig>
export type PactCategory = GetByIdFromConfig<typeof pactCategoryConfig>
export type PactGift = GetByIdFromConfig<typeof pactGiftConfig>
export type PatronCategory = GetByIdFromConfig<typeof patronCategoryConfig>
export type Patron = GetByIdFromConfig<typeof patronConfig>
export type PersonalityTrait = GetByIdFromConfig<typeof personalityTraitConfig>
export type Poison = GetByIdFromConfig<typeof poisonConfig>
export type Profession = GetByIdFromConfig<typeof professionConfig>
export type Property = GetByIdFromConfig<typeof propertyConfig>
export type ProtectiveWardingCircleSpecialAbility = GetByIdFromConfig<typeof protectiveWardingCircleSpecialAbilityConfig>
export type Publication = GetByIdFromConfig<typeof publicationConfig>
export type Race = GetByIdFromConfig<typeof raceConfig>
export type RangedCombatTechnique = GetByIdFromConfig<typeof rangedCombatTechniqueConfig>
export type Reach = GetByIdFromConfig<typeof reachConfig>
export type Region = GetByIdFromConfig<typeof regionConfig>
export type RingEnchantment = GetByIdFromConfig<typeof ringEnchantmentConfig>
export type Ritual = GetByIdFromConfig<typeof ritualConfig>
export type RopeOrChain = GetByIdFromConfig<typeof ropeOrChainConfig>
export type Script = GetByIdFromConfig<typeof scriptConfig>
export type Sermon = GetByIdFromConfig<typeof sermonConfig>
export type Service = GetByIdFromConfig<typeof serviceConfig>
export type SexPractice = GetByIdFromConfig<typeof sexPracticeConfig>
export type SexSpecialAbility = GetByIdFromConfig<typeof sexSpecialAbilityConfig>
export type SickleRitual = GetByIdFromConfig<typeof sickleRitualConfig>
export type SikaryanDrainSpecialAbility = GetByIdFromConfig<typeof sikaryanDrainSpecialAbilityConfig>
export type SkillGroup = GetByIdFromConfig<typeof skillGroupConfig>
export type SkillModificationLevel = GetByIdFromConfig<typeof skillModificationLevelConfig>
export type Skill = GetByIdFromConfig<typeof skillConfig>
export type SkillStyleSpecialAbility = GetByIdFromConfig<typeof skillStyleSpecialAbilityConfig>
export type SocialStatus = GetByIdFromConfig<typeof socialStatusConfig>
export type Spell = GetByIdFromConfig<typeof spellConfig>
export type SpellSwordEnchantment = GetByIdFromConfig<typeof spellSwordEnchantmentConfig>
export type StaffEnchantment = GetByIdFromConfig<typeof staffEnchantmentConfig>
export type State = GetByIdFromConfig<typeof stateConfig>
export type Stationary = GetByIdFromConfig<typeof stationaryConfig>
export type Talisman = GetByIdFromConfig<typeof talismanConfig>
export type TargetCategory = GetByIdFromConfig<typeof targetCategoryConfig>
export type ThievesTool = GetByIdFromConfig<typeof thievesToolConfig>
export type ToolOfTheTrade = GetByIdFromConfig<typeof toolOfTheTradeConfig>
export type ToyEnchantment = GetByIdFromConfig<typeof toyEnchantmentConfig>
export type TradeSecret = GetByIdFromConfig<typeof tradeSecretConfig>
export type TravelGearOrTool = GetByIdFromConfig<typeof travelGearOrToolConfig>
export type Trinkhornzauber = GetByIdFromConfig<typeof trinkhornzauberConfig>
export type UI = GetByIdFromConfig<typeof uIConfig>
export type VampiricGift = GetByIdFromConfig<typeof vampiricGiftConfig>
export type Vehicle = GetByIdFromConfig<typeof vehicleConfig>
export type Vision = GetByIdFromConfig<typeof visionConfig>
export type WandEnchantment = GetByIdFromConfig<typeof wandEnchantmentConfig>
export type WeaponAccessory = GetByIdFromConfig<typeof weaponAccessoryConfig>
export type WeaponEnchantment = GetByIdFromConfig<typeof weaponEnchantmentConfig>
export type Weapon = GetByIdFromConfig<typeof weaponConfig>
export type ZibiljaRitual = GetByIdFromConfig<typeof zibiljaRitualConfig>
}
}
// prettier-ignore
export namespace Singleton {
export namespace Static {
export type ExperienceLevel = TypeFromConfig<typeof experienceLevelConfig> | undefined
}
}
// prettier-ignore
export namespace All {
export namespace Static {
export type AdvancedCombatSpecialAbilities = AllFromConfig<typeof advancedCombatSpecialAbilityConfig>
export type AdvancedKarmaSpecialAbilities = AllFromConfig<typeof advancedKarmaSpecialAbilityConfig>
export type AdvancedMagicalSpecialAbilities = AllFromConfig<typeof advancedMagicalSpecialAbilityConfig>
export type AdvancedSkillSpecialAbilities = AllFromConfig<typeof advancedSkillSpecialAbilityConfig>
export type Advantages = AllFromConfig<typeof advantageConfig>
export type Ammunition = AllFromConfig<typeof ammunitionConfig>
export type AncestorGlyphs = AllFromConfig<typeof ancestorGlyphConfig>
export type AnimalCare = AllFromConfig<typeof animalCareConfig>
export type AnimalDiseases = AllFromConfig<typeof animalDiseaseConfig>
export type Animals = AllFromConfig<typeof animalConfig>
export type AnimalShapePaths = AllFromConfig<typeof animalShapePathConfig>
export type AnimalShapes = AllFromConfig<typeof animalShapeConfig>
export type AnimalShapeSizes = AllFromConfig<typeof animalShapeSizeConfig>
export type AnimalTypes = AllFromConfig<typeof animalTypeConfig>
export type AnimistPowers = AllFromConfig<typeof animistPowerConfig>
export type AnimistPowerTribes = AllFromConfig<typeof tribeConfig>
export type ArcaneBardTraditions = AllFromConfig<typeof arcaneBardTraditionConfig>
export type ArcaneDancerTraditions = AllFromConfig<typeof arcaneDancerTraditionConfig>
export type ArcaneOrbEnchantments = AllFromConfig<typeof arcaneOrbEnchantmentConfig>
export type Armors = AllFromConfig<typeof armorConfig>
export type ArmorTypes = AllFromConfig<typeof armorTypeConfig>
export type Aspects = AllFromConfig<typeof aspectConfig>
export type AttireEnchantments = AllFromConfig<typeof attireEnchantmentConfig>
export type Attributes = AllFromConfig<typeof attributeConfig>
export type BandagesAndRemedies = AllFromConfig<typeof bandageOrRemedyConfig>
export type BlessedTraditions = AllFromConfig<typeof blessedTraditionConfig>
export type Blessings = AllFromConfig<typeof blessingConfig>
export type Books = AllFromConfig<typeof bookConfig>
export type BowlEnchantments = AllFromConfig<typeof bowlEnchantmentConfig>
export type BrawlingSpecialAbilities = AllFromConfig<typeof brawlingSpecialAbilityConfig>
export type Brews = AllFromConfig<typeof brewConfig>
export type Cantrips = AllFromConfig<typeof cantripConfig>
export type CauldronEnchantments = AllFromConfig<typeof cauldronEnchantmentConfig>
export type CeremonialItems = AllFromConfig<typeof ceremonialItemConfig>
export type CeremonialItemSpecialAbilities = AllFromConfig<typeof ceremonialItemSpecialAbilityConfig>
export type Ceremonies = AllFromConfig<typeof ceremonyConfig>
export type ChronicleEnchantments = AllFromConfig<typeof chronicleEnchantmentConfig>
export type CloseCombatTechniques = AllFromConfig<typeof closeCombatTechniqueConfig>
export type Clothes = AllFromConfig<typeof clothesConfig>
export type CombatSpecialAbilities = AllFromConfig<typeof combatSpecialAbilityConfig>
export type CombatStyleSpecialAbilities = AllFromConfig<typeof combatStyleSpecialAbilityConfig>
export type CommandSpecialAbilities = AllFromConfig<typeof commandSpecialAbilityConfig>
export type Conditions = AllFromConfig<typeof conditionConfig>
export type Containers = AllFromConfig<typeof containerConfig>
export type Continents = AllFromConfig<typeof continentConfig>
export type CoreRules = AllFromConfig<typeof coreRuleConfig>
export type Cultures = AllFromConfig<typeof cultureConfig>
export type Curses = AllFromConfig<typeof curseConfig>
export type DaggerRituals = AllFromConfig<typeof daggerRitualConfig>
export type DerivedCharacteristics = AllFromConfig<typeof derivedCharacteristicConfig>
export type Disadvantages = AllFromConfig<typeof disadvantageConfig>
export type Diseases = AllFromConfig<typeof diseaseConfig>
export type DominationRituals = AllFromConfig<typeof dominationRitualConfig>
export type Elements = AllFromConfig<typeof elementConfig>
export type Elixirs = AllFromConfig<typeof elixirConfig>
export type ElvenMagicalSongs = AllFromConfig<typeof elvenMagicalSongConfig>
export type EquipmentOfBlessedOnes = AllFromConfig<typeof equipmentOfBlessedOnesConfig>
export type EquipmentPackages = AllFromConfig<typeof equipmentPackageConfig>
export type ExperienceLevels = AllFromConfig<typeof experienceLevelConfig>
export type EyeColors = AllFromConfig<typeof eyeColorConfig>
export type FamiliarSpecialAbilities = AllFromConfig<typeof familiarSpecialAbilityConfig>
export type FamiliarsTricks = AllFromConfig<typeof familiarsTrickConfig>
export type FatePointSexSpecialAbilities = AllFromConfig<typeof fatePointSexSpecialAbilityConfig>
export type FatePointSpecialAbilities = AllFromConfig<typeof fatePointSpecialAbilityConfig>
export type FocusRules = AllFromConfig<typeof focusRuleConfig>
export type FocusRuleSubjects = AllFromConfig<typeof subjectConfig>
export type FoolsHatEnchantments = AllFromConfig<typeof foolsHatEnchantmentConfig>
export type GemsAndPreciousStones = AllFromConfig<typeof gemOrPreciousStoneConfig>
export type GeneralSpecialAbilities = AllFromConfig<typeof generalSpecialAbilityConfig>
export type GeodeRituals = AllFromConfig<typeof geodeRitualConfig>
export type HairColors = AllFromConfig<typeof hairColorConfig>
export type IlluminationLightSources = AllFromConfig<typeof illuminationLightSourceConfig>
export type IlluminationRefillsAndSupplies = AllFromConfig<typeof illuminationRefillsOrSuppliesConfig>
export type InstrumentEnchantments = AllFromConfig<typeof instrumentEnchantmentConfig>
export type JesterTricks = AllFromConfig<typeof jesterTrickConfig>
export type Jewelry = AllFromConfig<typeof jewelryConfig>
export type KarmaSpecialAbilities = AllFromConfig<typeof karmaSpecialAbilityConfig>
export type Kirchenpraegungen = AllFromConfig<typeof kirchenpraegungConfig>
export type Krallenkettenzauber = AllFromConfig<typeof krallenkettenzauberConfig>
export type Languages = AllFromConfig<typeof languageConfig>
export type LessonsCurricula = AllFromConfig<typeof curriculumConfig>
export type LessonsGuidelines = AllFromConfig<typeof guidelineConfig>
export type Liebesspielzeug = AllFromConfig<typeof liebesspielzeugConfig>
export type LiturgicalChants = AllFromConfig<typeof liturgicalChantConfig>
export type LiturgicalStyleSpecialAbilities = AllFromConfig<typeof liturgicalStyleSpecialAbilityConfig>
export type Locales = AllFromConfig<typeof localeConfig>
export type LuxuryGoods = AllFromConfig<typeof luxuryGoodConfig>
export type LycantropicGifts = AllFromConfig<typeof lycantropicGiftConfig>
export type MagicalArtifacts = AllFromConfig<typeof magicalArtifactConfig>
export type MagicalDances = AllFromConfig<typeof magicalDanceConfig>
export type MagicalMelodies = AllFromConfig<typeof magicalMelodyConfig>
export type MagicalRunes = AllFromConfig<typeof magicalRuneConfig>
export type MagicalSigns = AllFromConfig<typeof magicalSignConfig>
export type MagicalSpecialAbilities = AllFromConfig<typeof magicalSpecialAbilityConfig>
export type MagicalTraditions = AllFromConfig<typeof magicalTraditionConfig>
export type MagicStyleSpecialAbilities = AllFromConfig<typeof magicStyleSpecialAbilityConfig>
export type MetaConditions = AllFromConfig<typeof metaConditionConfig>
export type MusicalInstruments = AllFromConfig<typeof musicalInstrumentConfig>
export type OptionalRules = AllFromConfig<typeof optionalRuleConfig>
export type OrbEnchantments = AllFromConfig<typeof orbEnchantmentConfig>
export type OrienteeringAids = AllFromConfig<typeof orienteeringAidConfig>
export type PactCategories = AllFromConfig<typeof pactCategoryConfig>
export type PactGifts = AllFromConfig<typeof pactGiftConfig>
export type PatronCategories = AllFromConfig<typeof patronCategoryConfig>
export type Patrons = AllFromConfig<typeof patronConfig>
export type PersonalityTraits = AllFromConfig<typeof personalityTraitConfig>
export type Poisons = AllFromConfig<typeof poisonConfig>
export type Professions = AllFromConfig<typeof professionConfig>
export type Properties = AllFromConfig<typeof propertyConfig>
export type ProtectiveWardingCircleSpecialAbilities = AllFromConfig<typeof protectiveWardingCircleSpecialAbilityConfig>
export type Publications = AllFromConfig<typeof publicationConfig>
export type Races = AllFromConfig<typeof raceConfig>
export type RangedCombatTechniques = AllFromConfig<typeof rangedCombatTechniqueConfig>
export type Reaches = AllFromConfig<typeof reachConfig>
export type Regions = AllFromConfig<typeof regionConfig>
export type RingEnchantments = AllFromConfig<typeof ringEnchantmentConfig>
export type Rituals = AllFromConfig<typeof ritualConfig>
export type RopesAndChains = AllFromConfig<typeof ropeOrChainConfig>
export type Scripts = AllFromConfig<typeof scriptConfig>
export type Sermons = AllFromConfig<typeof sermonConfig>
export type Services = AllFromConfig<typeof serviceConfig>
export type SexPractices = AllFromConfig<typeof sexPracticeConfig>
export type SexSpecialAbilities = AllFromConfig<typeof sexSpecialAbilityConfig>
export type SickleRituals = AllFromConfig<typeof sickleRitualConfig>
export type SikaryanDrainSpecialAbilities = AllFromConfig<typeof sikaryanDrainSpecialAbilityConfig>
export type SkillGroups = AllFromConfig<typeof skillGroupConfig>
export type SkillModificationLevels = AllFromConfig<typeof skillModificationLevelConfig>
export type Skills = AllFromConfig<typeof skillConfig>
export type SkillStyleSpecialAbilities = AllFromConfig<typeof skillStyleSpecialAbilityConfig>
export type SocialStatuses = AllFromConfig<typeof socialStatusConfig>
export type Spells = AllFromConfig<typeof spellConfig>
export type SpellSwordEnchantments = AllFromConfig<typeof spellSwordEnchantmentConfig>
export type StaffEnchantments = AllFromConfig<typeof staffEnchantmentConfig>
export type States = AllFromConfig<typeof stateConfig>
export type Stationary = AllFromConfig<typeof stationaryConfig>
export type Talismans = AllFromConfig<typeof talismanConfig>
export type TargetCategories = AllFromConfig<typeof targetCategoryConfig>
export type ThievesTools = AllFromConfig<typeof thievesToolConfig>
export type ToolsOfTheTrade = AllFromConfig<typeof toolOfTheTradeConfig>
export type ToyEnchantments = AllFromConfig<typeof toyEnchantmentConfig>
export type TradeSecrets = AllFromConfig<typeof tradeSecretConfig>
export type TravelGearAndTools = AllFromConfig<typeof travelGearOrToolConfig>
export type Trinkhornzauber = AllFromConfig<typeof trinkhornzauberConfig>
export type UI = AllFromConfig<typeof uIConfig>
export type VampiricGifts = AllFromConfig<typeof vampiricGiftConfig>
export type Vehicles = AllFromConfig<typeof vehicleConfig>
export type Visions = AllFromConfig<typeof visionConfig>
export type WandEnchantments = AllFromConfig<typeof wandEnchantmentConfig>
export type WeaponAccessories = AllFromConfig<typeof weaponAccessoryConfig>
export type WeaponEnchantments = AllFromConfig<typeof weaponEnchantmentConfig>
export type Weapons = AllFromConfig<typeof weaponConfig>
export type ZibiljaRituals = AllFromConfig<typeof zibiljaRitualConfig>
}
}
+32
View File
@@ -0,0 +1,32 @@
import { UI } from "optolith-database-schema/types/UI"
import { LocaleMap } from "optolith-database-schema/types/_LocaleMap"
/**
* Translates a given key into a string, optionally with parameters.
*/
export type Translate = <K extends keyof UI>(
key: K,
...params: (string | number)[]
) => string
const insertParams = (str: string, params: (string | number)[]): string =>
str.replace(
/\{(?<index>\d+)\}/gu,
(_match, _p1, _offset, _s, { index: rawIndex }) => {
const index = Number.parseInt(rawIndex, 10)
return params[index]?.toString() ?? `{${rawIndex}}`
}
)
/**
* A mocked translate function.
*/
export const translateMock: Translate = <K extends keyof UI>(
key: K,
...options: (string | number)[]
) => insertParams(key, options)
/**
* Selects a value from a locale dictionary based on the selected locale.
*/
export type TranslateMap = <T>(map: LocaleMap<T> | undefined) => T | undefined
+80
View File
@@ -0,0 +1,80 @@
import { filterNonNullable } from "@optolith/helpers/array"
import { Compare } from "@optolith/helpers/compare"
import { PublicationRefs } from "optolith-database-schema/types/source/_PublicationRef"
import { Translate, TranslateMap } from "./helpers/translate.js"
/**
* Creates a function that creates the JSON representation of the rules text for
* a library entry.
*/
export const createLibraryEntryCreator =
<T, A = undefined>(
fn: LibraryEntryCreator<T, A, RawLibraryEntry>
): LibraryEntryCreator<T | undefined, A> =>
(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<T, A = undefined, R = LibraryEntry> = (
entry: T,
...args: A extends undefined ? [] : [A]
) => LibraryEntryConfiguredCreator<R>
/**
* 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<R = LibraryEntry> = (params: {
translate: Translate
translateMap: TranslateMap
localeCompare: Compare<string>
}) => 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
}