feat: profession description generator
This commit is contained in:
@@ -2,3 +2,4 @@ arrowParens: avoid
|
||||
semi: false
|
||||
tabWidth: 2
|
||||
trailingComma: all
|
||||
printWidth: 100
|
||||
|
||||
@@ -73,6 +73,8 @@ const collator = new Intl.Collator(localeId, { usage: "sort" })
|
||||
|
||||
const localeEnv: LocaleEnvironment = {
|
||||
id: localeId,
|
||||
format: (text, args) =>
|
||||
new MessageFormat(localeId, text, { bidiIsolation: "none" }).format(args),
|
||||
compare: collator.compare.bind(collator),
|
||||
translate: (key, ...rest) =>
|
||||
new MessageFormat(localeId, localeInstance.translations?.[key] ?? key, {
|
||||
|
||||
+35
-35
@@ -1,44 +1,44 @@
|
||||
import { createEntityDescriptionCreator } from "../creator.js"
|
||||
import type { GetInstanceById } from "../helpers/getTypes.js"
|
||||
import { printInfluencePrerequisites } from "./partial/prerequisites/index.js"
|
||||
|
||||
/**
|
||||
* Get a JSON representation of the rules text for an influence.
|
||||
*/
|
||||
export const getInfluenceEntityDescription =
|
||||
createEntityDescriptionCreator<"Influence">(
|
||||
(_, locale, { content: entry }) => {
|
||||
const { translate, translateMap } = locale
|
||||
const translation = translateMap(entry.translations)
|
||||
export const getInfluenceEntityDescription = createEntityDescriptionCreator<
|
||||
"Influence",
|
||||
{
|
||||
getInstanceById: GetInstanceById<"Publication" | "Influence">
|
||||
}
|
||||
>(({ getInstanceById }, locale, { content: entry }) => {
|
||||
const { translate, translateMap } = locale
|
||||
const translation = translateMap(entry.translations)
|
||||
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (translation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
title: translation.name,
|
||||
className: "influence",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
...(translation.effects?.map(effect => ({
|
||||
label: effect.label,
|
||||
value: effect.text,
|
||||
})) ?? []),
|
||||
entry.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Prerequisites"),
|
||||
value: printInfluencePrerequisites(
|
||||
locale,
|
||||
entry.prerequisites,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
return {
|
||||
title: translation.name,
|
||||
className: "influence",
|
||||
body: [
|
||||
{
|
||||
type: "definitionList",
|
||||
items: [
|
||||
...(translation.effects?.map(effect => ({
|
||||
label: effect.label,
|
||||
value: effect.text,
|
||||
})) ?? []),
|
||||
entry.prerequisites === undefined
|
||||
? undefined
|
||||
: {
|
||||
label: translate("Prerequisites"),
|
||||
value: printInfluencePrerequisites(getInstanceById, locale, entry.prerequisites),
|
||||
},
|
||||
],
|
||||
errata: translation.errata,
|
||||
references: entry.src,
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
],
|
||||
errata: translation.errata,
|
||||
references: entry.src,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,34 +1,50 @@
|
||||
import { allSame } from "@elyukai/utils/array/filters"
|
||||
import { ensureNonEmpty, isNotEmpty } from "@elyukai/utils/array/nonEmpty"
|
||||
import { deepEqual } from "@elyukai/utils/equality"
|
||||
import { identity, on } from "@elyukai/utils/function"
|
||||
import { isNotNullish } from "@elyukai/utils/nullable"
|
||||
import { mapObject } from "@optolith/helpers/object"
|
||||
import { romanize } from "@optolith/helpers/roman"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { ResolvedSelectOption } from "optolith-database-schema/cache"
|
||||
import type {
|
||||
ActivatableIdentifier,
|
||||
ActivatableNameBuilderRules,
|
||||
RequirableSelectOptionIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../helpers/getTypes.js"
|
||||
import {
|
||||
AdvantageIdentifier,
|
||||
DisadvantageIdentifier,
|
||||
KarmaSpecialAbilityIdentifier,
|
||||
} from "../../helpers/identifiers.js"
|
||||
import { LocaleEnvironment } from "../../helpers/locale.js"
|
||||
import type { LocaleMap } from "../../helpers/translate.js"
|
||||
import type {
|
||||
LocaleMap,
|
||||
Translate,
|
||||
TranslateMap,
|
||||
} from "../../helpers/translate.js"
|
||||
import type { GetResolvedSelectOptionById } from "./prerequisites/single/activatable.js"
|
||||
import { MISSING_VALUE } from "./unknown.js"
|
||||
|
||||
/**
|
||||
* Returns the full name of the activatable entry as well as its components.
|
||||
*/
|
||||
export type ActivatableNameComponents = {
|
||||
full: ActivatableNameChunk
|
||||
fullWithoutLevel:
|
||||
id: ActivatableIdentifier
|
||||
base: ActivatableNameChunk
|
||||
options:
|
||||
| ActivatableNameChunk
|
||||
| [ActivatableNameChunk, ActivatableNameChunk]
|
||||
| undefined
|
||||
level: number | undefined
|
||||
nameBuilderRules: Required<ActivatableNameBuilderRules>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full name of the activatable entry as well as its components.
|
||||
*/
|
||||
export type CombinedActivatableNameComponents = {
|
||||
id: ActivatableIdentifier
|
||||
base: ActivatableNameChunk
|
||||
options: (
|
||||
| ActivatableNameChunk
|
||||
| [ActivatableNameChunk, ActivatableNameChunk]
|
||||
)[]
|
||||
level: number | undefined
|
||||
nameBuilderRules: Required<ActivatableNameBuilderRules>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,7 +54,7 @@ export type ActivatableNameComponents = {
|
||||
export type ActivatableNameChunk =
|
||||
| LocaleMap<string>
|
||||
| string
|
||||
| ((locale: LocaleEnvironment) => string)
|
||||
| ((translateMap: TranslateMap) => string)
|
||||
|
||||
const combineChunks = (
|
||||
a: ActivatableNameChunk,
|
||||
@@ -60,14 +76,16 @@ const combineChunks = (
|
||||
} else if (typeof b === "function") {
|
||||
return fs => join(a(fs), b(fs))
|
||||
} else if (typeof b === "object") {
|
||||
return fs => join(a(fs), fs.translateMap(b) ?? MISSING_VALUE)
|
||||
return translateMap =>
|
||||
join(a(translateMap), translateMap(b) ?? MISSING_VALUE)
|
||||
}
|
||||
return b
|
||||
} else if (typeof a === "object") {
|
||||
if (typeof b === "string") {
|
||||
return mapObject(a, aValue => join(aValue, b))
|
||||
} else if (typeof b === "function") {
|
||||
return fs => join(fs.translateMap(a) ?? MISSING_VALUE, b(fs))
|
||||
return translateMap =>
|
||||
join(translateMap(a) ?? MISSING_VALUE, b(translateMap))
|
||||
} else if (typeof b === "object") {
|
||||
const ret: LocaleMap<string> = {}
|
||||
for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {
|
||||
@@ -80,431 +98,459 @@ const combineChunks = (
|
||||
return a
|
||||
}
|
||||
|
||||
const mapChunk = (
|
||||
chunk: ActivatableNameChunk,
|
||||
map: (str: string) => string,
|
||||
): ActivatableNameChunk => {
|
||||
if (typeof chunk === "string") {
|
||||
return map(chunk)
|
||||
} else if (typeof chunk === "function") {
|
||||
return fs => map(chunk(fs))
|
||||
} else if (typeof chunk === "object") {
|
||||
return mapObject(chunk, chunkValue => map(chunkValue))
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
const combinePair = (pair: [ActivatableNameChunk, ActivatableNameChunk]) =>
|
||||
combineChunks(pair[0], pair[1], (a, b) => `${a}: ${b}`)
|
||||
|
||||
const normalizeChunks = (
|
||||
chunk: ActivatableNameChunk | [ActivatableNameChunk, ActivatableNameChunk],
|
||||
): ActivatableNameChunk => (Array.isArray(chunk) ? combinePair(chunk) : chunk)
|
||||
|
||||
/**
|
||||
* Zips multiple name chunks together, separating them with commas. Pairs of chunks are combined with a colon.
|
||||
*/
|
||||
const zipChunks = (
|
||||
chunks: (
|
||||
| ActivatableNameChunk
|
||||
| [ActivatableNameChunk, ActivatableNameChunk]
|
||||
)[],
|
||||
): ActivatableNameChunk => {
|
||||
const withNormalizedPairs = chunks.map(chunk => {
|
||||
if (Array.isArray(chunk)) {
|
||||
return combineChunks(chunk[0], chunk[1], (a, b) => `${a}: ${b}`)
|
||||
}
|
||||
): ActivatableNameChunk =>
|
||||
chunks
|
||||
.map(normalizeChunks)
|
||||
.reduce(
|
||||
(acc, chunk) =>
|
||||
acc === "" ? chunk : combineChunks(acc, chunk, (a, b) => `${a}, ${b}`),
|
||||
"",
|
||||
)
|
||||
|
||||
const renderLevel = (
|
||||
formatAsPrerequisite: boolean,
|
||||
id: ActivatableIdentifier,
|
||||
level: number | undefined,
|
||||
) =>
|
||||
level === undefined
|
||||
? undefined
|
||||
: formatAsPrerequisite ||
|
||||
id.kind === "Advantage" ||
|
||||
id.kind === "Disadvantage" ||
|
||||
level === 1
|
||||
? romanize(level)
|
||||
: `I–${romanize(level)}`
|
||||
|
||||
/**
|
||||
* Converts a name chunk to a displayable string.
|
||||
*/
|
||||
const renderActivatableNameChunk = (
|
||||
translateMap: TranslateMap,
|
||||
chunk: ActivatableNameChunk,
|
||||
): string => {
|
||||
if (typeof chunk === "string") {
|
||||
return chunk
|
||||
})
|
||||
|
||||
return withNormalizedPairs.reduce(
|
||||
(acc, chunk) =>
|
||||
acc === "" ? chunk : combineChunks(acc, chunk, (a, b) => `${a}, ${b}`),
|
||||
"",
|
||||
)
|
||||
} else if (typeof chunk === "function") {
|
||||
return chunk(translateMap)
|
||||
} else if (typeof chunk === "object") {
|
||||
return translateMap(chunk) ?? MISSING_VALUE
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
const combineBaseName = (
|
||||
base: ActivatableNameChunk,
|
||||
level: number | undefined,
|
||||
options: ActivatableNameComponents["options"],
|
||||
config: {
|
||||
levelPlacement?: "before" | "after"
|
||||
useParenthesis?: boolean
|
||||
} = {},
|
||||
): Pick<ActivatableNameComponents, "full" | "fullWithoutLevel"> => {
|
||||
const { levelPlacement = "after", useParenthesis = true } = config
|
||||
const appendLevel: (str: string) => string =
|
||||
level === undefined ? x => x : x => `${x} ${romanize(level)}`
|
||||
/**
|
||||
* This can be used to render an activatable entry with a selectable level, as it splits the text where the level should be inserted.
|
||||
*/
|
||||
export const renderActivatableNameComponentsWithoutLevel = (
|
||||
translateMap: TranslateMap,
|
||||
components: ActivatableNameComponents,
|
||||
): [beforeLevel: string, afterLevel?: string] => {
|
||||
const { levelPlacement, useParenthesis } = components.nameBuilderRules
|
||||
|
||||
const wrapParens = (str: string) => `(${str})`
|
||||
const wrapParens: (text: string) => string = useParenthesis
|
||||
? str => `(${str})`
|
||||
: identity
|
||||
|
||||
const appendOptions = useParenthesis
|
||||
? (baseStr: string, optionsStr: string) =>
|
||||
optionsStr === "" ? baseStr : `${baseStr} ${wrapParens(optionsStr)}`
|
||||
: (baseStr: string, optionsStr: string) =>
|
||||
optionsStr === "" ? baseStr : `${baseStr} ${optionsStr}`
|
||||
const base = renderActivatableNameChunk(translateMap, components.base)
|
||||
const options =
|
||||
components.options === undefined
|
||||
? undefined
|
||||
: wrapParens(
|
||||
renderActivatableNameChunk(
|
||||
translateMap,
|
||||
normalizeChunks(components.options),
|
||||
),
|
||||
)
|
||||
|
||||
const full = combineChunks(
|
||||
base,
|
||||
zipChunks(options),
|
||||
(() => {
|
||||
switch (levelPlacement) {
|
||||
case "before":
|
||||
return (a, b) => appendOptions(appendLevel(a), b)
|
||||
case "after":
|
||||
return (a, b) => appendLevel(appendOptions(a, b))
|
||||
default:
|
||||
return assertExhaustive(levelPlacement)
|
||||
}
|
||||
})(),
|
||||
)
|
||||
|
||||
switch (levelPlacement) {
|
||||
case "before":
|
||||
return {
|
||||
full,
|
||||
fullWithoutLevel: [base, mapChunk(zipChunks(options), wrapParens)],
|
||||
}
|
||||
case "after":
|
||||
return {
|
||||
full,
|
||||
fullWithoutLevel: combineChunks(
|
||||
base,
|
||||
zipChunks(options),
|
||||
appendOptions,
|
||||
),
|
||||
}
|
||||
switch (levelPlacement.kind) {
|
||||
case "BeforeOptions":
|
||||
return [base, options]
|
||||
case "AfterOptions":
|
||||
return [[base, options].filter(isNotNullish).join(" ")]
|
||||
default:
|
||||
return assertExhaustive(levelPlacement)
|
||||
}
|
||||
}
|
||||
|
||||
const getEntrySpecificFullName = (
|
||||
getInstanceById: GetInstanceById<"Aspect">,
|
||||
locale: LocaleEnvironment,
|
||||
id: ActivatableIdentifier,
|
||||
base: ActivatableNameChunk,
|
||||
level: number | undefined,
|
||||
options: RequirableSelectOptionIdentifier[] | undefined,
|
||||
printedOptions: ActivatableNameComponents["options"],
|
||||
): Pick<ActivatableNameComponents, "full" | "fullWithoutLevel"> | undefined => {
|
||||
switch (id.kind) {
|
||||
case "Advantage":
|
||||
switch (id.Advantage) {
|
||||
case AdvantageIdentifier.HatredOf: {
|
||||
const [firstOption, ...rest] = printedOptions
|
||||
/**
|
||||
* Renders the name components of an activatable entry.
|
||||
*/
|
||||
export const renderActivatableNameComponents = (
|
||||
translateMap: TranslateMap,
|
||||
components: ActivatableNameComponents,
|
||||
formatAsPrerequisite: boolean,
|
||||
): string => {
|
||||
const levelText = renderLevel(
|
||||
formatAsPrerequisite,
|
||||
components.id,
|
||||
components.level,
|
||||
)
|
||||
|
||||
if (firstOption === undefined || Array.isArray(firstOption)) {
|
||||
return undefined
|
||||
}
|
||||
const [beforeLevel, afterLevel] = renderActivatableNameComponentsWithoutLevel(
|
||||
translateMap,
|
||||
components,
|
||||
)
|
||||
|
||||
return combineBaseName(
|
||||
combineChunks(base, firstOption, (a, b) => `${a} ${b}`),
|
||||
level,
|
||||
rest,
|
||||
)
|
||||
}
|
||||
case AdvantageIdentifier.ImmunityToPoison:
|
||||
case AdvantageIdentifier.ImmunityToDisease:
|
||||
return combineBaseName(base, level, printedOptions, {
|
||||
useParenthesis: false,
|
||||
})
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
case "Disadvantage":
|
||||
switch (id.Disadvantage) {
|
||||
case DisadvantageIdentifier.PersonalityFlaw: {
|
||||
const [selection, optionalText, ...rest] = printedOptions
|
||||
return [beforeLevel, levelText, afterLevel].filter(isNotNullish).join(" ")
|
||||
}
|
||||
|
||||
if (
|
||||
selection === undefined ||
|
||||
Array.isArray(selection) ||
|
||||
Array.isArray(optionalText)
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return combineBaseName(base, level, [
|
||||
optionalText === undefined ? selection : [selection, optionalText],
|
||||
...rest,
|
||||
])
|
||||
}
|
||||
case DisadvantageIdentifier.AfraidOf:
|
||||
return combineBaseName(base, level, printedOptions, {
|
||||
useParenthesis: false,
|
||||
})
|
||||
case DisadvantageIdentifier.Principles:
|
||||
case DisadvantageIdentifier.Obligations:
|
||||
return combineBaseName(base, level, printedOptions, {
|
||||
levelPlacement: "before",
|
||||
})
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
case "AdvancedCombatSpecialAbility":
|
||||
case "AdvancedKarmaSpecialAbility":
|
||||
case "AdvancedMagicalSpecialAbility":
|
||||
return undefined
|
||||
case "AdvancedSkillSpecialAbility":
|
||||
switch (id.AdvancedSkillSpecialAbility) {
|
||||
// case AdvancedSkillSpecialAbilityIdentifier.Fachwissen: {
|
||||
// const [skillId, firstApplicationId, secondApplicationId] = options ?? []
|
||||
// const aspect =
|
||||
// aspectId?.tag === "Aspect"
|
||||
// ? getAspectById(aspectId.aspect)
|
||||
// : undefined
|
||||
// const aspectTranslations = locale.translateMap(aspect?.translations)
|
||||
|
||||
// if (aspectTranslations === undefined) {
|
||||
// return undefined
|
||||
// }
|
||||
|
||||
// return combineBaseName(
|
||||
// combineChunks(
|
||||
// base,
|
||||
// aspectTranslations.master_of_aspect_suffix ??
|
||||
// aspectTranslations.name,
|
||||
// (a, b) => `${a} ${b}`,
|
||||
// ),
|
||||
// level,
|
||||
// [],
|
||||
// )
|
||||
// const getApp = (
|
||||
// getSid: (r: Record<ActiveObjectWithId>) => Maybe<string | number>,
|
||||
// ) =>
|
||||
// pipe(
|
||||
// SA.applications,
|
||||
// filter(pipe(AA.prerequisite, isNothing)),
|
||||
// find(pipe(AA.id, elemF(getSid(hero_entry)))),
|
||||
// fmap(AA.name),
|
||||
// )
|
||||
// return pipe_(
|
||||
// hero_entry,
|
||||
// AOWIA.sid,
|
||||
// misStringM,
|
||||
// bindF(lookupF(SDA.skills(staticData))),
|
||||
// bindF(skill =>
|
||||
// pipe_(
|
||||
// List(getApp(AOWIA.sid2)(skill), getApp(AOWIA.sid3)(skill)),
|
||||
// catMaybes,
|
||||
// ensure(xs => flength(xs) === 2),
|
||||
// fmap(
|
||||
// pipe(
|
||||
// sortStrings(staticData),
|
||||
// formatList("conjunction")(staticData),
|
||||
// apps => `${SA.name(skill)}: ${apps}`,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
case "AncestorGlyph":
|
||||
case "ArcaneOrbEnchantment":
|
||||
case "AttireEnchantment":
|
||||
case "Beutelzauber":
|
||||
case "BlessedTradition":
|
||||
case "BowlEnchantment":
|
||||
case "BrawlingSpecialAbility":
|
||||
case "CauldronEnchantment":
|
||||
case "CeremonialItemSpecialAbility":
|
||||
case "ChronicleEnchantment":
|
||||
case "CombatSpecialAbility":
|
||||
case "CombatStyleSpecialAbility":
|
||||
case "CommandSpecialAbility":
|
||||
case "DaggerRitual":
|
||||
case "FamiliarSpecialAbility":
|
||||
case "FatePointSexSpecialAbility":
|
||||
case "FatePointSpecialAbility":
|
||||
case "FoolsHatEnchantment":
|
||||
return undefined
|
||||
case "GeneralSpecialAbility":
|
||||
switch (id.GeneralSpecialAbility) {
|
||||
// case GeneralSpecialAbilityIdentifier.LanguageSpecialization: {
|
||||
// const [languageId, specializationId] = options ?? []
|
||||
|
||||
// const language =
|
||||
// languageId?.tag === "Language"
|
||||
// ? getLanguageById(languageId.language)
|
||||
// : undefined
|
||||
|
||||
// const specializations =
|
||||
// language?.specializations?.tag === "Specific"
|
||||
// ? language.specializations.specific.list
|
||||
// : []
|
||||
|
||||
// const specialization =
|
||||
// specializationId?.tag === "General"
|
||||
// ? specializations.find(
|
||||
// spec => spec.id === specializationId.general,
|
||||
// )
|
||||
// : undefined
|
||||
|
||||
// if (aspectTranslations === undefined) {
|
||||
// return undefined
|
||||
// }
|
||||
|
||||
// return combineBaseName(
|
||||
// combineChunks(
|
||||
// base,
|
||||
// aspectTranslations.master_of_aspect_suffix ??
|
||||
// aspectTranslations.name,
|
||||
// (a, b) => `${a} ${b}`,
|
||||
// ),
|
||||
// level,
|
||||
// [],
|
||||
// )
|
||||
|
||||
// return pipe(
|
||||
// SDA.specialAbilities,
|
||||
// lookup<string>(SpecialAbilityId.Language),
|
||||
// bindF(pipe(findSelectOption, thrush(AOWIA.sid(hero_entry)))),
|
||||
// bindF(lang =>
|
||||
// pipe(
|
||||
// AOWIA.sid2,
|
||||
// bindF(
|
||||
// ifElse<string | number, string>(isString)<Maybe<string>>(
|
||||
// Just,
|
||||
// )(spec_id =>
|
||||
// bind(SOA.specializations(lang))(subscriptF(spec_id - 1)),
|
||||
// ),
|
||||
// ),
|
||||
// fmap(spec => `${SOA.name(lang)}: ${spec}`),
|
||||
// )(hero_entry),
|
||||
// ),
|
||||
// )(staticData)
|
||||
// }
|
||||
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
case "Haubenzauber":
|
||||
case "InstrumentEnchantment":
|
||||
return undefined
|
||||
case "KarmaSpecialAbility":
|
||||
switch (id.KarmaSpecialAbility) {
|
||||
case KarmaSpecialAbilityIdentifier.MasterOfAspect: {
|
||||
const [aspectId] = options ?? []
|
||||
const aspect =
|
||||
aspectId?.kind === "Aspect"
|
||||
? getInstanceById("Aspect", aspectId.Aspect)
|
||||
: undefined
|
||||
const aspectTranslations = locale.translateMap(aspect?.translations)
|
||||
|
||||
if (aspectTranslations === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return combineBaseName(
|
||||
combineChunks(
|
||||
base,
|
||||
aspectTranslations.master_of_aspect_suffix ??
|
||||
aspectTranslations.name,
|
||||
(a, b) => `${a} ${b}`,
|
||||
),
|
||||
level,
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
case "Krallenkettenzauber":
|
||||
case "Kristallkugelzauber":
|
||||
case "LiturgicalStyleSpecialAbility":
|
||||
case "LycantropicGift":
|
||||
case "MagicalSign":
|
||||
case "MagicalSpecialAbility":
|
||||
case "MagicalTradition":
|
||||
case "MagicStyleSpecialAbility":
|
||||
case "OrbEnchantment":
|
||||
case "PactGift":
|
||||
case "ProtectiveWardingCircleSpecialAbility":
|
||||
case "RingEnchantment":
|
||||
case "Sermon":
|
||||
case "SexSpecialAbility":
|
||||
case "SickleRitual":
|
||||
case "SikaryanDrainSpecialAbility":
|
||||
case "SkillStyleSpecialAbility":
|
||||
case "SpellSwordEnchantment":
|
||||
case "StaffEnchantment":
|
||||
case "ToyEnchantment":
|
||||
case "Trinkhornzauber":
|
||||
case "VampiricGift":
|
||||
case "Vision":
|
||||
case "WandEnchantment":
|
||||
case "WeaponEnchantment":
|
||||
return undefined
|
||||
default:
|
||||
return assertExhaustive(id)
|
||||
/**
|
||||
* Combines multiple name components into a single one when all values except for options are the same, joining options according to the passed function.
|
||||
*
|
||||
* Returns `undefined` if the components cannot be combined.
|
||||
*/
|
||||
export const combineNameComponents = (
|
||||
elements: ActivatableNameComponents[],
|
||||
): CombinedActivatableNameComponents | undefined => {
|
||||
if (
|
||||
isNotEmpty(elements) &&
|
||||
allSame(
|
||||
elements,
|
||||
on(item => [item.id, item.level], deepEqual),
|
||||
)
|
||||
) {
|
||||
return {
|
||||
...elements[0],
|
||||
options:
|
||||
ensureNonEmpty(elements.map(e => e.options).filter(isNotNullish)) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the name components of an activatable entry, combining multiple options into one string.
|
||||
*/
|
||||
export const renderCombinedActivatableNameComponents = (
|
||||
translateMap: TranslateMap,
|
||||
components: CombinedActivatableNameComponents,
|
||||
formatAsPrerequisite: boolean,
|
||||
join: (list: string[]) => string = list => list.join(", "),
|
||||
): string =>
|
||||
renderActivatableNameComponents(
|
||||
translateMap,
|
||||
{
|
||||
...components,
|
||||
options: join(
|
||||
components.options.map(chunk =>
|
||||
renderActivatableNameChunk(translateMap, normalizeChunks(chunk)),
|
||||
),
|
||||
),
|
||||
},
|
||||
formatAsPrerequisite,
|
||||
)
|
||||
|
||||
/**
|
||||
* Renders the name components of multiple activatable entries.
|
||||
*/
|
||||
export const renderMultipleStandaloneActivatableNameComponents = (
|
||||
translateMap: TranslateMap,
|
||||
components: ActivatableNameComponents[],
|
||||
formatAsPrerequisite: boolean,
|
||||
join: (list: string[]) => string = list => list.join(", "),
|
||||
): string =>
|
||||
join(
|
||||
components.map(item =>
|
||||
renderActivatableNameComponents(translateMap, item, formatAsPrerequisite),
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Renders the name components of multiple activatable entries, combining parts if possible.
|
||||
*/
|
||||
export const renderActivatableNameComponentsCombinedIfPossible = (
|
||||
translateMap: TranslateMap,
|
||||
components: ActivatableNameComponents[],
|
||||
formatAsPrerequisite: boolean,
|
||||
join: (list: string[]) => string = list => list.join(", "),
|
||||
): string => {
|
||||
const combined = combineNameComponents(components)
|
||||
if (combined === undefined) {
|
||||
return renderMultipleStandaloneActivatableNameComponents(
|
||||
translateMap,
|
||||
components,
|
||||
formatAsPrerequisite,
|
||||
join,
|
||||
)
|
||||
} else {
|
||||
return renderCombinedActivatableNameComponents(
|
||||
translateMap,
|
||||
combined,
|
||||
formatAsPrerequisite,
|
||||
join,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// const getEntrySpecificFullName = (
|
||||
// getInstanceById: GetInstanceById<"Aspect">,
|
||||
// locale: LocaleEnvironment,
|
||||
// id: ActivatableIdentifier,
|
||||
// base: ActivatableNameChunk,
|
||||
// level: number | undefined,
|
||||
// printedOptions: ActivatableNameComponents["options"],
|
||||
// ):
|
||||
// | Pick<ActivatableNameComponents, "full" | "fullWithoutLevel" | "level">
|
||||
// | undefined => {
|
||||
// switch (id.kind) {
|
||||
// case "Advantage":
|
||||
// switch (id.Advantage) {
|
||||
// case AdvantageIdentifier.HatredOf: {
|
||||
// const [firstOption, ...rest] = printedOptions
|
||||
|
||||
// if (firstOption === undefined || Array.isArray(firstOption)) {
|
||||
// return undefined
|
||||
// }
|
||||
|
||||
// return combineBaseName(
|
||||
// combineChunks(base, firstOption, (a, b) => `${a} ${b}`),
|
||||
// level,
|
||||
// rest,
|
||||
// )
|
||||
// }
|
||||
// default:
|
||||
// return undefined
|
||||
// }
|
||||
// case "Disadvantage":
|
||||
// switch (id.Disadvantage) {
|
||||
// case DisadvantageIdentifier.PersonalityFlaw: {
|
||||
// const [selection, optionalText, ...rest] = printedOptions
|
||||
|
||||
// if (
|
||||
// selection === undefined ||
|
||||
// Array.isArray(selection) ||
|
||||
// Array.isArray(optionalText)
|
||||
// ) {
|
||||
// return undefined
|
||||
// }
|
||||
|
||||
// return combineBaseName(base, level, [
|
||||
// optionalText === undefined ? selection : [selection, optionalText],
|
||||
// ...rest,
|
||||
// ])
|
||||
// }
|
||||
// default:
|
||||
// return undefined
|
||||
// }
|
||||
// case "AdvancedCombatSpecialAbility":
|
||||
// case "AdvancedKarmaSpecialAbility":
|
||||
// case "AdvancedMagicalSpecialAbility":
|
||||
// case "AdvancedSkillSpecialAbility":
|
||||
// switch (id.AdvancedSkillSpecialAbility) {
|
||||
// case AdvancedSkillSpecialAbilityIdentifier.Fachwissen: {
|
||||
// const [skillId, firstApplicationId, secondApplicationId] = options ?? []
|
||||
// const aspect =
|
||||
// aspectId?.tag === "Aspect"
|
||||
// ? getAspectById(aspectId.aspect)
|
||||
// : undefined
|
||||
// const aspectTranslations = locale.translateMap(aspect?.translations)
|
||||
|
||||
// if (aspectTranslations === undefined) {
|
||||
// return undefined
|
||||
// }
|
||||
|
||||
// return combineBaseName(
|
||||
// combineChunks(
|
||||
// base,
|
||||
// aspectTranslations.master_of_aspect_suffix ??
|
||||
// aspectTranslations.name,
|
||||
// (a, b) => `${a} ${b}`,
|
||||
// ),
|
||||
// level,
|
||||
// [],
|
||||
// )
|
||||
// const getApp = (
|
||||
// getSid: (r: Record<ActiveObjectWithId>) => Maybe<string | number>,
|
||||
// ) =>
|
||||
// pipe(
|
||||
// SA.applications,
|
||||
// filter(pipe(AA.prerequisite, isNothing)),
|
||||
// find(pipe(AA.id, elemF(getSid(hero_entry)))),
|
||||
// fmap(AA.name),
|
||||
// )
|
||||
// return pipe_(
|
||||
// hero_entry,
|
||||
// AOWIA.sid,
|
||||
// misStringM,
|
||||
// bindF(lookupF(SDA.skills(staticData))),
|
||||
// bindF(skill =>
|
||||
// pipe_(
|
||||
// List(getApp(AOWIA.sid2)(skill), getApp(AOWIA.sid3)(skill)),
|
||||
// catMaybes,
|
||||
// ensure(xs => flength(xs) === 2),
|
||||
// fmap(
|
||||
// pipe(
|
||||
// sortStrings(staticData),
|
||||
// formatList("conjunction")(staticData),
|
||||
// apps => `${SA.name(skill)}: ${apps}`,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
|
||||
// default:
|
||||
// return undefined
|
||||
// }
|
||||
// case "AncestorGlyph":
|
||||
// case "ArcaneOrbEnchantment":
|
||||
// case "AttireEnchantment":
|
||||
// case "Beutelzauber":
|
||||
// case "BlessedTradition":
|
||||
// case "BowlEnchantment":
|
||||
// case "BrawlingSpecialAbility":
|
||||
// case "CauldronEnchantment":
|
||||
// case "CeremonialItemSpecialAbility":
|
||||
// case "ChronicleEnchantment":
|
||||
// case "CombatSpecialAbility":
|
||||
// case "CombatStyleSpecialAbility":
|
||||
// case "CommandSpecialAbility":
|
||||
// case "DaggerRitual":
|
||||
// case "FamiliarSpecialAbility":
|
||||
// case "FatePointSexSpecialAbility":
|
||||
// case "FatePointSpecialAbility":
|
||||
// case "FoolsHatEnchantment":
|
||||
// return undefined
|
||||
// case "GeneralSpecialAbility":
|
||||
// switch (id.GeneralSpecialAbility) {
|
||||
// case GeneralSpecialAbilityIdentifier.LanguageSpecialization: {
|
||||
// const [languageId, specializationId] = options ?? []
|
||||
|
||||
// const language =
|
||||
// languageId?.tag === "Language"
|
||||
// ? getLanguageById(languageId.language)
|
||||
// : undefined
|
||||
|
||||
// const specializations =
|
||||
// language?.specializations?.tag === "Specific"
|
||||
// ? language.specializations.specific.list
|
||||
// : []
|
||||
|
||||
// const specialization =
|
||||
// specializationId?.tag === "General"
|
||||
// ? specializations.find(
|
||||
// spec => spec.id === specializationId.general,
|
||||
// )
|
||||
// : undefined
|
||||
|
||||
// if (aspectTranslations === undefined) {
|
||||
// return undefined
|
||||
// }
|
||||
|
||||
// return pipe(
|
||||
// SDA.specialAbilities,
|
||||
// lookup<string>(SpecialAbilityId.Language),
|
||||
// bindF(pipe(findSelectOption, thrush(AOWIA.sid(hero_entry)))),
|
||||
// bindF(lang =>
|
||||
// pipe(
|
||||
// AOWIA.sid2,
|
||||
// bindF(
|
||||
// ifElse<string | number, string>(isString)<Maybe<string>>(
|
||||
// Just,
|
||||
// )(spec_id =>
|
||||
// bind(SOA.specializations(lang))(subscriptF(spec_id - 1)),
|
||||
// ),
|
||||
// ),
|
||||
// fmap(spec => `${SOA.name(lang)}: ${spec}`),
|
||||
// )(hero_entry),
|
||||
// ),
|
||||
// )(staticData)
|
||||
// }
|
||||
|
||||
const renderOptions = (
|
||||
displayedInProfession: boolean,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
id: ActivatableIdentifier,
|
||||
options: RequirableSelectOptionIdentifier[] | undefined,
|
||||
):
|
||||
| ActivatableNameChunk
|
||||
| [ActivatableNameChunk, ActivatableNameChunk]
|
||||
| undefined => {
|
||||
const arr =
|
||||
options?.map(optionId => {
|
||||
const optTranslations = getResolvedSelectOptionById(id, optionId)?.content
|
||||
.translations
|
||||
return optTranslations === undefined
|
||||
? MISSING_VALUE
|
||||
: mapObject(optTranslations, t10n =>
|
||||
displayedInProfession
|
||||
? (t10n.name_in_profession ?? t10n.name)
|
||||
: t10n.name,
|
||||
)
|
||||
}) ?? []
|
||||
|
||||
if (isNotEmpty(arr) && arr.length > 1) {
|
||||
const [first, ...rest] = arr
|
||||
return [first, zipChunks(rest)]
|
||||
}
|
||||
|
||||
return arr[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name components for an activatable entry.
|
||||
*/
|
||||
export const getNameComponents = <T>(
|
||||
getInstanceById: GetInstanceById<"Aspect">,
|
||||
locale: LocaleEnvironment,
|
||||
translate: Translate,
|
||||
id: ActivatableIdentifier,
|
||||
options: RequirableSelectOptionIdentifier[] | undefined,
|
||||
level: number | undefined,
|
||||
nameBuilderRules: ActivatableNameBuilderRules | undefined,
|
||||
translations: LocaleMap<T>,
|
||||
getBaseName: (translation: T) => string,
|
||||
getSelectOptionById: (
|
||||
id: RequirableSelectOptionIdentifier,
|
||||
) => ResolvedSelectOption | undefined,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
displayedInProfession: boolean,
|
||||
): ActivatableNameComponents => {
|
||||
const base = mapObject(translations, getBaseName)
|
||||
const nameOptions: ActivatableNameComponents["options"] = (() => {
|
||||
const arr =
|
||||
options?.map(optionId => {
|
||||
const optTranslations =
|
||||
getSelectOptionById(optionId)?.content.translations
|
||||
return optTranslations === undefined
|
||||
? MISSING_VALUE
|
||||
: mapObject(optTranslations, t10n =>
|
||||
displayedInProfession
|
||||
? (t10n.name_in_profession ?? t10n.name)
|
||||
: t10n.name,
|
||||
)
|
||||
}) ?? []
|
||||
const nameBuilderRulesWithDefaults: Required<ActivatableNameBuilderRules> = {
|
||||
levelPlacement: nameBuilderRules?.levelPlacement ?? {
|
||||
kind: "AfterOptions",
|
||||
},
|
||||
useParenthesis: nameBuilderRules?.useParenthesis ?? true,
|
||||
}
|
||||
|
||||
if (arr.length > 1) {
|
||||
const [first, ...rest] = arr
|
||||
return [[first!, zipChunks(rest)]]
|
||||
}
|
||||
const renderedBase = mapObject(translations, getBaseName)
|
||||
const renderedOptions = renderOptions(
|
||||
displayedInProfession,
|
||||
getResolvedSelectOptionById,
|
||||
id,
|
||||
options,
|
||||
)
|
||||
|
||||
return arr
|
||||
})()
|
||||
const isTradition =
|
||||
id.kind === "MagicalTradition" || id.kind === "BlessedTradition"
|
||||
|
||||
const actualBase = isTradition ? translate("Tradition") : renderedBase
|
||||
const actualOptions:
|
||||
| ActivatableNameChunk
|
||||
| [ActivatableNameChunk, ActivatableNameChunk]
|
||||
| undefined = isTradition
|
||||
? renderedOptions === undefined
|
||||
? renderedBase
|
||||
: [renderedBase, normalizeChunks(renderedOptions)]
|
||||
: renderedOptions
|
||||
|
||||
return {
|
||||
...(getEntrySpecificFullName(
|
||||
getInstanceById,
|
||||
locale,
|
||||
id,
|
||||
base,
|
||||
level,
|
||||
options,
|
||||
nameOptions,
|
||||
) ?? combineBaseName(base, level, nameOptions)),
|
||||
base,
|
||||
options: nameOptions,
|
||||
id,
|
||||
base: actualBase,
|
||||
options: actualOptions,
|
||||
level,
|
||||
nameBuilderRules: nameBuilderRulesWithDefaults,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a name chunk to a displayable string.
|
||||
*/
|
||||
export const printActivatableNameChunk = (
|
||||
locale: LocaleEnvironment,
|
||||
chunk: ActivatableNameChunk,
|
||||
): string => {
|
||||
if (typeof chunk === "string") {
|
||||
return chunk
|
||||
} else if (typeof chunk === "function") {
|
||||
return chunk(locale)
|
||||
} else if (typeof chunk === "object") {
|
||||
return locale.translateMap(chunk) ?? MISSING_VALUE
|
||||
}
|
||||
return chunk
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { DisplayOption } from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import type { TranslateMap } from "../../../helpers/translate.js"
|
||||
import { MISSING_VALUE } from "../unknown.js"
|
||||
import { PrerequisitePart } from "./part.js"
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PrerequisitePart } from "./part.js"
|
||||
* Get the translation of a display option.
|
||||
*/
|
||||
export const printDisplayOption = (
|
||||
locale: LocaleEnvironment,
|
||||
translateMap: TranslateMap,
|
||||
displayOption: DisplayOption,
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (displayOption.kind) {
|
||||
@@ -16,9 +16,7 @@ export const printDisplayOption = (
|
||||
return undefined
|
||||
case "ReplaceWith":
|
||||
return {
|
||||
value:
|
||||
locale.translateMap(displayOption.ReplaceWith.translations)
|
||||
?.replacement ?? MISSING_VALUE,
|
||||
value: translateMap(displayOption.ReplaceWith.translations)?.replacement ?? MISSING_VALUE,
|
||||
sentenceType: displayOption.ReplaceWith.sentence_type,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { on } from "@elyukai/utils/function"
|
||||
import { mapNullable } from "@elyukai/utils/nullable"
|
||||
import { numAsc } from "@optolith/helpers/compare"
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { romanize } from "@optolith/helpers/roman"
|
||||
@@ -32,9 +33,14 @@ import {
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import type { TranslateMap } from "../../../helpers/translate.js"
|
||||
import {
|
||||
renderActivatableNameComponents,
|
||||
renderActivatableNameComponentsCombinedIfPossible,
|
||||
} from "../activatableNameChunks.js"
|
||||
import { MISSING_VALUE } from "../unknown.js"
|
||||
import { printDisplayOption } from "./displayOption.js"
|
||||
import { joinPrerequisiteParts, PrerequisitePart } from "./part.js"
|
||||
import { hasPartValueObject, joinPrerequisiteParts, PrerequisitePart } from "./part.js"
|
||||
import {
|
||||
printAdvantageDisadvantagePrerequisiteGroup,
|
||||
printAnimistPowerPrerequisiteGroup,
|
||||
@@ -56,26 +62,24 @@ import { GetResolvedSelectOptionById } from "./single/activatable.js"
|
||||
type Prerequisite = { kind: string }
|
||||
|
||||
const printPrerequisiteGroup = (
|
||||
locale: LocaleEnvironment,
|
||||
translateMap: TranslateMap,
|
||||
group: PrerequisiteGroup<unknown>,
|
||||
): PrerequisitePart => ({
|
||||
value: locale.translateMap(group.translations)?.text ?? MISSING_VALUE,
|
||||
value: translateMap(group.translations)?.text ?? MISSING_VALUE,
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
})
|
||||
|
||||
const printPrerequisitesDisjunction = <T extends Prerequisite>(
|
||||
getPrerequisiteTranslation: (prerequisite: T) => PrerequisitePart | undefined,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translateMap" | "join">,
|
||||
disjunction: PrerequisitesDisjunction<T>,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (disjunction.display_option !== undefined) {
|
||||
return printDisplayOption(locale, disjunction.display_option)
|
||||
return printDisplayOption(locale.translateMap, disjunction.display_option)
|
||||
}
|
||||
|
||||
const [first, ...other] = disjunction.list
|
||||
.map(getPrerequisiteTranslation)
|
||||
.filter(isNotNullish)
|
||||
const [first, ...other] = disjunction.list.map(getPrerequisiteTranslation).filter(isNotNullish)
|
||||
|
||||
if (first === undefined) {
|
||||
return undefined
|
||||
@@ -83,16 +87,26 @@ const printPrerequisitesDisjunction = <T extends Prerequisite>(
|
||||
|
||||
if (
|
||||
disjunction.list.length < 2 ||
|
||||
disjunction.list
|
||||
.slice(1)
|
||||
.every(part => part.kind === disjunction.list[0]!.kind)
|
||||
disjunction.list.slice(1).every(part => part.kind === disjunction.list[0]!.kind)
|
||||
) {
|
||||
return {
|
||||
label: first.label,
|
||||
value: locale.join(
|
||||
[first, ...other].map(part => part.value),
|
||||
"disjunction",
|
||||
),
|
||||
value:
|
||||
hasPartValueObject(first) && other.every(hasPartValueObject)
|
||||
? renderActivatableNameComponentsCombinedIfPossible(
|
||||
locale.translateMap,
|
||||
[first.value, ...other.map(part => part.value)],
|
||||
true,
|
||||
list => locale.join(list, "disjunction"),
|
||||
)
|
||||
: locale.join(
|
||||
[first, ...other].map(part =>
|
||||
typeof part.value === "string"
|
||||
? part.value
|
||||
: renderActivatableNameComponents(locale.translateMap, part.value, true),
|
||||
),
|
||||
"disjunction",
|
||||
),
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
@@ -113,20 +127,16 @@ const printPrerequisitesDisjunction = <T extends Prerequisite>(
|
||||
*/
|
||||
const printPrerequisitesElement = <T extends Prerequisite>(
|
||||
printPrerequisite: (prerequisite: T) => PrerequisitePart | undefined,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translateMap" | "join">,
|
||||
element: PrerequisitesElement<T>,
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (element.kind) {
|
||||
case "Single":
|
||||
return printPrerequisite(element.Single)
|
||||
case "Disjunction":
|
||||
return printPrerequisitesDisjunction(
|
||||
printPrerequisite,
|
||||
locale,
|
||||
element.Disjunction,
|
||||
)
|
||||
return printPrerequisitesDisjunction(printPrerequisite, locale, element.Disjunction)
|
||||
case "Group":
|
||||
return printPrerequisiteGroup(locale, element.Group)
|
||||
return printPrerequisiteGroup(locale.translateMap, element.Group)
|
||||
default:
|
||||
return assertExhaustive(element)
|
||||
}
|
||||
@@ -137,14 +147,19 @@ const printPrerequisitesElement = <T extends Prerequisite>(
|
||||
*/
|
||||
const printPlainPrerequisites = <T extends Prerequisite>(
|
||||
printPrerequisite: (prerequisite: T) => PrerequisitePart | undefined,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap" | "compare" | "join">,
|
||||
prerequisites: PlainPrerequisites<T>,
|
||||
): string =>
|
||||
joinPrerequisiteParts(
|
||||
locale,
|
||||
locale.translate,
|
||||
locale.translateMap,
|
||||
locale.compare,
|
||||
prerequisites
|
||||
.map(element =>
|
||||
printPrerequisitesElement(printPrerequisite, locale, element),
|
||||
mapNullable(printPrerequisitesElement(printPrerequisite, locale, element), part => ({
|
||||
type: element.kind === "Single" ? element.Single.kind : element.kind,
|
||||
part,
|
||||
})),
|
||||
)
|
||||
.filter(isNotNullish),
|
||||
)
|
||||
@@ -173,18 +188,13 @@ const printPrerequisitesForLevels = <T extends Prerequisite>(
|
||||
const previousLevelPrerequisites: PrerequisitesForLevels<T> =
|
||||
printPreviousLevelPrerequisites === undefined
|
||||
? []
|
||||
: Array.from(
|
||||
{ length: printPreviousLevelPrerequisites.levels - 1 },
|
||||
(_, i) => ({
|
||||
level: i + 2,
|
||||
prerequisite: {
|
||||
kind: "Single",
|
||||
Single: printPreviousLevelPrerequisites.createPreerequisite(
|
||||
i + 2,
|
||||
),
|
||||
},
|
||||
}),
|
||||
)
|
||||
: Array.from({ length: printPreviousLevelPrerequisites.levels - 1 }, (_, i) => ({
|
||||
level: i + 2,
|
||||
prerequisite: {
|
||||
kind: "Single",
|
||||
Single: printPreviousLevelPrerequisites.createPreerequisite(i + 2),
|
||||
},
|
||||
}))
|
||||
|
||||
const groupedByLevel = Map.groupBy(
|
||||
[...value, ...previousLevelPrerequisites],
|
||||
@@ -198,8 +208,7 @@ const printPrerequisitesForLevels = <T extends Prerequisite>(
|
||||
.toArray()
|
||||
.sort(on(item => item[0], numAsc))
|
||||
|
||||
const hasOnlyBasePrerequisites =
|
||||
groupedByLevel.size === 1 && hasBasePrerequisites
|
||||
const hasOnlyBasePrerequisites = groupedByLevel.size === 1 && hasBasePrerequisites
|
||||
|
||||
const printedParts = [
|
||||
...(hasBasePrerequisites
|
||||
@@ -211,10 +220,18 @@ const printPrerequisitesForLevels = <T extends Prerequisite>(
|
||||
]),
|
||||
...sortedByLevel.map(([levelNumber, prerequisites]) => {
|
||||
const prerequisitesString = joinPrerequisiteParts(
|
||||
locale,
|
||||
locale.translate,
|
||||
locale.translateMap,
|
||||
locale.compare,
|
||||
prerequisites
|
||||
.map(prerequisite =>
|
||||
printPrerequisiteForLevel(printPrerequisite, locale, prerequisite),
|
||||
.map(element =>
|
||||
mapNullable(printPrerequisiteForLevel(printPrerequisite, locale, element), part => ({
|
||||
type:
|
||||
element.prerequisite.kind === "Single"
|
||||
? element.prerequisite.Single.kind
|
||||
: element.prerequisite.kind,
|
||||
part,
|
||||
})),
|
||||
)
|
||||
.filter(isNotNullish),
|
||||
)
|
||||
@@ -238,8 +255,7 @@ export const printDerivedCharacteristicPrerequisites = (
|
||||
value: DerivedCharacteristicPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printDerivedCharacteristicPrerequisiteGroup(locale, prerequisite),
|
||||
prerequisite => printDerivedCharacteristicPrerequisiteGroup(locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -253,8 +269,7 @@ export const printPublicationPrerequisites = (
|
||||
value: PublicationPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printPublicationPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
prerequisite => printPublicationPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -347,14 +362,10 @@ export const printGeneralPrerequisites = (
|
||||
*/
|
||||
export const printProfessionPrerequisites = (
|
||||
getInstanceById: GetInstanceById<
|
||||
| "Race"
|
||||
| "Culture"
|
||||
| ActivatableIdentifier["kind"]
|
||||
| RatedIdentifier["kind"]
|
||||
| "Aspect"
|
||||
"Race" | "Culture" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Aspect"
|
||||
>,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap" | "compare" | "join">,
|
||||
value: ProfessionPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
@@ -415,12 +426,7 @@ export const printArcaneTraditionPrerequisites = (
|
||||
value: ArcaneTraditionPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printArcaneTraditionPrerequisiteGroup(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite,
|
||||
),
|
||||
prerequisite => printArcaneTraditionPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -434,12 +440,7 @@ export const printPersonalityTraitPrerequisites = (
|
||||
value: PersonalityTraitPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printPersonalityTraitPrerequisiteGroup(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite,
|
||||
),
|
||||
prerequisite => printPersonalityTraitPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -453,8 +454,7 @@ export const printSpellworkPrerequisites = (
|
||||
value: SpellworkPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printSpellworkPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
prerequisite => printSpellworkPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -476,11 +476,12 @@ export const printLiturgyPrerequisites = (
|
||||
* Print influence prerequisites as a string.
|
||||
*/
|
||||
export const printInfluencePrerequisites = (
|
||||
getInstanceById: GetInstanceById<"Influence">,
|
||||
locale: LocaleEnvironment,
|
||||
value: InfluencePrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite => printInfluencePrerequisiteGroup(locale, prerequisite),
|
||||
prerequisite => printInfluencePrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -489,9 +490,7 @@ export const printInfluencePrerequisites = (
|
||||
* Print language prerequisites as a string.
|
||||
*/
|
||||
export const printLanguagePrerequisites = (
|
||||
getInstanceById: GetInstanceById<
|
||||
"Race" | ActivatableIdentifier["kind"] | "Aspect"
|
||||
>,
|
||||
getInstanceById: GetInstanceById<"Race" | ActivatableIdentifier["kind"] | "Aspect">,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
locale: LocaleEnvironment,
|
||||
value: LanguagePrerequisites,
|
||||
@@ -517,8 +516,7 @@ export const printAnimistPowerPrerequisites = (
|
||||
value: AnimistPowerPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printAnimistPowerPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
prerequisite => printAnimistPowerPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -527,11 +525,12 @@ export const printAnimistPowerPrerequisites = (
|
||||
* Print geode ritual prerequisites as a string.
|
||||
*/
|
||||
export const printGeodeRitualPrerequisites = (
|
||||
getInstanceById: GetInstanceById<"Influence">,
|
||||
locale: LocaleEnvironment,
|
||||
value: GeodeRitualPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite => printGeodeRitualPrerequisiteGroup(locale, prerequisite),
|
||||
prerequisite => printGeodeRitualPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
@@ -545,8 +544,7 @@ export const printEnhancementPrerequisites = (
|
||||
value: EnhancementPrerequisites,
|
||||
): string =>
|
||||
printPlainPrerequisites(
|
||||
prerequisite =>
|
||||
printEnhancementPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
prerequisite => printEnhancementPrerequisiteGroup(getInstanceById, locale, prerequisite),
|
||||
locale,
|
||||
value,
|
||||
)
|
||||
|
||||
@@ -1,47 +1,208 @@
|
||||
import { groupBy } from "@elyukai/utils/array/groups"
|
||||
import { deepEqual, equal } from "@elyukai/utils/equality"
|
||||
import { on } from "@elyukai/utils/function"
|
||||
import { compareNumber, reduceCompare, type Compare } from "@elyukai/utils/ordering"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import { SentenceType } from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import { SentenceType, type ActivatableIdentifier } from "optolith-database-schema/gen"
|
||||
import type { LocaleCompare } from "../../../helpers/locale.js"
|
||||
import type { Translate, TranslateMap } from "../../../helpers/translate.js"
|
||||
import {
|
||||
renderActivatableNameComponents,
|
||||
renderActivatableNameComponentsCombinedIfPossible,
|
||||
type ActivatableNameComponents,
|
||||
} from "../activatableNameChunks.js"
|
||||
|
||||
/**
|
||||
* A part of the total list of prerequisites.
|
||||
*/
|
||||
export type PrerequisitePart = {
|
||||
label?: string
|
||||
value: string
|
||||
value: string | ActivatableNameComponents
|
||||
sentenceType: SentenceType | undefined
|
||||
isMeta: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard for prerequisite parts with a value object.
|
||||
*/
|
||||
export const hasPartValueObject = (
|
||||
part: PrerequisitePart,
|
||||
): part is PrerequisitePart & { value: ActivatableNameComponents } => typeof part.value !== "string"
|
||||
|
||||
const countFurtherToCombined = (
|
||||
part: PrerequisitePart & { value: ActivatableNameComponents },
|
||||
remainingParts: (PrerequisitePart & { value: ActivatableNameComponents })[],
|
||||
) => {
|
||||
const indexHasDifferentBaseValues = remainingParts.findIndex(
|
||||
remaining =>
|
||||
!(
|
||||
part.label === remaining.label &&
|
||||
deepEqual(part.value.id, remaining.value.id) &&
|
||||
part.value.level === remaining.value.level
|
||||
),
|
||||
)
|
||||
|
||||
return indexHasDifferentBaseValues === -1
|
||||
? // if -1 is returned, all remaining parts have the same base values, so we can combine with all of them
|
||||
remainingParts.length
|
||||
: indexHasDifferentBaseValues
|
||||
}
|
||||
|
||||
const joinAdjacentParts = (
|
||||
translateMap: TranslateMap,
|
||||
localeCompare: LocaleCompare,
|
||||
part: PrerequisitePart & { value: ActivatableNameComponents },
|
||||
remainingParts: (PrerequisitePart & { value: ActivatableNameComponents })[],
|
||||
): [renderedValue: string, furtherIncluded: number] => {
|
||||
const valuesWithSameBase = remainingParts.slice(0, countFurtherToCombined(part, remainingParts))
|
||||
|
||||
if (valuesWithSameBase.length > 0) {
|
||||
return [
|
||||
renderActivatableNameComponentsCombinedIfPossible(
|
||||
translateMap,
|
||||
[part.value, ...valuesWithSameBase.map(p => p.value)],
|
||||
true,
|
||||
list => list.toSorted(localeCompare).join(", "),
|
||||
),
|
||||
valuesWithSameBase.length,
|
||||
]
|
||||
}
|
||||
|
||||
return [renderActivatableNameComponents(translateMap, part.value, true), 0]
|
||||
}
|
||||
|
||||
const appendBySentenceType = (
|
||||
previouslyRendered: string,
|
||||
currentRendered: string,
|
||||
sentenceType: SentenceType | undefined,
|
||||
isLast: boolean,
|
||||
): string => {
|
||||
switch (sentenceType?.kind) {
|
||||
case "Standalone":
|
||||
return `${
|
||||
/[.;]$/u.test(previouslyRendered)
|
||||
? `${previouslyRendered.slice(0, -1)}. `
|
||||
: `${previouslyRendered}. `
|
||||
}${currentRendered}${currentRendered.endsWith(".") ? "" : "."}`
|
||||
case "Connected":
|
||||
return `${previouslyRendered === "" ? "" : /[.;]$/u.test(previouslyRendered) ? `${previouslyRendered} ` : `${previouslyRendered}; `}${currentRendered}${
|
||||
isLast ? "" : ";"
|
||||
}`
|
||||
case undefined:
|
||||
return previouslyRendered === ""
|
||||
? currentRendered
|
||||
: `${previouslyRendered}${/[.;]$/u.test(previouslyRendered) ? " " : ", "}${currentRendered}`
|
||||
default:
|
||||
return assertExhaustive(sentenceType)
|
||||
}
|
||||
}
|
||||
|
||||
type ActivatableGroup = "Advantage" | "Disadvantage" | "SpecialAbility"
|
||||
const activatableKindToGroup = (kind: ActivatableIdentifier["kind"]): ActivatableGroup =>
|
||||
kind === "Advantage" ? "Advantage" : kind === "Disadvantage" ? "Disadvantage" : "SpecialAbility"
|
||||
const activatableGroupOrder: ActivatableGroup[] = ["Advantage", "Disadvantage", "SpecialAbility"]
|
||||
const sortByActivatableGroup: Compare<ActivatableGroup> = on(
|
||||
group => activatableGroupOrder.indexOf(group),
|
||||
compareNumber,
|
||||
)
|
||||
|
||||
const sortByActivatableGroupAndName = (
|
||||
localeCompare: LocaleCompare,
|
||||
): Compare<[ActivatableGroup, string]> =>
|
||||
reduceCompare(
|
||||
on(item => item[0], sortByActivatableGroup),
|
||||
on(item => item[1], localeCompare),
|
||||
)
|
||||
|
||||
const appendPrerequisitePartGroup = (
|
||||
previous: string,
|
||||
translateMap: TranslateMap,
|
||||
localeCompare: LocaleCompare,
|
||||
parts: PrerequisitePart[],
|
||||
isLast: boolean,
|
||||
) => {
|
||||
if (parts.length === 0) {
|
||||
return previous
|
||||
} else if (parts.every(hasPartValueObject)) {
|
||||
return appendBySentenceType(
|
||||
previous,
|
||||
parts
|
||||
.reduce(
|
||||
(
|
||||
acc: [[ActivatableGroup, string][], number],
|
||||
current,
|
||||
i,
|
||||
arr,
|
||||
): [[ActivatableGroup, string][], number] => {
|
||||
if (acc[1] > 0) {
|
||||
return [acc[0], acc[1] - 1]
|
||||
}
|
||||
|
||||
const [rendered, furtherIncluded] = joinAdjacentParts(
|
||||
translateMap,
|
||||
localeCompare,
|
||||
current,
|
||||
arr.slice(i + 1),
|
||||
)
|
||||
|
||||
return [
|
||||
[
|
||||
...acc[0],
|
||||
[activatableKindToGroup(current.value.id.kind), (current.label ?? "") + rendered],
|
||||
],
|
||||
furtherIncluded,
|
||||
]
|
||||
},
|
||||
[[], 0],
|
||||
)[0]
|
||||
.toSorted(sortByActivatableGroupAndName(localeCompare))
|
||||
.map(grouped => grouped[1])
|
||||
.join(", "),
|
||||
undefined,
|
||||
isLast,
|
||||
)
|
||||
} else {
|
||||
return parts.reduce(
|
||||
(acc, current, i, arr) =>
|
||||
appendBySentenceType(
|
||||
acc,
|
||||
(current.label ?? "") +
|
||||
(typeof current.value === "string"
|
||||
? current.value
|
||||
: renderActivatableNameComponents(translateMap, current.value, true)),
|
||||
current.sentenceType,
|
||||
isLast && i === arr.length - 1,
|
||||
),
|
||||
previous,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join prerequisite parts using their configuration.
|
||||
*/
|
||||
export const joinPrerequisiteParts = (
|
||||
locale: LocaleEnvironment,
|
||||
parts: PrerequisitePart[],
|
||||
translate: Translate,
|
||||
translateMap: TranslateMap,
|
||||
localeCompare: LocaleCompare,
|
||||
parts: { type: string; part: PrerequisitePart }[],
|
||||
): string =>
|
||||
parts.reduce(
|
||||
(acc, part, i, arr) => {
|
||||
const text =
|
||||
part.label === undefined ? part.value : part.label + part.value
|
||||
|
||||
if (acc === "") {
|
||||
return text
|
||||
}
|
||||
|
||||
switch (part.sentenceType?.kind) {
|
||||
case "Standalone":
|
||||
return `${
|
||||
/[.;]$/u.test(acc) ? `${acc.slice(0, -1)}. ` : `${acc}. `
|
||||
}${text}${text.endsWith(".") ? "" : "."}`
|
||||
case "Connected":
|
||||
return `${/[.;]$/u.test(acc) ? `${acc} ` : `${acc}; `}${text}${
|
||||
i < arr.length - 1 ? ";" : ""
|
||||
}`
|
||||
case undefined:
|
||||
return `${acc}${/[.;]$/u.test(acc) ? " " : ", "}${text}`
|
||||
default:
|
||||
return assertExhaustive(part.sentenceType)
|
||||
}
|
||||
},
|
||||
parts.every(part => part.isMeta) ? locale.translate("none") : "",
|
||||
groupBy(
|
||||
parts,
|
||||
on(part => part.type, equal),
|
||||
)
|
||||
.map(group => ({
|
||||
type: group[0]!.type,
|
||||
parts: group.map(groupItem => groupItem.part),
|
||||
}))
|
||||
.reduce(
|
||||
(acc, partGroup, i, arr) =>
|
||||
appendPrerequisitePartGroup(
|
||||
acc,
|
||||
translateMap,
|
||||
localeCompare,
|
||||
partGroup.parts,
|
||||
i === arr.length - 1,
|
||||
),
|
||||
parts.every(({ part }) => part.isMeta) ? translate("none") : "",
|
||||
)
|
||||
|
||||
@@ -21,10 +21,7 @@ import {
|
||||
import type { GetInstanceById } from "../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../helpers/locale.js"
|
||||
import { PrerequisitePart } from "./part.js"
|
||||
import {
|
||||
GetResolvedSelectOptionById,
|
||||
printActivatablePrerequisite,
|
||||
} from "./single/activatable.js"
|
||||
import { GetResolvedSelectOptionById, printActivatablePrerequisite } from "./single/activatable.js"
|
||||
import { printAnimistPowerPrerequisite } from "./single/animistPower.js"
|
||||
import { printBlessedTraditionPrerequisite } from "./single/blessedTradition.js"
|
||||
import { printCommonSuggestedByRCPPrerequisite } from "./single/commonSuggestedByRCP.js"
|
||||
@@ -59,15 +56,9 @@ export const printDerivedCharacteristicPrerequisiteGroup = (
|
||||
case "Rule":
|
||||
return printRulePrerequisite(locale, prerequisite.Rule)
|
||||
case "BlessedTradition":
|
||||
return printBlessedTraditionPrerequisite(
|
||||
locale,
|
||||
prerequisite.BlessedTradition,
|
||||
)
|
||||
return printBlessedTraditionPrerequisite(locale, prerequisite.BlessedTradition)
|
||||
case "MagicalTradition":
|
||||
return printMagicalTraditionPrerequisite(
|
||||
locale,
|
||||
prerequisite.MagicalTradition,
|
||||
)
|
||||
return printMagicalTraditionPrerequisite(locale, prerequisite.MagicalTradition)
|
||||
default:
|
||||
return assertExhaustive(prerequisite)
|
||||
}
|
||||
@@ -86,11 +77,7 @@ export const printPublicationPrerequisiteGroup = (
|
||||
// default:
|
||||
// return assertExhaustive(prerequisite)
|
||||
// }
|
||||
printPublicationPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Publication,
|
||||
)
|
||||
printPublicationPrerequisite(getInstanceById, locale, prerequisite.Publication)
|
||||
|
||||
/**
|
||||
* Print the translation of a general prerequisite group.
|
||||
@@ -115,49 +102,33 @@ export const printGeneralPrerequisiteGroup = (
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (prerequisite.kind) {
|
||||
case "Sex":
|
||||
return printBinarySexPrerequisite(locale, prerequisite.Sex)
|
||||
return printBinarySexPrerequisite(locale.translate, prerequisite.Sex)
|
||||
case "Race":
|
||||
return printRacePrerequisite(getInstanceById, locale, prerequisite.Race)
|
||||
case "Culture":
|
||||
return printCulturePrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Culture,
|
||||
)
|
||||
return printCulturePrerequisite(getInstanceById, locale, prerequisite.Culture)
|
||||
case "Pact":
|
||||
return printPactPrerequisite(getInstanceById, locale, prerequisite.Pact)
|
||||
case "SocialStatus":
|
||||
return printSocialStatusPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.SocialStatus,
|
||||
)
|
||||
return printSocialStatusPrerequisite(getInstanceById, locale, prerequisite.SocialStatus)
|
||||
case "State":
|
||||
return printStatePrerequisite(getInstanceById, locale, prerequisite.State)
|
||||
case "Rule":
|
||||
return printRulePrerequisite(locale, prerequisite.Rule)
|
||||
case "PrimaryAttribute":
|
||||
return printPrimaryAttributePrerequisite(
|
||||
locale,
|
||||
prerequisite.PrimaryAttribute,
|
||||
)
|
||||
return printPrimaryAttributePrerequisite(locale, prerequisite.PrimaryAttribute)
|
||||
case "Activatable":
|
||||
return printActivatablePrerequisite(
|
||||
getInstanceById,
|
||||
getResolvedSelectOptionById,
|
||||
locale,
|
||||
prerequisite.Activatable,
|
||||
false,
|
||||
)
|
||||
case "BlessedTradition":
|
||||
return printBlessedTraditionPrerequisite(
|
||||
locale,
|
||||
prerequisite.BlessedTradition,
|
||||
)
|
||||
return printBlessedTraditionPrerequisite(locale, prerequisite.BlessedTradition)
|
||||
case "MagicalTradition":
|
||||
return printMagicalTraditionPrerequisite(
|
||||
locale,
|
||||
prerequisite.MagicalTradition,
|
||||
)
|
||||
return printMagicalTraditionPrerequisite(locale, prerequisite.MagicalTradition)
|
||||
case "Rated":
|
||||
return printRatedPrerequisite(getInstanceById, locale, prerequisite.Rated)
|
||||
case "RatedMinimumNumber":
|
||||
@@ -167,24 +138,13 @@ export const printGeneralPrerequisiteGroup = (
|
||||
prerequisite.RatedMinimumNumber,
|
||||
)
|
||||
case "RatedSum":
|
||||
return printRatedSumPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.RatedSum,
|
||||
)
|
||||
return printRatedSumPrerequisite(getInstanceById, locale, prerequisite.RatedSum)
|
||||
case "Enhancement":
|
||||
return printEnhancementPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Enhancement,
|
||||
)
|
||||
return printEnhancementPrerequisite(getInstanceById, locale, prerequisite.Enhancement)
|
||||
case "Text":
|
||||
return printTextPrerequisite(locale, prerequisite.Text)
|
||||
case "SexualCharacteristic":
|
||||
return printSexualCharacteristicPrerequisite(
|
||||
locale,
|
||||
prerequisite.SexualCharacteristic,
|
||||
)
|
||||
return printSexualCharacteristicPrerequisite(locale, prerequisite.SexualCharacteristic)
|
||||
default:
|
||||
return assertExhaustive(prerequisite)
|
||||
}
|
||||
@@ -195,33 +155,26 @@ export const printGeneralPrerequisiteGroup = (
|
||||
*/
|
||||
export const printProfessionPrerequisiteGroup = (
|
||||
getInstanceById: GetInstanceById<
|
||||
| "Race"
|
||||
| "Culture"
|
||||
| ActivatableIdentifier["kind"]
|
||||
| RatedIdentifier["kind"]
|
||||
| "Aspect"
|
||||
"Race" | "Culture" | ActivatableIdentifier["kind"] | RatedIdentifier["kind"] | "Aspect"
|
||||
>,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap">,
|
||||
prerequisite: ProfessionPrerequisiteGroup,
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (prerequisite.kind) {
|
||||
case "Sex":
|
||||
return printBinarySexPrerequisite(locale, prerequisite.Sex)
|
||||
return printBinarySexPrerequisite(locale.translate, prerequisite.Sex)
|
||||
case "Race":
|
||||
return printRacePrerequisite(getInstanceById, locale, prerequisite.Race)
|
||||
case "Culture":
|
||||
return printCulturePrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Culture,
|
||||
)
|
||||
return printCulturePrerequisite(getInstanceById, locale, prerequisite.Culture)
|
||||
case "Activatable":
|
||||
return printActivatablePrerequisite(
|
||||
getInstanceById,
|
||||
getResolvedSelectOptionById,
|
||||
locale,
|
||||
prerequisite.Activatable,
|
||||
true,
|
||||
)
|
||||
case "Rated":
|
||||
return printRatedPrerequisite(getInstanceById, locale, prerequisite.Rated)
|
||||
@@ -296,13 +249,9 @@ export const printArcaneTraditionPrerequisiteGroup = (
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (prerequisite.kind) {
|
||||
case "Sex":
|
||||
return printBinarySexPrerequisite(locale, prerequisite.Sex)
|
||||
return printBinarySexPrerequisite(locale.translate, prerequisite.Sex)
|
||||
case "Culture":
|
||||
return printCulturePrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Culture,
|
||||
)
|
||||
return printCulturePrerequisite(getInstanceById, locale, prerequisite.Culture)
|
||||
default:
|
||||
return assertExhaustive(prerequisite)
|
||||
}
|
||||
@@ -320,11 +269,7 @@ export const printPersonalityTraitPrerequisiteGroup = (
|
||||
case "Race":
|
||||
return printRacePrerequisite(getInstanceById, locale, prerequisite.Race)
|
||||
case "Culture":
|
||||
return printCulturePrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Culture,
|
||||
)
|
||||
return printCulturePrerequisite(getInstanceById, locale, prerequisite.Culture)
|
||||
case "PersonalityTrait":
|
||||
return printPersonalityTraitPrerequisite(
|
||||
getInstanceById,
|
||||
@@ -374,12 +319,13 @@ export const printLiturgyPrerequisiteGroup = (
|
||||
* Print the translation of an influence prerequisite group.
|
||||
*/
|
||||
export const printInfluencePrerequisiteGroup = (
|
||||
getInstanceById: GetInstanceById<"Influence">,
|
||||
locale: LocaleEnvironment,
|
||||
prerequisite: InfluencePrerequisiteGroup,
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (prerequisite.kind) {
|
||||
case "Influence":
|
||||
return printInfluencePrerequisite(locale, prerequisite.Influence)
|
||||
return printInfluencePrerequisite(getInstanceById, locale, prerequisite.Influence)
|
||||
case "Text":
|
||||
return printTextPrerequisite(locale, prerequisite.Text)
|
||||
default:
|
||||
@@ -391,9 +337,7 @@ export const printInfluencePrerequisiteGroup = (
|
||||
* Print the translation of a language prerequisite group.
|
||||
*/
|
||||
export const printLanguagePrerequisiteGroup = (
|
||||
getInstanceById: GetInstanceById<
|
||||
"Race" | ActivatableIdentifier["kind"] | "Aspect"
|
||||
>,
|
||||
getInstanceById: GetInstanceById<"Race" | ActivatableIdentifier["kind"] | "Aspect">,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
locale: LocaleEnvironment,
|
||||
prerequisite: LanguagePrerequisiteGroup,
|
||||
@@ -407,6 +351,7 @@ export const printLanguagePrerequisiteGroup = (
|
||||
getResolvedSelectOptionById,
|
||||
locale,
|
||||
prerequisite.Activatable,
|
||||
false,
|
||||
)
|
||||
case "Text":
|
||||
return printTextPrerequisite(locale, prerequisite.Text)
|
||||
@@ -428,16 +373,13 @@ export const printAnimistPowerPrerequisiteGroup = (
|
||||
// default:
|
||||
// return assertExhaustive(prerequisite)
|
||||
// }
|
||||
printAnimistPowerPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.AnimistPower,
|
||||
)
|
||||
printAnimistPowerPrerequisite(getInstanceById, locale, prerequisite.AnimistPower)
|
||||
|
||||
/**
|
||||
* Print the translation of a geode ritual prerequisite group.
|
||||
*/
|
||||
export const printGeodeRitualPrerequisiteGroup = (
|
||||
getInstanceById: GetInstanceById<"Influence">,
|
||||
locale: LocaleEnvironment,
|
||||
prerequisite: GeodeRitualPrerequisiteGroup,
|
||||
): PrerequisitePart | undefined =>
|
||||
@@ -446,7 +388,7 @@ export const printGeodeRitualPrerequisiteGroup = (
|
||||
// default:
|
||||
// return assertExhaustive(prerequisite)
|
||||
// }
|
||||
printInfluencePrerequisite(locale, prerequisite.Influence)
|
||||
printInfluencePrerequisite(getInstanceById, locale, prerequisite.Influence)
|
||||
|
||||
/**
|
||||
* Print the translation of an enhancement prerequisite group.
|
||||
@@ -460,11 +402,7 @@ export const printEnhancementPrerequisiteGroup = (
|
||||
case "Rated":
|
||||
return printRatedPrerequisite(getInstanceById, locale, prerequisite.Rated)
|
||||
case "Enhancement":
|
||||
return printEnhancementPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Enhancement,
|
||||
)
|
||||
return printEnhancementPrerequisite(getInstanceById, locale, prerequisite.Enhancement)
|
||||
default:
|
||||
return assertExhaustive(prerequisite)
|
||||
}
|
||||
@@ -480,16 +418,9 @@ export const printPreconditionGroup = (
|
||||
): PrerequisitePart | undefined => {
|
||||
switch (prerequisite.kind) {
|
||||
case "Publication":
|
||||
return printPublicationPrerequisite(
|
||||
getInstanceById,
|
||||
locale,
|
||||
prerequisite.Publication,
|
||||
)
|
||||
return printPublicationPrerequisite(getInstanceById, locale, prerequisite.Publication)
|
||||
case "SexualCharacteristic":
|
||||
return printSexualCharacteristicPrerequisite(
|
||||
locale,
|
||||
prerequisite.SexualCharacteristic,
|
||||
)
|
||||
return printSexualCharacteristicPrerequisite(locale, prerequisite.SexualCharacteristic)
|
||||
default:
|
||||
return assertExhaustive(prerequisite)
|
||||
}
|
||||
|
||||
@@ -4,16 +4,14 @@ import type {
|
||||
} from "optolith-database-schema/cache"
|
||||
import type {
|
||||
ActivatableIdentifier,
|
||||
ActivatableNameBuilderRules,
|
||||
ActivatablePrerequisite,
|
||||
RequirableSelectOptionIdentifier,
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { LocaleMap } from "../../../../helpers/translate.js"
|
||||
import {
|
||||
getNameComponents,
|
||||
printActivatableNameChunk,
|
||||
} from "../../activatableNameChunks.js"
|
||||
import type { LocaleMap, Translate } from "../../../../helpers/translate.js"
|
||||
import { getNameComponents } from "../../activatableNameChunks.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
|
||||
@@ -25,31 +23,39 @@ export type GetResolvedSelectOptionById = (
|
||||
selectOptionId: ResolvedSelectOptionIdentifier,
|
||||
) => ResolvedSelectOption | undefined
|
||||
|
||||
const printActivatableName = (
|
||||
/**
|
||||
* Get the name components of an activatable.
|
||||
*/
|
||||
export const printActivatableName = (
|
||||
getInstanceById: GetInstanceById<ActivatableIdentifier["kind"] | "Aspect">,
|
||||
locale: LocaleEnvironment,
|
||||
translate: Translate,
|
||||
id: ActivatableIdentifier,
|
||||
options: RequirableSelectOptionIdentifier[] | undefined,
|
||||
level: number | undefined,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
displayedInProfession: boolean,
|
||||
) => {
|
||||
const entry: { translations: LocaleMap<{ name: string }> } | undefined =
|
||||
getInstanceById(id)
|
||||
const entry:
|
||||
| {
|
||||
nameBuilderRules?: ActivatableNameBuilderRules
|
||||
translations: LocaleMap<{ name: string }>
|
||||
}
|
||||
| undefined = getInstanceById(id)
|
||||
|
||||
if (entry === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return getNameComponents(
|
||||
getInstanceById,
|
||||
locale,
|
||||
translate,
|
||||
id,
|
||||
options,
|
||||
level,
|
||||
entry.nameBuilderRules,
|
||||
entry.translations,
|
||||
t => t.name,
|
||||
selectOptionId => getResolvedSelectOptionById(id, selectOptionId),
|
||||
false,
|
||||
getResolvedSelectOptionById,
|
||||
displayedInProfession,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,20 +65,22 @@ const printActivatableName = (
|
||||
export const printActivatablePrerequisite = (
|
||||
getInstanceById: GetInstanceById<ActivatableIdentifier["kind"] | "Aspect">,
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap">,
|
||||
prerequisite: ActivatablePrerequisite,
|
||||
displayedInProfession: boolean,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const nameComponents = printActivatableName(
|
||||
getInstanceById,
|
||||
locale,
|
||||
locale.translate,
|
||||
prerequisite.id,
|
||||
prerequisite.options,
|
||||
prerequisite.level,
|
||||
getResolvedSelectOptionById,
|
||||
displayedInProfession,
|
||||
)
|
||||
|
||||
if (nameComponents === undefined) {
|
||||
@@ -81,11 +89,19 @@ export const printActivatablePrerequisite = (
|
||||
|
||||
return {
|
||||
label: `${
|
||||
prerequisite.active
|
||||
? locale.translate("special ability")
|
||||
: locale.translate("no special ability")
|
||||
prerequisite.id.kind === "Advantage"
|
||||
? prerequisite.active
|
||||
? locale.translate("advantage")
|
||||
: locale.translate("no advantage")
|
||||
: prerequisite.id.kind === "Disadvantage"
|
||||
? prerequisite.active
|
||||
? locale.translate("disadvantage")
|
||||
: locale.translate("no disadvantage")
|
||||
: prerequisite.active
|
||||
? locale.translate("special ability")
|
||||
: locale.translate("no special ability")
|
||||
} `,
|
||||
value: printActivatableNameChunk(locale, nameComponents.full),
|
||||
value: nameComponents,
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const printAnimistPowerPrerequisite = (
|
||||
prerequisite: AnimistPowerPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const animistPower = getInstanceById("AnimistPower", prerequisite.id)
|
||||
@@ -23,9 +23,7 @@ export const printAnimistPowerPrerequisite = (
|
||||
return {
|
||||
value: [
|
||||
locale.translateMap(animistPower?.translations)?.name ?? "MISSING_VALUE",
|
||||
prerequisite.level === undefined
|
||||
? undefined
|
||||
: romanize(prerequisite.level),
|
||||
prerequisite.level === undefined ? undefined : romanize(prerequisite.level),
|
||||
prerequisite.value.toString(),
|
||||
]
|
||||
.filter(isNotNullish)
|
||||
|
||||
@@ -35,7 +35,7 @@ export const printBlessedTraditionPrerequisite = (
|
||||
prerequisite: BlessedTraditionPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -9,11 +9,11 @@ import { PrerequisitePart } from "../part.js"
|
||||
*/
|
||||
export const printCulturePrerequisite = (
|
||||
getInstanceById: GetInstanceById<"Culture">,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap">,
|
||||
prerequisite: CulturePrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const culture = getInstanceById("Culture", prerequisite.id)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { InfluencePrerequisite } from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
@@ -8,17 +9,20 @@ import { PrerequisitePart } from "../part.js"
|
||||
* Get the translation of a culture prerequisite.
|
||||
*/
|
||||
export const printInfluencePrerequisite = (
|
||||
locale: LocaleEnvironment,
|
||||
getInstanceById: GetInstanceById<"Influence">,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap">,
|
||||
prerequisite: InfluencePrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
// TODO
|
||||
const name =
|
||||
locale.translateMap(getInstanceById("Influence", prerequisite.id)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
|
||||
return {
|
||||
value: MISSING_VALUE,
|
||||
value: `${locale.translate("no influence")} ${name}`,
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const printMagicalTraditionPrerequisite = (
|
||||
prerequisite: MagicalTraditionPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -16,7 +16,7 @@ export const printPactPrerequisite = (
|
||||
prerequisite: PactPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const pactCategory = getInstanceById("PactCategory", prerequisite.category)
|
||||
@@ -28,16 +28,14 @@ export const printPactPrerequisite = (
|
||||
domain: locale.join(
|
||||
prerequisite.domain.map(
|
||||
id =>
|
||||
locale.translateMap(
|
||||
getInstanceById("PactDomain", id)?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
locale.translateMap(getInstanceById("PactDomain", id)?.translations)?.name ??
|
||||
MISSING_VALUE,
|
||||
),
|
||||
"disjunction",
|
||||
),
|
||||
}),
|
||||
locale.translate("{$pact} level {$pactLevel}", {
|
||||
pact:
|
||||
locale.translateMap(pactCategory?.translations)?.name ?? MISSING_VALUE,
|
||||
pact: locale.translateMap(pactCategory?.translations)?.name ?? MISSING_VALUE,
|
||||
pactLevel: romanize(prerequisite.level ?? 1),
|
||||
}),
|
||||
].filter(isNotNullish)
|
||||
|
||||
@@ -13,25 +13,19 @@ export const printPersonalityTraitPrerequisite = (
|
||||
prerequisite: PersonalityTraitPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const personalityTrait = getInstanceById("PersonalityTrait", prerequisite.id)
|
||||
const personalityTraitTranslation = locale.translateMap(
|
||||
personalityTrait?.translations,
|
||||
)
|
||||
const personalityTraitTranslation = locale.translateMap(personalityTrait?.translations)
|
||||
|
||||
if (
|
||||
personalityTrait === undefined ||
|
||||
personalityTraitTranslation === undefined
|
||||
) {
|
||||
if (personalityTrait === undefined || personalityTraitTranslation === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const name = `${personalityTraitTranslation.name} (${locale.translate(
|
||||
"Level {$level}",
|
||||
{ level: personalityTrait.level },
|
||||
)})`
|
||||
const name = `${personalityTraitTranslation.name} (${locale.translate("Level {$level}", {
|
||||
level: personalityTrait.level,
|
||||
})})`
|
||||
|
||||
return {
|
||||
value: prerequisite.active
|
||||
|
||||
@@ -11,7 +11,7 @@ export const printPrimaryAttributePrerequisite = (
|
||||
prerequisite: PrimaryAttributePrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const printPublicationPrerequisite = (
|
||||
prerequisite: PublicationPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const publication = getInstanceById("Publication", prerequisite.id)
|
||||
|
||||
@@ -9,11 +9,11 @@ import { PrerequisitePart } from "../part.js"
|
||||
*/
|
||||
export const printRacePrerequisite = (
|
||||
getInstanceById: GetInstanceById<"Race">,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translate" | "translateMap">,
|
||||
prerequisite: RacePrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const race = getInstanceById("Race", prerequisite.id)
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type {
|
||||
RatedIdentifier,
|
||||
RatedPrerequisite,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { RatedIdentifier, RatedPrerequisite } from "optolith-database-schema/gen"
|
||||
import { type GetInstanceById } from "../../../../helpers/getTypes.js"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { TranslateMap } from "../../../../helpers/translate.js"
|
||||
import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
@@ -20,56 +18,40 @@ const printRatedName = (
|
||||
| "LiturgicalChant"
|
||||
| "Ceremony"
|
||||
>,
|
||||
locale: LocaleEnvironment,
|
||||
translateMap: TranslateMap,
|
||||
id: RatedIdentifier,
|
||||
) => {
|
||||
switch (id.kind) {
|
||||
case "Attribute":
|
||||
return (
|
||||
locale.translateMap(
|
||||
getInstanceById("Attribute", id.Attribute)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("Attribute", id.Attribute)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
)
|
||||
case "Skill":
|
||||
return (
|
||||
locale.translateMap(getInstanceById("Skill", id.Skill)?.translations)
|
||||
?.name ?? MISSING_VALUE
|
||||
)
|
||||
return translateMap(getInstanceById("Skill", id.Skill)?.translations)?.name ?? MISSING_VALUE
|
||||
case "CloseCombatTechnique":
|
||||
return (
|
||||
locale.translateMap(
|
||||
getInstanceById("CloseCombatTechnique", id.CloseCombatTechnique)
|
||||
?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("CloseCombatTechnique", id.CloseCombatTechnique)?.translations)
|
||||
?.name ?? MISSING_VALUE
|
||||
)
|
||||
case "RangedCombatTechnique":
|
||||
return (
|
||||
locale.translateMap(
|
||||
getInstanceById("RangedCombatTechnique", id.RangedCombatTechnique)
|
||||
?.translations,
|
||||
translateMap(
|
||||
getInstanceById("RangedCombatTechnique", id.RangedCombatTechnique)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
)
|
||||
case "Spell":
|
||||
return (
|
||||
locale.translateMap(getInstanceById("Spell", id.Spell)?.translations)
|
||||
?.name ?? MISSING_VALUE
|
||||
)
|
||||
return translateMap(getInstanceById("Spell", id.Spell)?.translations)?.name ?? MISSING_VALUE
|
||||
case "Ritual":
|
||||
return (
|
||||
locale.translateMap(getInstanceById("Ritual", id.Ritual)?.translations)
|
||||
?.name ?? MISSING_VALUE
|
||||
)
|
||||
return translateMap(getInstanceById("Ritual", id.Ritual)?.translations)?.name ?? MISSING_VALUE
|
||||
case "LiturgicalChant":
|
||||
return (
|
||||
locale.translateMap(
|
||||
getInstanceById("LiturgicalChant", id.LiturgicalChant)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("LiturgicalChant", id.LiturgicalChant)?.translations)?.name ??
|
||||
MISSING_VALUE
|
||||
)
|
||||
case "Ceremony":
|
||||
return (
|
||||
locale.translateMap(
|
||||
getInstanceById("Ceremony", id.Ceremony)?.translations,
|
||||
)?.name ?? MISSING_VALUE
|
||||
translateMap(getInstanceById("Ceremony", id.Ceremony)?.translations)?.name ?? MISSING_VALUE
|
||||
)
|
||||
default:
|
||||
return assertExhaustive(id)
|
||||
@@ -90,17 +72,15 @@ export const printRatedPrerequisite = (
|
||||
| "LiturgicalChant"
|
||||
| "Ceremony"
|
||||
>,
|
||||
locale: LocaleEnvironment,
|
||||
locale: Pick<LocaleEnvironment, "translateMap">,
|
||||
prerequisite: RatedPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
return {
|
||||
value: `${printRatedName(getInstanceById, locale, prerequisite.id)} ${
|
||||
prerequisite.value
|
||||
}`,
|
||||
value: `${printRatedName(getInstanceById, locale.translateMap, prerequisite.id)} ${prerequisite.value}`,
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -10,40 +10,25 @@ import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { printDisplayOption } from "../displayOption.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printNumberOfTheFollowingSkills = (
|
||||
locale: LocaleEnvironment,
|
||||
number: number,
|
||||
): string =>
|
||||
locale.translate(
|
||||
".input {$count :number} {{{$count} of the following skills}}",
|
||||
{ count: number },
|
||||
)
|
||||
const printNumberOfTheFollowingSkills = (locale: LocaleEnvironment, number: number): string =>
|
||||
locale.translate(".input {$count :number} {{{$count} of the following skills}}", {
|
||||
count: number,
|
||||
})
|
||||
|
||||
const printNumberOfAllCombatTechniques = (
|
||||
locale: LocaleEnvironment,
|
||||
number: number,
|
||||
): string =>
|
||||
const printNumberOfAllCombatTechniques = (locale: LocaleEnvironment, number: number): string =>
|
||||
locale.translate(".input {$count :number} {{{$count} combat techniques}}", {
|
||||
count: number,
|
||||
})
|
||||
|
||||
const printNumberOfCloseCombatTechniques = (
|
||||
locale: LocaleEnvironment,
|
||||
number: number,
|
||||
): string =>
|
||||
locale.translate(
|
||||
".input {$count :number} {{{$count} close combat techniques}}",
|
||||
{ count: number },
|
||||
)
|
||||
const printNumberOfCloseCombatTechniques = (locale: LocaleEnvironment, number: number): string =>
|
||||
locale.translate(".input {$count :number} {{{$count} close combat techniques}}", {
|
||||
count: number,
|
||||
})
|
||||
|
||||
const printNumberOfRangedCombatTechniques = (
|
||||
locale: LocaleEnvironment,
|
||||
number: number,
|
||||
): string =>
|
||||
locale.translate(
|
||||
".input {$count :number} {{{$count} ranged combat techniques}}",
|
||||
{ count: number },
|
||||
)
|
||||
const printNumberOfRangedCombatTechniques = (locale: LocaleEnvironment, number: number): string =>
|
||||
locale.translate(".input {$count :number} {{{$count} ranged combat techniques}}", {
|
||||
count: number,
|
||||
})
|
||||
|
||||
const printNumberOfCombatTechniques = (
|
||||
locale: LocaleEnvironment,
|
||||
@@ -71,17 +56,13 @@ export const printRatedMinimumNumberPrerequisite = (
|
||||
prerequisite: RatedMinimumNumberPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
switch (prerequisite.targets.kind) {
|
||||
case "Skills": {
|
||||
const skills = prerequisite.targets.Skills.targets
|
||||
.map(
|
||||
id =>
|
||||
locale.translateMap(getInstanceById("Skill", id)?.translations)
|
||||
?.name,
|
||||
)
|
||||
.map(id => locale.translateMap(getInstanceById("Skill", id)?.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
|
||||
return {
|
||||
@@ -124,10 +105,7 @@ export const printRatedMinimumNumberPrerequisite = (
|
||||
count: prerequisite.number,
|
||||
property:
|
||||
locale.translateMap(
|
||||
getInstanceById(
|
||||
"Property",
|
||||
prerequisite.targets.Spellworks.property,
|
||||
)?.translations,
|
||||
getInstanceById("Property", prerequisite.targets.Spellworks.property)?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
minRating: prerequisite.value,
|
||||
},
|
||||
@@ -145,8 +123,7 @@ export const printRatedMinimumNumberPrerequisite = (
|
||||
count: prerequisite.number,
|
||||
aspect:
|
||||
locale.translateMap(
|
||||
getInstanceById("Aspect", prerequisite.targets.Liturgies.aspect)
|
||||
?.translations,
|
||||
getInstanceById("Aspect", prerequisite.targets.Liturgies.aspect)?.translations,
|
||||
)?.name ?? MISSING_VALUE,
|
||||
minRating: prerequisite.value,
|
||||
},
|
||||
|
||||
@@ -14,25 +14,18 @@ export const printRatedSumPrerequisite = (
|
||||
prerequisite: RatedSumPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const skills = prerequisite.targets
|
||||
.map(
|
||||
skillId =>
|
||||
locale.translateMap(getInstanceById("Skill", skillId)?.translations)
|
||||
?.name,
|
||||
)
|
||||
.map(skillId => locale.translateMap(getInstanceById("Skill", skillId)?.translations)?.name)
|
||||
.filter(isNotNullish)
|
||||
|
||||
return {
|
||||
value: locale.translate(
|
||||
"the SR for {$skill} combined must add up to at least {$minRating}",
|
||||
{
|
||||
skill: locale.join(skills, "conjunction"),
|
||||
minRating: prerequisite.sum,
|
||||
},
|
||||
),
|
||||
value: locale.translate("the SR for {$skill} combined must add up to at least {$minRating}", {
|
||||
skill: locale.join(skills, "conjunction"),
|
||||
minRating: prerequisite.sum,
|
||||
}),
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { BinarySex, SexPrerequisite } from "optolith-database-schema/gen"
|
||||
import { LocaleEnvironment } from "../../../../helpers/locale.js"
|
||||
import type { Translate } from "../../../../helpers/translate.js"
|
||||
import { PrerequisitePart } from "../part.js"
|
||||
|
||||
const printId = (locale: LocaleEnvironment, id: BinarySex): string => {
|
||||
const printId = (translate: Translate, id: BinarySex): string => {
|
||||
switch (id.kind) {
|
||||
case "Male":
|
||||
return locale.translate("Male")
|
||||
return translate("Male")
|
||||
case "Female":
|
||||
return locale.translate("Female")
|
||||
return translate("Female")
|
||||
default:
|
||||
return assertExhaustive(id)
|
||||
}
|
||||
@@ -18,10 +18,10 @@ const printId = (locale: LocaleEnvironment, id: BinarySex): string => {
|
||||
* Get the translation of a (binary) sex prerequisite.
|
||||
*/
|
||||
export const printBinarySexPrerequisite = (
|
||||
locale: LocaleEnvironment,
|
||||
translate: Translate,
|
||||
prerequisite: SexPrerequisite,
|
||||
): PrerequisitePart | undefined => ({
|
||||
value: printId(locale, prerequisite.id),
|
||||
value: printId(translate, prerequisite.id),
|
||||
sentenceType: undefined,
|
||||
isMeta: false,
|
||||
})
|
||||
|
||||
@@ -13,13 +13,11 @@ export const printSocialStatusPrerequisite = (
|
||||
prerequisite: SocialStatusPrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const socialStatus = getInstanceById("SocialStatus", prerequisite.id)
|
||||
const socialStatusTranslation = locale.translateMap(
|
||||
socialStatus?.translations,
|
||||
)
|
||||
const socialStatusTranslation = locale.translateMap(socialStatus?.translations)
|
||||
|
||||
if (socialStatusTranslation === undefined) {
|
||||
return undefined
|
||||
|
||||
@@ -13,7 +13,7 @@ export const printStatePrerequisite = (
|
||||
prerequisite: StatePrerequisite,
|
||||
): PrerequisitePart | undefined => {
|
||||
if (prerequisite.display_option !== undefined) {
|
||||
return printDisplayOption(locale, prerequisite.display_option)
|
||||
return printDisplayOption(locale.translateMap, prerequisite.display_option)
|
||||
}
|
||||
|
||||
const state = getInstanceById("State", prerequisite.id)
|
||||
|
||||
@@ -37,9 +37,8 @@ export const getBaseProfessionPackageForCurriculum = (
|
||||
(_acc: { id: string; content: ProfessionPackage } | undefined, version) =>
|
||||
getChildInstancesForInstanceId("ProfessionPackage", version.id).find(
|
||||
professionPackage =>
|
||||
professionPackage.content.experience_level === undefined ||
|
||||
professionPackage.content.experience_level ===
|
||||
idMap.ExperienceLevel.Experienced,
|
||||
idMap.ExperienceLevel.Experienced,
|
||||
),
|
||||
isNotNullish,
|
||||
undefined,
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from "optolith-database-schema/gen"
|
||||
import { Case } from "../../../../helpers/enums.js"
|
||||
import {
|
||||
getInstanceByIdR,
|
||||
getInstanceByIdFnR,
|
||||
modifiableBySpeedR,
|
||||
type StdReader,
|
||||
} from "../../reader.js"
|
||||
@@ -37,7 +37,7 @@ const deriveModifiableCastingTime = (
|
||||
"s" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> =>
|
||||
getInstanceByIdR<"SkillModificationLevel">().thenW(
|
||||
getInstanceByIdFnR<"SkillModificationLevel">().thenW(
|
||||
getInstanceById =>
|
||||
mapNullable(
|
||||
getInstanceById("SkillModificationLevel", modificationLevelId),
|
||||
|
||||
@@ -26,7 +26,7 @@ import { additionFormatter } from "../../mathOperation.js"
|
||||
import {
|
||||
formatEnergyFnR,
|
||||
formatEnergyR,
|
||||
getInstanceByIdR,
|
||||
getInstanceByIdFnR,
|
||||
modifiableBySpeedR,
|
||||
responsiveLocaleJoinR,
|
||||
responsiveR,
|
||||
@@ -53,7 +53,7 @@ import {
|
||||
const deriveModifiableCost = (
|
||||
modificationLevelId: SkillModificationLevel_ID,
|
||||
): StdReader<number | undefined, "s" | "ibi", "SkillModificationLevel"> =>
|
||||
getInstanceByIdR<"SkillModificationLevel">().thenW(
|
||||
getInstanceByIdFnR<"SkillModificationLevel">().thenW(
|
||||
getInstanceById =>
|
||||
mapNullable(
|
||||
getInstanceById("SkillModificationLevel", modificationLevelId),
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
SkillModificationLevel_ID,
|
||||
} from "optolith-database-schema/gen"
|
||||
import {
|
||||
getInstanceByIdR,
|
||||
getInstanceByIdFnR,
|
||||
modifiableBySpeedOptionalR,
|
||||
modifiableBySpeedR,
|
||||
translateFnR,
|
||||
@@ -38,7 +38,7 @@ const deriveModifiableRange = (
|
||||
"s" | "tm" | "ibi",
|
||||
"SkillModificationLevel"
|
||||
> =>
|
||||
getInstanceByIdR<"SkillModificationLevel">().thenW(
|
||||
getInstanceByIdFnR<"SkillModificationLevel">().thenW(
|
||||
getInstanceById =>
|
||||
mapNullable(
|
||||
getInstanceById("SkillModificationLevel", modificationLevelId),
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type RawDefinitionListEntityDescriptionSectionItem } from "../../../../index.js"
|
||||
import {
|
||||
getInstanceByIdR,
|
||||
getInstanceByIdFnR,
|
||||
translateMapR,
|
||||
translateR,
|
||||
type StdReader,
|
||||
@@ -17,7 +17,7 @@ import { MISSING_VALUE } from "../../unknown.js"
|
||||
import { appendInParensIfNotEmpty } from "./parensIf.js"
|
||||
|
||||
const renderPredefined = (targetCategoryId: TargetCategory_ID) =>
|
||||
getInstanceByIdR<"TargetCategory">()
|
||||
getInstanceByIdFnR<"TargetCategory">()
|
||||
.thenW(
|
||||
getInstanceById =>
|
||||
mapNullable(
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
} from "optolith-database-schema/gen"
|
||||
import { type IdMap } from "../../../index.js"
|
||||
import {
|
||||
getInstanceByIdR,
|
||||
getInstanceByIdFnR,
|
||||
responsiveR,
|
||||
responsiveThenR,
|
||||
responsiveTranslateR,
|
||||
@@ -50,7 +50,7 @@ const renderSkillCheckPenalty = (
|
||||
penalty: SkillCheckPenalty,
|
||||
): StdReader<string, "t" | "tm" | "rts" | "ibi", "DerivedCharacteristic"> => {
|
||||
const getDerivedCharacteristicTranslation = (id: string) =>
|
||||
getInstanceByIdR<"DerivedCharacteristic">()
|
||||
getInstanceByIdFnR<"DerivedCharacteristic">()
|
||||
.map(getInstanceById => getInstanceById("DerivedCharacteristic", id))
|
||||
.thenW(dc =>
|
||||
dc === undefined
|
||||
|
||||
+165
-52
@@ -2,6 +2,7 @@ import { isNotNullish } from "@elyukai/utils/nullable"
|
||||
import { Reader } from "@elyukai/utils/reader"
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import type {
|
||||
ChildEntityMap,
|
||||
EntityMap,
|
||||
FastSkillModificationLevelConfig,
|
||||
ResponsiveText,
|
||||
@@ -9,13 +10,15 @@ import type {
|
||||
SkillModificationLevel,
|
||||
SlowSkillModificationLevelConfig,
|
||||
} from "optolith-database-schema/gen"
|
||||
import type { GetInstanceById } from "../../helpers/getTypes.js"
|
||||
import type { IdArgsVariant } from "tsondb/schema/gen"
|
||||
import type {
|
||||
LocaleCompare,
|
||||
LocaleJoin,
|
||||
LocaleJoinType,
|
||||
} from "../../helpers/locale.js"
|
||||
GetAllChildInstancesForParent,
|
||||
GetAllInstances,
|
||||
GetInstanceById,
|
||||
} from "../../helpers/getTypes.js"
|
||||
import type { LocaleCompare, LocaleJoin, LocaleJoinType } from "../../helpers/locale.js"
|
||||
import type {
|
||||
Format,
|
||||
LocaleMap,
|
||||
Translate,
|
||||
TranslateMap,
|
||||
@@ -24,15 +27,22 @@ import type {
|
||||
TranslationParamsInArray,
|
||||
Translations,
|
||||
} from "../../helpers/translate.js"
|
||||
import type { GetResolvedSelectOptionById } from "./prerequisites/single/activatable.js"
|
||||
import type { ModifiableParameter } from "./rated/activatable/nonModifiableSuffix.js"
|
||||
import { Speed } from "./rated/activatable/speed.js"
|
||||
import { responsive, ResponsiveTextSize } from "./responsiveText.js"
|
||||
import { formatEnergy, type EnergyUnit } from "./units/energy.js"
|
||||
import { MISSING_VALUE } from "./unknown.js"
|
||||
|
||||
/**
|
||||
* The standard set of environment properties for readers in this project.
|
||||
*/
|
||||
export type EnvMap<E extends keyof EntityMap = never> = {
|
||||
export type EnvMap<
|
||||
E extends keyof EntityMap = never,
|
||||
AE extends keyof EntityMap = never,
|
||||
CE extends keyof ChildEntityMap = never,
|
||||
> = {
|
||||
format: Format
|
||||
translate: Translate
|
||||
translateMap: TranslateMap
|
||||
localeJoin: LocaleJoin
|
||||
@@ -40,16 +50,18 @@ export type EnvMap<E extends keyof EntityMap = never> = {
|
||||
responsiveTextSize: ResponsiveTextSize
|
||||
speed: Speed
|
||||
energyUnit: EnergyUnit
|
||||
nonModifiableSuffix?: (
|
||||
param: ModifiableParameter,
|
||||
) => TranslationKeysWithoutParams
|
||||
nonModifiableSuffix?: (param: ModifiableParameter) => TranslationKeysWithoutParams
|
||||
getInstanceById: GetInstanceById<E>
|
||||
getAllInstances: GetAllInstances<AE>
|
||||
getChildInstancesForInstanceId: GetAllChildInstancesForParent<CE>
|
||||
getResolvedSelectOptionById: GetResolvedSelectOptionById
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcuts for selecting keys of the shared environment map type.
|
||||
*/
|
||||
export type EnvMapAbbr = {
|
||||
f: "format"
|
||||
t: "translate"
|
||||
tm: "translateMap"
|
||||
lj: "localeJoin"
|
||||
@@ -59,6 +71,9 @@ export type EnvMapAbbr = {
|
||||
eu: "energyUnit"
|
||||
nms: "nonModifiableSuffix"
|
||||
ibi: "getInstanceById"
|
||||
ai: "getAllInstances"
|
||||
acibp: "getChildInstancesForInstanceId"
|
||||
rso: "getResolvedSelectOptionById"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,9 +82,11 @@ export type EnvMapAbbr = {
|
||||
* The keys are abbreviated to keep type annotations short.
|
||||
*/
|
||||
export type StdEnv<
|
||||
in K extends keyof EnvMapAbbr = keyof EnvMapAbbr,
|
||||
in E extends keyof EntityMap = never,
|
||||
> = Pick<EnvMap<E>, EnvMapAbbr[K]>
|
||||
K extends keyof EnvMapAbbr = keyof EnvMapAbbr,
|
||||
E extends keyof EntityMap = never,
|
||||
AE extends keyof EntityMap = never,
|
||||
CE extends keyof ChildEntityMap = never,
|
||||
> = Pick<EnvMap<E, AE, CE>, EnvMapAbbr[K]>
|
||||
|
||||
/**
|
||||
* Shortcut for a reader with common environment properties.
|
||||
@@ -80,24 +97,34 @@ export type StdReader<
|
||||
T,
|
||||
K extends keyof EnvMapAbbr,
|
||||
E extends keyof EntityMap = never,
|
||||
> = Reader<StdEnv<K, E>, T>
|
||||
AE extends keyof EntityMap = never,
|
||||
CE extends keyof ChildEntityMap = never,
|
||||
> = Reader<StdEnv<K, E, AE, CE>, T>
|
||||
|
||||
// Specialized constructors for common contexts
|
||||
|
||||
/**
|
||||
* Treats the text as a translation string and applies supplied arguments.
|
||||
*/
|
||||
export const formatR = (
|
||||
text: string,
|
||||
args?: Record<string, unknown> | undefined,
|
||||
): Reader<{ format: Format }, string> => Reader.asks(env => env.format(text, args))
|
||||
|
||||
/**
|
||||
* Creates a value from a translation key.
|
||||
*/
|
||||
export const translateR = <K extends keyof Translations>(
|
||||
key: K,
|
||||
...rest: TranslationParamsInArray<K>
|
||||
): Reader<{ translate: Translate }, string> =>
|
||||
Reader.asks(env => env.translate(key, ...rest))
|
||||
): Reader<{ translate: Translate }, string> => Reader.asks(env => env.translate(key, ...rest))
|
||||
|
||||
/**
|
||||
* Returns the `translate` function from the context.
|
||||
*/
|
||||
export const translateFnR: Reader<{ translate: Translate }, Translate> =
|
||||
Reader.asks(env => env.translate)
|
||||
export const translateFnR: Reader<{ translate: Translate }, Translate> = Reader.asks(
|
||||
env => env.translate,
|
||||
)
|
||||
|
||||
/**
|
||||
* Takes the appropriate translation from a locale map.
|
||||
@@ -110,10 +137,80 @@ export const translateMapR = <T>(
|
||||
/**
|
||||
* Returns the `translateMap` function from the context.
|
||||
*/
|
||||
export const translateMapFnR: Reader<
|
||||
{ translateMap: TranslateMap },
|
||||
TranslateMap
|
||||
> = Reader.asks(env => env.translateMap)
|
||||
export const translateMapFnR: Reader<{ translateMap: TranslateMap }, TranslateMap> = Reader.asks(
|
||||
env => env.translateMap,
|
||||
)
|
||||
|
||||
/**
|
||||
* Retrieves the translation for the current locale from the given value’s `translations` property.
|
||||
* @returns `undefined` if the value does not exist or does not have a translation for the current locale, otherwise the translation.
|
||||
*/
|
||||
export const translationR = <T>(
|
||||
value: { translations?: LocaleMap<T> } | undefined,
|
||||
): Reader<{ translateMap: TranslateMap }, T | undefined> =>
|
||||
Reader.asks(env => env.translateMap(value?.translations))
|
||||
|
||||
/**
|
||||
* Retrieves a specific property of the specified entry.
|
||||
*/
|
||||
export const mapTranslationR = <
|
||||
E extends keyof EntityMap &
|
||||
{
|
||||
[K in keyof EntityMap]: EntityMap[K] extends { translations: LocaleMap<object> } ? K : never
|
||||
}[keyof EntityMap],
|
||||
R,
|
||||
>(
|
||||
...args: [
|
||||
...IdArgsVariant<EntityMap, E>,
|
||||
fn: (
|
||||
translation: EntityMap[E] extends { translations: LocaleMap<infer T> }
|
||||
? T | undefined
|
||||
: never,
|
||||
) => R,
|
||||
]
|
||||
): Reader<{ translateMap: TranslateMap; getInstanceById: GetInstanceById<E> }, R | undefined> =>
|
||||
Reader.asks(env => {
|
||||
const idArgs = args.length === 3 ? ([args[0], args[1]] as const) : ([args[0]] as const)
|
||||
const fn = args.length === 3 ? args[2] : args[1]
|
||||
const translation = env.translateMap<object>(env.getInstanceById(...idArgs)?.translations)
|
||||
return fn(
|
||||
translation as EntityMap[E] extends { translations: LocaleMap<infer T> }
|
||||
? T | undefined
|
||||
: never,
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* Retrieves the `name` property of the specified entry.
|
||||
*/
|
||||
export const nameR = <
|
||||
E extends {
|
||||
[K in keyof EntityMap]: EntityMap[K] extends { translations: LocaleMap<{ name: string }> }
|
||||
? K
|
||||
: never
|
||||
}[keyof EntityMap],
|
||||
>(
|
||||
...args: IdArgsVariant<EntityMap, E>
|
||||
): Reader<
|
||||
{ translateMap: TranslateMap; getInstanceById: GetInstanceById<E> },
|
||||
string | undefined
|
||||
> => Reader.asks(env => env.translateMap(env.getInstanceById(...args)?.translations)?.name)
|
||||
|
||||
/**
|
||||
* Retrieves the `name` property of the specified entry.
|
||||
*
|
||||
* Returns {@link MISSING_VALUE} if there is no name for the entry in the locale. This can happen if the entry itself does not exist, or it does not have a translation for the current locale.
|
||||
*/
|
||||
export const strictNameR = <
|
||||
E extends {
|
||||
[K in keyof EntityMap]: EntityMap[K] extends { translations: LocaleMap<{ name: string }> }
|
||||
? K
|
||||
: never
|
||||
}[keyof EntityMap],
|
||||
>(
|
||||
...args: IdArgsVariant<EntityMap, E>
|
||||
): Reader<{ translateMap: TranslateMap; getInstanceById: GetInstanceById<E> }, string> =>
|
||||
nameR(...args).map(name => name ?? MISSING_VALUE)
|
||||
|
||||
/**
|
||||
* Joins a list of strings according to the locale’s rules for the given type.
|
||||
@@ -121,27 +218,50 @@ export const translateMapFnR: Reader<
|
||||
export const localeJoinR = (
|
||||
arr: string[],
|
||||
type: LocaleJoinType,
|
||||
): Reader<{ localeJoin: LocaleJoin }, string> =>
|
||||
Reader.asks(env => env.localeJoin(arr, type))
|
||||
): Reader<{ localeJoin: LocaleJoin }, string> => Reader.asks(env => env.localeJoin(arr, type))
|
||||
|
||||
/**
|
||||
* Returns a function to retrieve an instance from the database by its entity name and ID.
|
||||
*/
|
||||
export const getInstanceByIdR = <E extends keyof EntityMap = never>(): Reader<
|
||||
export const getInstanceByIdFnR = <E extends keyof EntityMap = never>(): Reader<
|
||||
{ getInstanceById: GetInstanceById<E> },
|
||||
GetInstanceById<E>
|
||||
> => Reader.asks(env => env.getInstanceById)
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the database by its entity name and ID.
|
||||
*/
|
||||
export const getInstanceByIdR = <E extends keyof EntityMap>(
|
||||
...args: IdArgsVariant<EntityMap, E>
|
||||
): Reader<{ getInstanceById: GetInstanceById<E> }, EntityMap[E] | undefined> =>
|
||||
Reader.asks(env => env.getInstanceById(...args))
|
||||
|
||||
/**
|
||||
* Retrieves all instances of an entity from the database by their entity name.
|
||||
*/
|
||||
export const getAllInstancesR = <E extends keyof EntityMap>(
|
||||
entityName: E,
|
||||
): Reader<{ getAllInstances: GetAllInstances<E> }, { id: string; content: EntityMap[E] }[]> =>
|
||||
Reader.asks(env => env.getAllInstances(entityName))
|
||||
|
||||
/**
|
||||
* Retrieves all child instances of an entity from the database by their child entity name and their parent’s identifier.
|
||||
*/
|
||||
export const getChildInstancesForInstanceIdR = <CE extends keyof ChildEntityMap>(
|
||||
entityName: CE,
|
||||
parentId: ChildEntityMap[CE][2],
|
||||
): Reader<
|
||||
{ getChildInstancesForInstanceId: GetAllChildInstancesForParent<CE> },
|
||||
{ id: string; content: ChildEntityMap[CE][0] }[]
|
||||
> => Reader.asks(env => env.getChildInstancesForInstanceId(entityName, parentId))
|
||||
|
||||
/**
|
||||
* Joins a list of strings according to the locale’s rules for the given type.
|
||||
*/
|
||||
export const responsiveLocaleJoinR = (
|
||||
arr: string[],
|
||||
type: LocaleJoinType,
|
||||
): Reader<
|
||||
{ localeJoin: LocaleJoin; responsiveTextSize: ResponsiveTextSize },
|
||||
string
|
||||
> =>
|
||||
): Reader<{ localeJoin: LocaleJoin; responsiveTextSize: ResponsiveTextSize }, string> =>
|
||||
Reader.asks(({ localeJoin, responsiveTextSize }) =>
|
||||
responsive(
|
||||
responsiveTextSize,
|
||||
@@ -164,10 +284,17 @@ export const responsiveLocaleJoinR = (
|
||||
/**
|
||||
* Compares two strings according to the locale’s sorting rules.
|
||||
*/
|
||||
export const localeCompareR: Reader<
|
||||
{ localeCompare: LocaleCompare },
|
||||
LocaleCompare
|
||||
> = Reader.asks(env => env.localeCompare)
|
||||
export const localeCompareR: Reader<{ localeCompare: LocaleCompare }, LocaleCompare> = Reader.asks(
|
||||
env => env.localeCompare,
|
||||
)
|
||||
|
||||
/**
|
||||
* Sorts an array of strings according to the locale’s sorting rules.
|
||||
*/
|
||||
export const localeSortR = <T extends string>(
|
||||
arr: T[],
|
||||
): Reader<{ localeCompare: LocaleCompare }, T[]> =>
|
||||
Reader.asks(({ localeCompare }) => arr.toSorted(localeCompare))
|
||||
|
||||
/**
|
||||
* Creates a responsive value from two functions that return the value for the full and compressed version, respectively.
|
||||
@@ -176,9 +303,7 @@ export const responsiveR = <T>(
|
||||
full: () => T,
|
||||
compressed: () => T,
|
||||
): Reader<{ responsiveTextSize: ResponsiveTextSize }, T> =>
|
||||
Reader.asks(({ responsiveTextSize }) =>
|
||||
responsive(responsiveTextSize, full, compressed),
|
||||
)
|
||||
Reader.asks(({ responsiveTextSize }) => responsive(responsiveTextSize, full, compressed))
|
||||
|
||||
/**
|
||||
* Creates a responsive value from two functions that return the value for the full and compressed version, respectively.
|
||||
@@ -208,10 +333,7 @@ export const responsiveTranslateR = <
|
||||
fullKey: K,
|
||||
compressedKey: K2,
|
||||
...rest: TranslationParamsInArray<K> & TranslationParamsInArray<K2>
|
||||
): Reader<
|
||||
{ translate: Translate; responsiveTextSize: ResponsiveTextSize },
|
||||
string
|
||||
> =>
|
||||
): Reader<{ translate: Translate; responsiveTextSize: ResponsiveTextSize }, string> =>
|
||||
Reader.asks(({ translate, responsiveTextSize }) =>
|
||||
responsive(
|
||||
responsiveTextSize,
|
||||
@@ -254,9 +376,7 @@ export const responsiveTextOptionalR = (
|
||||
export const formatEnergyR = (
|
||||
value: string | number,
|
||||
): Reader<{ translate: Translate; energyUnit: EnergyUnit }, string> =>
|
||||
Reader.asks(({ translate, energyUnit }) =>
|
||||
formatEnergy(translate, energyUnit, value),
|
||||
)
|
||||
Reader.asks(({ translate, energyUnit }) => formatEnergy(translate, energyUnit, value))
|
||||
|
||||
/**
|
||||
* Formats the given energy cost value with the appropriate unit based on the entity type.
|
||||
@@ -330,19 +450,12 @@ export const modifyBySpeedR: Reader<
|
||||
) => SpeedMap[S][K]
|
||||
> = Reader.asks(
|
||||
({ speed }) =>
|
||||
<S extends Speed, K extends keyof SpeedMap[S]>(
|
||||
key: K,
|
||||
level: SkillModificationLevel,
|
||||
) => {
|
||||
<S extends Speed, K extends keyof SpeedMap[S]>(key: K, level: SkillModificationLevel) => {
|
||||
switch (speed) {
|
||||
case Speed.Fast:
|
||||
return level.fast[
|
||||
key as keyof FastSkillModificationLevelConfig
|
||||
] as SpeedMap[S][K]
|
||||
return level.fast[key as keyof FastSkillModificationLevelConfig] as SpeedMap[S][K]
|
||||
case Speed.Slow:
|
||||
return level.slow[
|
||||
key as keyof SlowSkillModificationLevelConfig
|
||||
] as SpeedMap[S][K]
|
||||
return level.slow[key as keyof SlowSkillModificationLevelConfig] as SpeedMap[S][K]
|
||||
default:
|
||||
return assertExhaustive(speed)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+303
-544
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import { Compare } from "@optolith/helpers/compare"
|
||||
import type { LocaleMeasurementAdjustments } from "optolith-database-schema/gen"
|
||||
import { Translate, TranslateMap } from "./translate.js"
|
||||
import { Translate, TranslateMap, type Format } from "./translate.js"
|
||||
|
||||
/**
|
||||
* The type of list to join in a locale-aware way.
|
||||
@@ -12,6 +12,7 @@ export type LocaleJoinType = "conjunction" | "disjunction" | "unit"
|
||||
*/
|
||||
export type LocaleEnvironment = {
|
||||
id: string
|
||||
format: Format
|
||||
translate: Translate
|
||||
translateMap: TranslateMap
|
||||
compare: LocaleCompare
|
||||
|
||||
@@ -7,6 +7,11 @@ import { ResponsiveTextSize } from "../entities/partial/responsiveText.js"
|
||||
*/
|
||||
export type Translations = NonNullable<Locale["translations"]>
|
||||
|
||||
/**
|
||||
* A function that formats a string with the given arguments.
|
||||
*/
|
||||
export type Format = (text: string, args?: Record<string, unknown>) => string
|
||||
|
||||
/**
|
||||
* Translates a given key into a string, optionally with parameters.
|
||||
*/
|
||||
|
||||
+2
-1
@@ -42,6 +42,7 @@ import { getOptionalRuleEntityDescription } from "./entities/optionalRule.js"
|
||||
import type { GetResolvedSelectOptionById } from "./entities/partial/prerequisites/single/activatable.js"
|
||||
import { getPersonalityTraitEntityDescription } from "./entities/personalityTrait.js"
|
||||
import { getPoisonEntityDescription } from "./entities/poison.js"
|
||||
import { getProfessionVersionEntityDescription } from "./entities/profession.js"
|
||||
import { getRaceEntityDescription } from "./entities/race.js"
|
||||
import { getSexPracticeEntityDescription } from "./entities/sexPractice.js"
|
||||
import { getSkillEntityDescription } from "./entities/skill.js"
|
||||
@@ -260,7 +261,7 @@ const registeredEntityDescriptionCreators = {
|
||||
DerivedCharacteristic: getDerivedCharacteristicEntityDescription,
|
||||
Race: getRaceEntityDescription,
|
||||
Culture: getCultureEntityDescription,
|
||||
// ProfessionVersion: getProfessionVersionEntityDescription,
|
||||
ProfessionVersion: getProfessionVersionEntityDescription,
|
||||
Advantage: getActivatableEntityDescription,
|
||||
Disadvantage: getActivatableEntityDescription,
|
||||
// core values
|
||||
|
||||
@@ -19,7 +19,12 @@ describe("joinPrerequisiteParts", () => {
|
||||
},
|
||||
{ value: "C", sentenceType: undefined, isMeta: false },
|
||||
]
|
||||
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
|
||||
const result = joinPrerequisiteParts(
|
||||
defaultLocaleEnvironment.translate,
|
||||
defaultLocaleEnvironment.translateMap,
|
||||
defaultLocaleEnvironment.compare,
|
||||
parts.map(part => ({ type: "test", part })),
|
||||
)
|
||||
assert.equal(result, "A, Label for B, C")
|
||||
})
|
||||
|
||||
@@ -32,7 +37,12 @@ describe("joinPrerequisiteParts", () => {
|
||||
{ value: "E", sentenceType: undefined, isMeta: false },
|
||||
{ value: "F", sentenceType: Case("Connected"), isMeta: false },
|
||||
]
|
||||
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
|
||||
const result = joinPrerequisiteParts(
|
||||
defaultLocaleEnvironment.translate,
|
||||
defaultLocaleEnvironment.translateMap,
|
||||
defaultLocaleEnvironment.compare,
|
||||
parts.map(part => ({ type: "test", part })),
|
||||
)
|
||||
assert.equal(result, "A; B; C; D, E; F")
|
||||
})
|
||||
|
||||
@@ -49,7 +59,12 @@ describe("joinPrerequisiteParts", () => {
|
||||
{ value: "I", sentenceType: undefined, isMeta: false },
|
||||
{ value: "J", sentenceType: Case("Standalone"), isMeta: false },
|
||||
]
|
||||
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
|
||||
const result = joinPrerequisiteParts(
|
||||
defaultLocaleEnvironment.translate,
|
||||
defaultLocaleEnvironment.translateMap,
|
||||
defaultLocaleEnvironment.compare,
|
||||
parts.map(part => ({ type: "test", part })),
|
||||
)
|
||||
assert.equal(result, "A. B. C. D, E. F. G. H, I. J.")
|
||||
})
|
||||
|
||||
@@ -59,7 +74,12 @@ describe("joinPrerequisiteParts", () => {
|
||||
{ value: "B", sentenceType: undefined, isMeta: true },
|
||||
{ value: "C", sentenceType: undefined, isMeta: true },
|
||||
]
|
||||
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
|
||||
const result = joinPrerequisiteParts(
|
||||
defaultLocaleEnvironment.translate,
|
||||
defaultLocaleEnvironment.translateMap,
|
||||
defaultLocaleEnvironment.compare,
|
||||
parts.map(part => ({ type: "test", part })),
|
||||
)
|
||||
assert.equal(result, "none, A, B, C")
|
||||
})
|
||||
|
||||
@@ -68,7 +88,13 @@ describe("joinPrerequisiteParts", () => {
|
||||
// @ts-expect-error Testing invalid input
|
||||
{ value: "A", sentenceType: Case("any"), isMeta: true },
|
||||
]
|
||||
const block = () => joinPrerequisiteParts(defaultLocaleEnvironment, parts)
|
||||
const block = () =>
|
||||
joinPrerequisiteParts(
|
||||
defaultLocaleEnvironment.translate,
|
||||
defaultLocaleEnvironment.translateMap,
|
||||
defaultLocaleEnvironment.compare,
|
||||
parts.map(part => ({ type: "test", part })),
|
||||
)
|
||||
assert.throws(block)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { assertExhaustive } from "@elyukai/utils/typeSafety"
|
||||
import { LocaleEnvironment } from "../../src/helpers/locale.js"
|
||||
import { translateMapMock, translateMock } from "./translate.js"
|
||||
import { formatMock, translateMapMock, translateMock } from "./translate.js"
|
||||
|
||||
const localeId = "en-US"
|
||||
|
||||
@@ -21,6 +21,7 @@ const unitListFormat = new Intl.ListFormat(localeId, {
|
||||
*/
|
||||
export const defaultLocaleEnvironment: LocaleEnvironment = {
|
||||
id: "en-US",
|
||||
format: formatMock,
|
||||
translate: translateMock,
|
||||
translateMap: translateMapMock,
|
||||
compare: (x, y) => collator.compare(x, y),
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { MessageFormat } from "messageformat"
|
||||
import { Translate, TranslateMap } from "../../src/helpers/translate.js"
|
||||
import {
|
||||
Translate,
|
||||
TranslateMap,
|
||||
type Format,
|
||||
} from "../../src/helpers/translate.js"
|
||||
|
||||
/**
|
||||
* A mocked format function.
|
||||
*/
|
||||
export const formatMock: Format = (text, args) =>
|
||||
new MessageFormat("en", text).format(args)
|
||||
|
||||
/**
|
||||
* A mocked translate function.
|
||||
|
||||
Reference in New Issue
Block a user