485 lines
15 KiB
TypeScript
485 lines
15 KiB
TypeScript
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 { getDurationTranslationForCantrip } from "./partial/rated/activatable/duration.js"
|
|
import { getTextForEffect } from "./partial/rated/activatable/effect.js"
|
|
import { Entity } from "./partial/rated/activatable/entity.js"
|
|
import {
|
|
getFastOneTimePerformanceParametersTranslations,
|
|
getFastSustainedPerformanceParametersTranslations,
|
|
getSlowOneTimePerformanceParametersTranslations,
|
|
getSlowSustainedPerformanceParametersTranslations,
|
|
} from "./partial/rated/activatable/index.js"
|
|
import { parensIf } from "./partial/rated/activatable/parensIf.js"
|
|
import { getTextForCantripRange } from "./partial/rated/activatable/range.js"
|
|
import { getTargetCategoryTranslation } 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,
|
|
}
|
|
) =>
|
|
(locale) => {
|
|
const { translate, translateMap, compare: localeCompare } = locale
|
|
const translation = translateMap(entry.translations)
|
|
|
|
if (translation === undefined) {
|
|
return undefined
|
|
}
|
|
|
|
const range = getTextForCantripRange(
|
|
locale,
|
|
ResponsiveTextSize.Full,
|
|
entry.parameters.range
|
|
)
|
|
|
|
const duration = getDurationTranslationForCantrip(
|
|
locale,
|
|
ResponsiveTextSize.Full,
|
|
entry.parameters.duration
|
|
)
|
|
|
|
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,
|
|
},
|
|
getTargetCategoryTranslation(
|
|
getTargetCategoryById,
|
|
locale,
|
|
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,
|
|
}
|
|
) =>
|
|
(locale) => {
|
|
const { translate, translateMap, compare: localeCompare } = locale
|
|
const translation = translateMap(entry.translations)
|
|
|
|
if (translation === undefined) {
|
|
return undefined
|
|
}
|
|
|
|
const { castingTime, cost, range, duration } = (() => {
|
|
switch (entry.parameters.tag) {
|
|
case "OneTime":
|
|
return getFastOneTimePerformanceParametersTranslations(
|
|
getSkillModificationLevelById,
|
|
locale,
|
|
Entity.Spell,
|
|
ResponsiveTextSize.Full,
|
|
entry.parameters.one_time
|
|
)
|
|
|
|
case "Sustained":
|
|
return getFastSustainedPerformanceParametersTranslations(
|
|
getSkillModificationLevelById,
|
|
locale,
|
|
Entity.Spell,
|
|
ResponsiveTextSize.Full,
|
|
entry.parameters.sustained
|
|
)
|
|
|
|
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(locale, translation.effect),
|
|
{
|
|
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,
|
|
},
|
|
getTargetCategoryTranslation(
|
|
getTargetCategoryById,
|
|
locale,
|
|
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,
|
|
}
|
|
) =>
|
|
(locale) => {
|
|
const { translate, translateMap, compare: localeCompare } = locale
|
|
const translation = translateMap(entry.translations)
|
|
|
|
if (translation === undefined) {
|
|
return undefined
|
|
}
|
|
|
|
const { castingTime, cost, range, duration } = (() => {
|
|
switch (entry.parameters.tag) {
|
|
case "OneTime":
|
|
return getSlowOneTimePerformanceParametersTranslations(
|
|
getSkillModificationLevelById,
|
|
locale,
|
|
Entity.Ritual,
|
|
ResponsiveTextSize.Full,
|
|
entry.parameters.one_time
|
|
)
|
|
|
|
case "Sustained":
|
|
return getSlowSustainedPerformanceParametersTranslations(
|
|
getSkillModificationLevelById,
|
|
locale,
|
|
Entity.Ritual,
|
|
ResponsiveTextSize.Full,
|
|
entry.parameters.sustained
|
|
)
|
|
|
|
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(locale, translation.effect),
|
|
{
|
|
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,
|
|
},
|
|
getTargetCategoryTranslation(
|
|
getTargetCategoryById,
|
|
locale,
|
|
entry.target
|
|
),
|
|
getTextForProperty(
|
|
{ translate, translateMap, getPropertyById },
|
|
entry.property
|
|
),
|
|
getTextForTraditions(
|
|
{ translate, translateMap, localeCompare, getMagicalTraditionById },
|
|
entry.traditions
|
|
),
|
|
createImprovementCost(translate, entry.improvement_cost),
|
|
],
|
|
src: entry.src,
|
|
}
|
|
}
|
|
)
|