feat: print prerequisites

This commit is contained in:
Lukas Obermann
2025-01-19 13:34:58 +01:00
parent 625f222759
commit 235f6eb08d
35 changed files with 4765 additions and 7 deletions
@@ -0,0 +1,506 @@
import { mapObject } from "@optolith/helpers/object"
import { romanize } from "@optolith/helpers/roman"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { ResolvedSelectOption } from "optolith-database-schema/cache/activatableSelectOptions"
import {
ActivatableIdentifier,
SelectOptionIdentifier,
} from "optolith-database-schema/types/_IdentifierGroup"
import { LocaleMap } from "optolith-database-schema/types/_LocaleMap"
import { GetById } from "../../helpers/getTypes.js"
import {
AdvantageIdentifier,
DisadvantageIdentifier,
KarmaSpecialAbilityIdentifier,
} from "../../helpers/identifiers.js"
import { LocaleEnvironment } from "../../helpers/locale.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:
| ActivatableNameChunk
| [ActivatableNameChunk, ActivatableNameChunk]
base: ActivatableNameChunk
options: (
| ActivatableNameChunk
| [ActivatableNameChunk, ActivatableNameChunk]
)[]
}
/**
* A part of the name, which can be a locale map, a string for all locales or a
* function returning a string.
*/
export type ActivatableNameChunk =
| LocaleMap<string>
| string
| ((locale: LocaleEnvironment) => string)
const combineChunks = (
a: ActivatableNameChunk,
b: ActivatableNameChunk,
join: (a: string, b: string) => string,
): ActivatableNameChunk => {
if (typeof a === "string") {
if (typeof b === "string") {
return join(a, b)
} else if (typeof b === "function") {
return fs => join(a, b(fs))
} else if (typeof b === "object") {
return mapObject(b, bValue => join(a, bValue))
}
return b
} else if (typeof a === "function") {
if (typeof b === "string") {
return fs => join(a(fs), b)
} 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 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))
} else if (typeof b === "object") {
const ret: LocaleMap<string> = {}
for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {
ret[key] = join(a[key] ?? MISSING_VALUE, b[key] ?? MISSING_VALUE)
}
return ret
}
return b
}
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 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}`)
}
return chunk
})
return withNormalizedPairs.reduce(
(acc, chunk) =>
acc === "" ? chunk : combineChunks(acc, chunk, (a, b) => `${a}, ${b}`),
"",
)
}
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)}`
const wrapParens = (str: string) => `(${str})`
const appendOptions = useParenthesis
? (baseStr: string, optionsStr: string) =>
optionsStr === "" ? baseStr : `${baseStr} ${wrapParens(optionsStr)}`
: (baseStr: string, optionsStr: string) =>
optionsStr === "" ? baseStr : `${baseStr} ${optionsStr}`
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,
),
}
default:
return assertExhaustive(levelPlacement)
}
}
const getEntrySpecificFullName = (
getAspectById: GetById.Static.Aspect,
locale: LocaleEnvironment,
id: ActivatableIdentifier,
base: ActivatableNameChunk,
level: number | undefined,
options: SelectOptionIdentifier[] | undefined,
printedOptions: ActivatableNameComponents["options"],
): Pick<ActivatableNameComponents, "full" | "fullWithoutLevel"> | undefined => {
switch (id.tag) {
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,
)
}
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
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.advanced_skill_special_ability) {
// 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 "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.general_special_ability) {
// 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 "InstrumentEnchantment":
return undefined
case "KarmaSpecialAbility":
switch (id.karma_special_ability) {
case KarmaSpecialAbilityIdentifier.MasterOfAspect: {
const [aspectId] = 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,
[],
)
}
default:
return undefined
}
case "Krallenkettenzauber":
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)
}
}
/**
* Gets the name components for an activatable entry.
*/
export const getNameComponents = <T>(
getAspectById: GetById.Static.Aspect,
locale: LocaleEnvironment,
id: ActivatableIdentifier,
options: SelectOptionIdentifier[] | undefined,
level: number | undefined,
translations: LocaleMap<T>,
getBaseName: (translation: T) => string,
getSelectOptionById: (
id: SelectOptionIdentifier,
) => ResolvedSelectOption | undefined,
displayedInProfession: boolean,
): ActivatableNameComponents => {
const base = mapObject(translations, getBaseName)
const nameOptions: ActivatableNameComponents["options"] = (() => {
const arr =
options?.map(optionId => {
const optTranslations = getSelectOptionById(optionId)?.translations
return optTranslations === undefined
? MISSING_VALUE
: mapObject(optTranslations, t10n =>
displayedInProfession
? t10n.name_in_profession ?? t10n.name
: t10n.name,
)
}) ?? []
if (arr.length > 1) {
const [first, ...rest] = arr
return [[first!, zipChunks(rest)]]
}
return arr
})()
return {
...(getEntrySpecificFullName(
getAspectById,
locale,
id,
base,
level,
options,
nameOptions,
) ?? combineBaseName(base, level, nameOptions)),
base,
options: nameOptions,
}
}
/**
* 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
}
@@ -0,0 +1,28 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { DisplayOption } from "optolith-database-schema/types/prerequisites/DisplayOption"
import { LocaleEnvironment } from "../../../helpers/locale.js"
import { MISSING_VALUE } from "../unknown.js"
import { PrerequisitePart } from "./part.js"
/**
* Get the translation of a display option.
*/
export const printDisplayOption = (
locale: LocaleEnvironment,
displayOption: DisplayOption,
): PrerequisitePart | undefined => {
switch (displayOption.tag) {
case "Hide":
return undefined
case "ReplaceWith":
return {
value:
locale.translateMap(displayOption.replace_with.translations) ??
MISSING_VALUE,
sentenceType: displayOption.replace_with.sentence_type,
isMeta: false,
}
default:
return assertExhaustive(displayOption)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,47 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { SentenceType } from "optolith-database-schema/types/prerequisites/single/TextPrerequisite"
import { LocaleEnvironment } from "../../../helpers/locale.js"
/**
* A part of the total list of prerequisites.
*/
export type PrerequisitePart = {
label?: string
value: string
sentenceType: SentenceType | undefined
isMeta: boolean
}
/**
* Join prerequisite parts using their configuration.
*/
export const joinPrerequisiteParts = (
locale: LocaleEnvironment,
parts: 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) {
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") : "",
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,467 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { ResolvedSelectOption } from "optolith-database-schema/cache/activatableSelectOptions"
import {
ActivatableIdentifier,
SelectOptionIdentifier,
} from "optolith-database-schema/types/_IdentifierGroup"
import { LocaleMap } from "optolith-database-schema/types/_LocaleMap"
import { ActivatablePrerequisite } from "optolith-database-schema/types/prerequisites/single/ActivatablePrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import {
getNameComponents,
printActivatableNameChunk,
} from "../../activatableNameChunks.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
const getTranslationsForActivatable = (
getAdvantageById: GetById.Static.Advantage,
getDisadvantageById: GetById.Static.Disadvantage,
getAdvancedCombatSpecialAbilityById: GetById.Static.AdvancedCombatSpecialAbility,
getAdvancedKarmaSpecialAbilityById: GetById.Static.AdvancedKarmaSpecialAbility,
getAdvancedMagicalSpecialAbilityById: GetById.Static.AdvancedMagicalSpecialAbility,
getAdvancedSkillSpecialAbilityById: GetById.Static.AdvancedSkillSpecialAbility,
getAncestorGlyphById: GetById.Static.AncestorGlyph,
getArcaneOrbEnchantmentById: GetById.Static.ArcaneOrbEnchantment,
getAttireEnchantmentById: GetById.Static.AttireEnchantment,
getBlessedTraditionById: GetById.Static.BlessedTradition,
getBowlEnchantmentById: GetById.Static.BowlEnchantment,
getBrawlingSpecialAbilityById: GetById.Static.BrawlingSpecialAbility,
getCauldronEnchantmentById: GetById.Static.CauldronEnchantment,
getCeremonialItemSpecialAbilityById: GetById.Static.CeremonialItemSpecialAbility,
getChronicleEnchantmentById: GetById.Static.ChronicleEnchantment,
getCombatSpecialAbilityById: GetById.Static.CombatSpecialAbility,
getCombatStyleSpecialAbilityById: GetById.Static.CombatStyleSpecialAbility,
getCommandSpecialAbilityById: GetById.Static.CommandSpecialAbility,
getDaggerRitualById: GetById.Static.DaggerRitual,
getFamiliarSpecialAbilityById: GetById.Static.FamiliarSpecialAbility,
getFatePointSexSpecialAbilityById: GetById.Static.FatePointSexSpecialAbility,
getFatePointSpecialAbilityById: GetById.Static.FatePointSpecialAbility,
getFoolsHatEnchantmentById: GetById.Static.FoolsHatEnchantment,
getGeneralSpecialAbilityById: GetById.Static.GeneralSpecialAbility,
getInstrumentEnchantmentById: GetById.Static.InstrumentEnchantment,
getKarmaSpecialAbilityById: GetById.Static.KarmaSpecialAbility,
getKrallenkettenzauberById: GetById.Static.Krallenkettenzauber,
getLiturgicalStyleSpecialAbilityById: GetById.Static.LiturgicalStyleSpecialAbility,
getLycantropicGiftById: GetById.Static.LycantropicGift,
getMagicalSignById: GetById.Static.MagicalSign,
getMagicalSpecialAbilityById: GetById.Static.MagicalSpecialAbility,
getMagicalTraditionById: GetById.Static.MagicalTradition,
getMagicStyleSpecialAbilityById: GetById.Static.MagicStyleSpecialAbility,
getOrbEnchantmentById: GetById.Static.OrbEnchantment,
getPactGiftById: GetById.Static.PactGift,
getProtectiveWardingCircleSpecialAbilityById: GetById.Static.ProtectiveWardingCircleSpecialAbility,
getRingEnchantmentById: GetById.Static.RingEnchantment,
getSermonById: GetById.Static.Sermon,
getSexSpecialAbilityById: GetById.Static.SexSpecialAbility,
getSickleRitualById: GetById.Static.SickleRitual,
getSikaryanDrainSpecialAbilityById: GetById.Static.SikaryanDrainSpecialAbility,
getSkillStyleSpecialAbilityById: GetById.Static.SkillStyleSpecialAbility,
getSpellSwordEnchantmentById: GetById.Static.SpellSwordEnchantment,
getStaffEnchantmentById: GetById.Static.StaffEnchantment,
getToyEnchantmentById: GetById.Static.ToyEnchantment,
getTrinkhornzauberById: GetById.Static.Trinkhornzauber,
getVampiricGiftById: GetById.Static.VampiricGift,
getVisionById: GetById.Static.Vision,
getWandEnchantmentById: GetById.Static.WandEnchantment,
getWeaponEnchantmentById: GetById.Static.WeaponEnchantment,
id: ActivatableIdentifier,
): { translations: LocaleMap<{ name: string }> } | undefined => {
switch (id.tag) {
case "Advantage":
return getAdvantageById(id.advantage)
case "Disadvantage":
return getDisadvantageById(id.disadvantage)
case "AdvancedCombatSpecialAbility":
return getAdvancedCombatSpecialAbilityById(
id.advanced_combat_special_ability,
)
case "AdvancedKarmaSpecialAbility":
return getAdvancedKarmaSpecialAbilityById(
id.advanced_karma_special_ability,
)
case "AdvancedMagicalSpecialAbility":
return getAdvancedMagicalSpecialAbilityById(
id.advanced_magical_special_ability,
)
case "AdvancedSkillSpecialAbility":
return getAdvancedSkillSpecialAbilityById(
id.advanced_skill_special_ability,
)
case "AncestorGlyph":
return getAncestorGlyphById(id.ancestor_glyph)
case "ArcaneOrbEnchantment":
return getArcaneOrbEnchantmentById(id.arcane_orb_enchantment)
case "AttireEnchantment":
return getAttireEnchantmentById(id.attire_enchantment)
case "BlessedTradition":
return getBlessedTraditionById(id.blessed_tradition)
case "BowlEnchantment":
return getBowlEnchantmentById(id.bowl_enchantment)
case "BrawlingSpecialAbility":
return getBrawlingSpecialAbilityById(id.brawling_special_ability)
case "CauldronEnchantment":
return getCauldronEnchantmentById(id.cauldron_enchantment)
case "CeremonialItemSpecialAbility":
return getCeremonialItemSpecialAbilityById(
id.ceremonial_item_special_ability,
)
case "ChronicleEnchantment":
return getChronicleEnchantmentById(id.chronicle_enchantment)
case "CombatSpecialAbility":
return getCombatSpecialAbilityById(id.combat_special_ability)
case "CombatStyleSpecialAbility":
return getCombatStyleSpecialAbilityById(id.combat_style_special_ability)
case "CommandSpecialAbility":
return getCommandSpecialAbilityById(id.command_special_ability)
case "DaggerRitual":
return getDaggerRitualById(id.dagger_ritual)
case "FamiliarSpecialAbility":
return getFamiliarSpecialAbilityById(id.familiar_special_ability)
case "FatePointSexSpecialAbility":
return getFatePointSexSpecialAbilityById(
id.fate_point_sex_special_ability,
)
case "FatePointSpecialAbility":
return getFatePointSpecialAbilityById(id.fate_point_special_ability)
case "FoolsHatEnchantment":
return getFoolsHatEnchantmentById(id.fools_hat_enchantment)
case "GeneralSpecialAbility":
return getGeneralSpecialAbilityById(id.general_special_ability)
case "InstrumentEnchantment":
return getInstrumentEnchantmentById(id.instrument_enchantment)
case "KarmaSpecialAbility":
return getKarmaSpecialAbilityById(id.karma_special_ability)
case "Krallenkettenzauber":
return getKrallenkettenzauberById(id.krallenkettenzauber)
case "LiturgicalStyleSpecialAbility":
return getLiturgicalStyleSpecialAbilityById(
id.liturgical_style_special_ability,
)
case "LycantropicGift":
return getLycantropicGiftById(id.lycantropic_gift)
case "MagicalSign":
return getMagicalSignById(id.magical_sign)
case "MagicalSpecialAbility":
return getMagicalSpecialAbilityById(id.magical_special_ability)
case "MagicalTradition":
return getMagicalTraditionById(id.magical_tradition)
case "MagicStyleSpecialAbility":
return getMagicStyleSpecialAbilityById(id.magic_style_special_ability)
case "OrbEnchantment":
return getOrbEnchantmentById(id.orb_enchantment)
case "PactGift":
return getPactGiftById(id.pact_gift)
case "ProtectiveWardingCircleSpecialAbility":
return getProtectiveWardingCircleSpecialAbilityById(
id.protective_warding_circle_special_ability,
)
case "RingEnchantment":
return getRingEnchantmentById(id.ring_enchantment)
case "Sermon":
return getSermonById(id.sermon)
case "SexSpecialAbility":
return getSexSpecialAbilityById(id.sex_special_ability)
case "SickleRitual":
return getSickleRitualById(id.sickle_ritual)
case "SikaryanDrainSpecialAbility":
return getSikaryanDrainSpecialAbilityById(
id.sikaryan_drain_special_ability,
)
case "SkillStyleSpecialAbility":
return getSkillStyleSpecialAbilityById(id.skill_style_special_ability)
case "SpellSwordEnchantment":
return getSpellSwordEnchantmentById(id.spell_sword_enchantment)
case "StaffEnchantment":
return getStaffEnchantmentById(id.staff_enchantment)
case "ToyEnchantment":
return getToyEnchantmentById(id.toy_enchantment)
case "Trinkhornzauber":
return getTrinkhornzauberById(id.trinkhornzauber)
case "VampiricGift":
return getVampiricGiftById(id.vampiric_gift)
case "Vision":
return getVisionById(id.vision)
case "WandEnchantment":
return getWandEnchantmentById(id.wand_enchantment)
case "WeaponEnchantment":
return getWeaponEnchantmentById(id.weapon_enchantment)
default:
return assertExhaustive(id)
}
}
/**
* Gets a resolved select option by its identifier.
*/
export type GetResolvedSelectOptionById = (
id: ActivatableIdentifier,
selectOptionId: SelectOptionIdentifier,
) => ResolvedSelectOption | undefined
const printActivatableName = (
getAdvantageById: GetById.Static.Advantage,
getDisadvantageById: GetById.Static.Disadvantage,
getAdvancedCombatSpecialAbilityById: GetById.Static.AdvancedCombatSpecialAbility,
getAdvancedKarmaSpecialAbilityById: GetById.Static.AdvancedKarmaSpecialAbility,
getAdvancedMagicalSpecialAbilityById: GetById.Static.AdvancedMagicalSpecialAbility,
getAdvancedSkillSpecialAbilityById: GetById.Static.AdvancedSkillSpecialAbility,
getAncestorGlyphById: GetById.Static.AncestorGlyph,
getArcaneOrbEnchantmentById: GetById.Static.ArcaneOrbEnchantment,
getAttireEnchantmentById: GetById.Static.AttireEnchantment,
getBlessedTraditionById: GetById.Static.BlessedTradition,
getBowlEnchantmentById: GetById.Static.BowlEnchantment,
getBrawlingSpecialAbilityById: GetById.Static.BrawlingSpecialAbility,
getCauldronEnchantmentById: GetById.Static.CauldronEnchantment,
getCeremonialItemSpecialAbilityById: GetById.Static.CeremonialItemSpecialAbility,
getChronicleEnchantmentById: GetById.Static.ChronicleEnchantment,
getCombatSpecialAbilityById: GetById.Static.CombatSpecialAbility,
getCombatStyleSpecialAbilityById: GetById.Static.CombatStyleSpecialAbility,
getCommandSpecialAbilityById: GetById.Static.CommandSpecialAbility,
getDaggerRitualById: GetById.Static.DaggerRitual,
getFamiliarSpecialAbilityById: GetById.Static.FamiliarSpecialAbility,
getFatePointSexSpecialAbilityById: GetById.Static.FatePointSexSpecialAbility,
getFatePointSpecialAbilityById: GetById.Static.FatePointSpecialAbility,
getFoolsHatEnchantmentById: GetById.Static.FoolsHatEnchantment,
getGeneralSpecialAbilityById: GetById.Static.GeneralSpecialAbility,
getInstrumentEnchantmentById: GetById.Static.InstrumentEnchantment,
getKarmaSpecialAbilityById: GetById.Static.KarmaSpecialAbility,
getKrallenkettenzauberById: GetById.Static.Krallenkettenzauber,
getLiturgicalStyleSpecialAbilityById: GetById.Static.LiturgicalStyleSpecialAbility,
getLycantropicGiftById: GetById.Static.LycantropicGift,
getMagicalSignById: GetById.Static.MagicalSign,
getMagicalSpecialAbilityById: GetById.Static.MagicalSpecialAbility,
getMagicalTraditionById: GetById.Static.MagicalTradition,
getMagicStyleSpecialAbilityById: GetById.Static.MagicStyleSpecialAbility,
getOrbEnchantmentById: GetById.Static.OrbEnchantment,
getPactGiftById: GetById.Static.PactGift,
getProtectiveWardingCircleSpecialAbilityById: GetById.Static.ProtectiveWardingCircleSpecialAbility,
getRingEnchantmentById: GetById.Static.RingEnchantment,
getSermonById: GetById.Static.Sermon,
getSexSpecialAbilityById: GetById.Static.SexSpecialAbility,
getSickleRitualById: GetById.Static.SickleRitual,
getSikaryanDrainSpecialAbilityById: GetById.Static.SikaryanDrainSpecialAbility,
getSkillStyleSpecialAbilityById: GetById.Static.SkillStyleSpecialAbility,
getSpellSwordEnchantmentById: GetById.Static.SpellSwordEnchantment,
getStaffEnchantmentById: GetById.Static.StaffEnchantment,
getToyEnchantmentById: GetById.Static.ToyEnchantment,
getTrinkhornzauberById: GetById.Static.Trinkhornzauber,
getVampiricGiftById: GetById.Static.VampiricGift,
getVisionById: GetById.Static.Vision,
getWandEnchantmentById: GetById.Static.WandEnchantment,
getWeaponEnchantmentById: GetById.Static.WeaponEnchantment,
getAspectById: GetById.Static.Aspect,
locale: LocaleEnvironment,
id: ActivatableIdentifier,
options: SelectOptionIdentifier[] | undefined,
level: number | undefined,
getResolvedSelectOptionById: GetResolvedSelectOptionById,
) => {
const entry = getTranslationsForActivatable(
getAdvantageById,
getDisadvantageById,
getAdvancedCombatSpecialAbilityById,
getAdvancedKarmaSpecialAbilityById,
getAdvancedMagicalSpecialAbilityById,
getAdvancedSkillSpecialAbilityById,
getAncestorGlyphById,
getArcaneOrbEnchantmentById,
getAttireEnchantmentById,
getBlessedTraditionById,
getBowlEnchantmentById,
getBrawlingSpecialAbilityById,
getCauldronEnchantmentById,
getCeremonialItemSpecialAbilityById,
getChronicleEnchantmentById,
getCombatSpecialAbilityById,
getCombatStyleSpecialAbilityById,
getCommandSpecialAbilityById,
getDaggerRitualById,
getFamiliarSpecialAbilityById,
getFatePointSexSpecialAbilityById,
getFatePointSpecialAbilityById,
getFoolsHatEnchantmentById,
getGeneralSpecialAbilityById,
getInstrumentEnchantmentById,
getKarmaSpecialAbilityById,
getKrallenkettenzauberById,
getLiturgicalStyleSpecialAbilityById,
getLycantropicGiftById,
getMagicalSignById,
getMagicalSpecialAbilityById,
getMagicalTraditionById,
getMagicStyleSpecialAbilityById,
getOrbEnchantmentById,
getPactGiftById,
getProtectiveWardingCircleSpecialAbilityById,
getRingEnchantmentById,
getSermonById,
getSexSpecialAbilityById,
getSickleRitualById,
getSikaryanDrainSpecialAbilityById,
getSkillStyleSpecialAbilityById,
getSpellSwordEnchantmentById,
getStaffEnchantmentById,
getToyEnchantmentById,
getTrinkhornzauberById,
getVampiricGiftById,
getVisionById,
getWandEnchantmentById,
getWeaponEnchantmentById,
id,
)
if (entry === undefined) {
return undefined
}
return getNameComponents(
getAspectById,
locale,
id,
options,
level,
entry.translations,
t => t.name,
selectOptionId => getResolvedSelectOptionById(id, selectOptionId),
false,
)
}
/**
* Get the translation of a blessed tradition prerequisite.
*/
export const printActivatablePrerequisite = (
getAdvantageById: GetById.Static.Advantage,
getDisadvantageById: GetById.Static.Disadvantage,
getAdvancedCombatSpecialAbilityById: GetById.Static.AdvancedCombatSpecialAbility,
getAdvancedKarmaSpecialAbilityById: GetById.Static.AdvancedKarmaSpecialAbility,
getAdvancedMagicalSpecialAbilityById: GetById.Static.AdvancedMagicalSpecialAbility,
getAdvancedSkillSpecialAbilityById: GetById.Static.AdvancedSkillSpecialAbility,
getAncestorGlyphById: GetById.Static.AncestorGlyph,
getArcaneOrbEnchantmentById: GetById.Static.ArcaneOrbEnchantment,
getAttireEnchantmentById: GetById.Static.AttireEnchantment,
getBlessedTraditionById: GetById.Static.BlessedTradition,
getBowlEnchantmentById: GetById.Static.BowlEnchantment,
getBrawlingSpecialAbilityById: GetById.Static.BrawlingSpecialAbility,
getCauldronEnchantmentById: GetById.Static.CauldronEnchantment,
getCeremonialItemSpecialAbilityById: GetById.Static.CeremonialItemSpecialAbility,
getChronicleEnchantmentById: GetById.Static.ChronicleEnchantment,
getCombatSpecialAbilityById: GetById.Static.CombatSpecialAbility,
getCombatStyleSpecialAbilityById: GetById.Static.CombatStyleSpecialAbility,
getCommandSpecialAbilityById: GetById.Static.CommandSpecialAbility,
getDaggerRitualById: GetById.Static.DaggerRitual,
getFamiliarSpecialAbilityById: GetById.Static.FamiliarSpecialAbility,
getFatePointSexSpecialAbilityById: GetById.Static.FatePointSexSpecialAbility,
getFatePointSpecialAbilityById: GetById.Static.FatePointSpecialAbility,
getFoolsHatEnchantmentById: GetById.Static.FoolsHatEnchantment,
getGeneralSpecialAbilityById: GetById.Static.GeneralSpecialAbility,
getInstrumentEnchantmentById: GetById.Static.InstrumentEnchantment,
getKarmaSpecialAbilityById: GetById.Static.KarmaSpecialAbility,
getKrallenkettenzauberById: GetById.Static.Krallenkettenzauber,
getLiturgicalStyleSpecialAbilityById: GetById.Static.LiturgicalStyleSpecialAbility,
getLycantropicGiftById: GetById.Static.LycantropicGift,
getMagicalSignById: GetById.Static.MagicalSign,
getMagicalSpecialAbilityById: GetById.Static.MagicalSpecialAbility,
getMagicalTraditionById: GetById.Static.MagicalTradition,
getMagicStyleSpecialAbilityById: GetById.Static.MagicStyleSpecialAbility,
getOrbEnchantmentById: GetById.Static.OrbEnchantment,
getPactGiftById: GetById.Static.PactGift,
getProtectiveWardingCircleSpecialAbilityById: GetById.Static.ProtectiveWardingCircleSpecialAbility,
getRingEnchantmentById: GetById.Static.RingEnchantment,
getSermonById: GetById.Static.Sermon,
getSexSpecialAbilityById: GetById.Static.SexSpecialAbility,
getSickleRitualById: GetById.Static.SickleRitual,
getSikaryanDrainSpecialAbilityById: GetById.Static.SikaryanDrainSpecialAbility,
getSkillStyleSpecialAbilityById: GetById.Static.SkillStyleSpecialAbility,
getSpellSwordEnchantmentById: GetById.Static.SpellSwordEnchantment,
getStaffEnchantmentById: GetById.Static.StaffEnchantment,
getToyEnchantmentById: GetById.Static.ToyEnchantment,
getTrinkhornzauberById: GetById.Static.Trinkhornzauber,
getVampiricGiftById: GetById.Static.VampiricGift,
getVisionById: GetById.Static.Vision,
getWandEnchantmentById: GetById.Static.WandEnchantment,
getWeaponEnchantmentById: GetById.Static.WeaponEnchantment,
getAspectById: GetById.Static.Aspect,
getResolvedSelectOptionById: GetResolvedSelectOptionById,
locale: LocaleEnvironment,
prerequisite: ActivatablePrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const nameComponents = printActivatableName(
getAdvantageById,
getDisadvantageById,
getAdvancedCombatSpecialAbilityById,
getAdvancedKarmaSpecialAbilityById,
getAdvancedMagicalSpecialAbilityById,
getAdvancedSkillSpecialAbilityById,
getAncestorGlyphById,
getArcaneOrbEnchantmentById,
getAttireEnchantmentById,
getBlessedTraditionById,
getBowlEnchantmentById,
getBrawlingSpecialAbilityById,
getCauldronEnchantmentById,
getCeremonialItemSpecialAbilityById,
getChronicleEnchantmentById,
getCombatSpecialAbilityById,
getCombatStyleSpecialAbilityById,
getCommandSpecialAbilityById,
getDaggerRitualById,
getFamiliarSpecialAbilityById,
getFatePointSexSpecialAbilityById,
getFatePointSpecialAbilityById,
getFoolsHatEnchantmentById,
getGeneralSpecialAbilityById,
getInstrumentEnchantmentById,
getKarmaSpecialAbilityById,
getKrallenkettenzauberById,
getLiturgicalStyleSpecialAbilityById,
getLycantropicGiftById,
getMagicalSignById,
getMagicalSpecialAbilityById,
getMagicalTraditionById,
getMagicStyleSpecialAbilityById,
getOrbEnchantmentById,
getPactGiftById,
getProtectiveWardingCircleSpecialAbilityById,
getRingEnchantmentById,
getSermonById,
getSexSpecialAbilityById,
getSickleRitualById,
getSikaryanDrainSpecialAbilityById,
getSkillStyleSpecialAbilityById,
getSpellSwordEnchantmentById,
getStaffEnchantmentById,
getToyEnchantmentById,
getTrinkhornzauberById,
getVampiricGiftById,
getVisionById,
getWandEnchantmentById,
getWeaponEnchantmentById,
getAspectById,
locale,
prerequisite.id,
prerequisite.options,
prerequisite.level,
getResolvedSelectOptionById,
)
if (nameComponents === undefined) {
return undefined
}
return {
label: `${
prerequisite.active
? locale.translate("special ability")
: locale.translate("no special ability")
} `,
value: printActivatableNameChunk(locale, nameComponents.full),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,36 @@
import { isNotNullish } from "@optolith/helpers/nullable"
import { romanize } from "@optolith/helpers/roman"
import { AnimistPowerPrerequisite } from "optolith-database-schema/types/prerequisites/single/AnimistPowerPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a culture prerequisite.
*/
export const printAnimistPowerPrerequisite = (
getAnimistPowerById: GetById.Static.AnimistPower,
locale: LocaleEnvironment,
prerequisite: AnimistPowerPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const animistPower = getAnimistPowerById(prerequisite.id.animist_power)
return {
value: [
locale.translateMap(animistPower?.translations)?.name ?? "MISSING_VALUE",
prerequisite.level === undefined
? undefined
: romanize(prerequisite.level),
prerequisite.value.toString(),
]
.filter(isNotNullish)
.join(" "),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,43 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
BlessedTraditionPrerequisite,
BlessedTraditionPrerequisiteRestriction,
} from "optolith-database-schema/types/prerequisites/single/TraditionPrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
const printValue = (
locale: LocaleEnvironment,
restriction: BlessedTraditionPrerequisiteRestriction | undefined,
) => {
switch (restriction) {
case "Church":
return locale.translate("Tradition ({0})", locale.translate("Church"))
case "Shamanistic":
return locale.translate("Tradition ({0})", locale.translate("Shaman"))
case undefined:
return locale.translate("Tradition")
default:
return assertExhaustive(restriction)
}
}
/**
* Get the translation of a blessed tradition prerequisite.
*/
export const printBlessedTraditionPrerequisite = (
locale: LocaleEnvironment,
prerequisite: BlessedTraditionPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
return {
label: `${locale.translate("special ability")} `,
value: printValue(locale, prerequisite.restriction),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,35 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { PrerequisitePart } from "../part.js"
const printType = (
locale: LocaleEnvironment,
type: "advantage" | "disadvantage",
): string => {
switch (type) {
case "advantage":
return locale.translate("advantage")
case "disadvantage":
return locale.translate("disadvantage")
default:
return assertExhaustive(type)
}
}
/**
* Get the translation of a culture prerequisite.
*/
export const printCommonSuggestedByRCPPrerequisite = (
locale: LocaleEnvironment,
_prerequisite: Record<string, never>,
name: string,
type: "advantage" | "disadvantage",
): PrerequisitePart | undefined => ({
value: locale.translate(
"Race, culture, or profession must have {0} as an automatic or suggested {1}",
name,
printType(locale, type),
),
sentenceType: undefined,
isMeta: false,
})
@@ -0,0 +1,32 @@
import { CulturePrerequisite } from "optolith-database-schema/types/prerequisites/single/CulturePrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a culture prerequisite.
*/
export const printCulturePrerequisite = (
getCultureById: GetById.Static.Culture,
locale: LocaleEnvironment,
prerequisite: CulturePrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const culture = getCultureById(prerequisite.id.culture)
const cultureTranslation = locale.translateMap(culture?.translations)
if (cultureTranslation === undefined) {
return undefined
}
return {
label: `${locale.translate("Culture")} `,
value: cultureTranslation.name,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,120 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Enhancements } from "optolith-database-schema/types/_Enhancements"
import { SkillWithEnhancementsIdentifier } from "optolith-database-schema/types/_IdentifierGroup"
import { LocaleMap } from "optolith-database-schema/types/_LocaleMap"
import {
ExternalEnhancementPrerequisite,
InternalEnhancementPrerequisite,
} from "optolith-database-schema/types/prerequisites/single/EnhancementPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
const printLabel = (
locale: LocaleEnvironment,
skillId: SkillWithEnhancementsIdentifier,
): string => {
switch (skillId.tag) {
case "Spell":
case "Ritual":
return locale.translate("spell enhancement")
case "LiturgicalChant":
case "Ceremony":
return locale.translate("liturgical enhancement")
default:
return assertExhaustive(skillId)
}
}
const getSkill = (
getSpellById: GetById.Static.Spell,
getRitualById: GetById.Static.Ritual,
getLiturgicalChantById: GetById.Static.LiturgicalChant,
getCeremonyById: GetById.Static.Ceremony,
parentId: SkillWithEnhancementsIdentifier,
):
| { translations: LocaleMap<{ name: string }>; enhancements?: Enhancements }
| undefined => {
switch (parentId.tag) {
case "Spell":
return getSpellById(parentId.spell)
case "Ritual":
return getRitualById(parentId.ritual)
case "LiturgicalChant":
return getLiturgicalChantById(parentId.liturgical_chant)
case "Ceremony":
return getCeremonyById(parentId.ceremony)
default:
return assertExhaustive(parentId)
}
}
/**
* Get the translation of an external enhancement prerequisite.
*/
export const printExternalEnhancementPrerequisite = (
getSpellById: GetById.Static.Spell,
getRitualById: GetById.Static.Ritual,
getLiturgicalChantById: GetById.Static.LiturgicalChant,
getCeremonyById: GetById.Static.Ceremony,
locale: LocaleEnvironment,
prerequisite: ExternalEnhancementPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const skill = getSkill(
getSpellById,
getRitualById,
getLiturgicalChantById,
getCeremonyById,
prerequisite.skill.id,
)
const enhancement = skill?.enhancements?.find(
e => e.id === prerequisite.enhancement.id,
)
return {
label: `${printLabel(locale, prerequisite.skill.id)} `,
value: `*${
locale.translateMap(enhancement?.translations)?.name
}* ${locale.translate("for")} ${
locale.translateMap(skill?.translations)?.name
}`,
sentenceType: undefined,
isMeta: false,
}
}
/**
* Get the translation of an internal enhancement prerequisite.
*/
export const printInternalEnhancementPrerequisite = (
getSpellById: GetById.Static.Spell,
getRitualById: GetById.Static.Ritual,
getLiturgicalChantById: GetById.Static.LiturgicalChant,
getCeremonyById: GetById.Static.Ceremony,
locale: LocaleEnvironment,
prerequisite: InternalEnhancementPrerequisite,
parentId: SkillWithEnhancementsIdentifier,
): PrerequisitePart | undefined => {
const skill = getSkill(
getSpellById,
getRitualById,
getLiturgicalChantById,
getCeremonyById,
parentId,
)
const enhancement = skill?.enhancements?.find(e => e.id === prerequisite.id)
return {
label: `${printLabel(locale, parentId)} `,
value: `*${locale.translateMap(enhancement?.translations)?.name}*`,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,25 @@
import { InfluencePrerequisite } from "optolith-database-schema/types/prerequisites/single/InfluencePrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { MISSING_VALUE } from "../../unknown.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a culture prerequisite.
*/
export const printInfluencePrerequisite = (
locale: LocaleEnvironment,
prerequisite: InfluencePrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
// TODO
return {
value: MISSING_VALUE,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,48 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
MagicalTraditionPrerequisite,
MagicalTraditionPrerequisiteRestriction,
} from "optolith-database-schema/types/prerequisites/single/TraditionPrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
const printValue = (
locale: LocaleEnvironment,
restriction: MagicalTraditionPrerequisiteRestriction | undefined,
) => {
switch (restriction) {
case "CanLearnRituals":
return locale.translate(
"Tradition must be able to use rituals",
locale.translate("Church"),
)
case "CanBindFamiliars":
return locale.translate(
"Tradition must be able to bind familiars",
locale.translate("Shaman"),
)
case undefined:
return locale.translate("Tradition")
default:
return assertExhaustive(restriction)
}
}
/**
* Get the translation of a magical tradition prerequisite.
*/
export const printMagicalTraditionPrerequisite = (
locale: LocaleEnvironment,
prerequisite: MagicalTraditionPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
return {
value: printValue(locale, prerequisite.restriction),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,14 @@
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a culture prerequisite.
*/
export const printNoOtherAncestorBloodAdvantagePrerequisite = (
locale: LocaleEnvironment,
_prerequisite: Record<string, never>,
): PrerequisitePart | undefined => ({
value: locale.translate("no other ancestor blood advantage"),
sentenceType: undefined,
isMeta: false,
})
@@ -0,0 +1,54 @@
import { isNotNullish } from "@optolith/helpers/nullable"
import { romanize } from "@optolith/helpers/roman"
import { PactPrerequisite } from "optolith-database-schema/types/prerequisites/single/PactPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { MISSING_VALUE } from "../../unknown.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a culture prerequisite.
*/
export const printPactPrerequisite = (
getPactCategoryById: GetById.Static.PactCategory,
locale: LocaleEnvironment,
prerequisite: PactPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const pactCategory = getPactCategoryById(
prerequisite.category.id.pact_category,
)
const parts = [
prerequisite.domain_id === undefined
? undefined
: locale.translate(
"domain {0}",
locale.joinDisjunctionList(
prerequisite.domain_id.map(
ref =>
locale.translateMap(
pactCategory?.domains.find(
domain => domain.id === ref.id.pact_domain,
)?.translations,
)?.name ?? MISSING_VALUE,
),
),
),
locale.translate(
"{0} level {1}",
locale.translateMap(pactCategory?.translations)?.name ?? MISSING_VALUE,
romanize(prerequisite.level ?? 1),
),
].filter(isNotNullish)
return {
value: parts.join(", "),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,23 @@
import { PrimaryAttributePrerequisite } from "optolith-database-schema/types/prerequisites/single/PrimaryAttributePrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a state prerequisite.
*/
export const printPrimaryAttributePrerequisite = (
locale: LocaleEnvironment,
prerequisite: PrimaryAttributePrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
return {
label: `${locale.translate("Primary Attribute")} `,
value: prerequisite.value.toString(),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,31 @@
import { PublicationPrerequisite } from "optolith-database-schema/types/prerequisites/single/PublicationPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a state prerequisite.
*/
export const printPublicationPrerequisite = (
getPublicationById: GetById.Static.Publication,
locale: LocaleEnvironment,
prerequisite: PublicationPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const publication = getPublicationById(prerequisite.id.publication)
const publicationTranslation = locale.translateMap(publication?.translations)
if (publicationTranslation === undefined) {
return undefined
}
return {
value: publicationTranslation.name,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,32 @@
import { RacePrerequisite } from "optolith-database-schema/types/prerequisites/single/RacePrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a race prerequisite.
*/
export const printRacePrerequisite = (
getRaceById: GetById.Static.Race,
locale: LocaleEnvironment,
prerequisite: RacePrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const race = getRaceById(prerequisite.id.race)
const raceTranslation = locale.translateMap(race?.translations)
if (raceTranslation === undefined) {
return undefined
}
return {
label: `${locale.translate("Race")} `,
value: raceTranslation.name,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,107 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { RatedIdentifier } from "optolith-database-schema/types/_IdentifierGroup"
import { RatedPrerequisite } from "optolith-database-schema/types/prerequisites/single/RatedPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { MISSING_VALUE } from "../../unknown.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
const printRatedName = (
getAttributeById: GetById.Static.Attribute,
getSkillById: GetById.Static.Skill,
getCloseCombatTechniqueById: GetById.Static.CloseCombatTechnique,
getRangedCombatTechniqueById: GetById.Static.RangedCombatTechnique,
getSpellById: GetById.Static.Spell,
getRitualById: GetById.Static.Ritual,
getLiturgicalChantById: GetById.Static.LiturgicalChant,
getCeremonyById: GetById.Static.Ceremony,
locale: LocaleEnvironment,
id: RatedIdentifier,
) => {
switch (id.tag) {
case "Attribute":
return (
locale.translateMap(getAttributeById(id.attribute)?.translations)
?.name ?? MISSING_VALUE
)
case "Skill":
return (
locale.translateMap(getSkillById(id.skill)?.translations)?.name ??
MISSING_VALUE
)
case "CloseCombatTechnique":
return (
locale.translateMap(
getCloseCombatTechniqueById(id.close_combat_technique)?.translations,
)?.name ?? MISSING_VALUE
)
case "RangedCombatTechnique":
return (
locale.translateMap(
getRangedCombatTechniqueById(id.ranged_combat_technique)
?.translations,
)?.name ?? MISSING_VALUE
)
case "Spell":
return (
locale.translateMap(getSpellById(id.spell)?.translations)?.name ??
MISSING_VALUE
)
case "Ritual":
return (
locale.translateMap(getRitualById(id.ritual)?.translations)?.name ??
MISSING_VALUE
)
case "LiturgicalChant":
return (
locale.translateMap(
getLiturgicalChantById(id.liturgical_chant)?.translations,
)?.name ?? MISSING_VALUE
)
case "Ceremony":
return (
locale.translateMap(getCeremonyById(id.ceremony)?.translations)?.name ??
MISSING_VALUE
)
default:
return assertExhaustive(id)
}
}
/**
* Get the translation of a blessed tradition prerequisite.
*/
export const printRatedPrerequisite = (
getAttributeById: GetById.Static.Attribute,
getSkillById: GetById.Static.Skill,
getCloseCombatTechniqueById: GetById.Static.CloseCombatTechnique,
getRangedCombatTechniqueById: GetById.Static.RangedCombatTechnique,
getSpellById: GetById.Static.Spell,
getRitualById: GetById.Static.Ritual,
getLiturgicalChantById: GetById.Static.LiturgicalChant,
getCeremonyById: GetById.Static.Ceremony,
locale: LocaleEnvironment,
prerequisite: RatedPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
return {
value: `${printRatedName(
getAttributeById,
getSkillById,
getCloseCombatTechniqueById,
getRangedCombatTechniqueById,
getSpellById,
getRitualById,
getLiturgicalChantById,
getCeremonyById,
locale,
prerequisite.id,
)} ${prerequisite.value}`,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,230 @@
import { isNotNullish } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
CombatTechniquesTargetGroup,
RatedMinimumNumberPrerequisite,
} from "optolith-database-schema/types/prerequisites/single/RatedMinimumNumberPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { MISSING_VALUE } from "../../unknown.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
const printNumberOfTheFollowingSkills = (
locale: LocaleEnvironment,
number: number,
): string => {
switch (number) {
case 1:
return locale.translate("one of the following skills")
case 2:
return locale.translate("two of the following skills")
case 3:
return locale.translate("three of the following skills")
case 4:
return locale.translate("four of the following skills")
case 5:
return locale.translate("five of the following skills")
case 6:
return locale.translate("six of the following skills")
case 7:
return locale.translate("seven of the following skills")
case 8:
return locale.translate("eight of the following skills")
case 9:
return locale.translate("nine of the following skills")
default:
return locale.translate("{0} of the following skills")
}
}
const printNumberOfAllCombatTechniques = (
locale: LocaleEnvironment,
number: number,
): string => {
switch (number) {
case 1:
return locale.translate("one combat technique")
case 2:
return locale.translate("two combat techniques")
case 3:
return locale.translate("three combat techniques")
case 4:
return locale.translate("four combat techniques")
case 5:
return locale.translate("five combat techniques")
case 6:
return locale.translate("six combat techniques")
case 7:
return locale.translate("seven combat techniques")
case 8:
return locale.translate("eight combat techniques")
case 9:
return locale.translate("nine combat techniques")
default:
return locale.translate("{0} combat techniques")
}
}
const printNumberOfCloseCombatTechniques = (
locale: LocaleEnvironment,
number: number,
): string => {
switch (number) {
case 1:
return locale.translate("one close combat technique")
case 2:
return locale.translate("two close combat techniques")
case 3:
return locale.translate("three close combat techniques")
case 4:
return locale.translate("four close combat techniques")
case 5:
return locale.translate("five close combat techniques")
case 6:
return locale.translate("six close combat techniques")
case 7:
return locale.translate("seven close combat techniques")
case 8:
return locale.translate("eight close combat techniques")
case 9:
return locale.translate("nine close combat techniques")
default:
return locale.translate("{0} close combat techniques")
}
}
const printNumberOfRangedCombatTechniques = (
locale: LocaleEnvironment,
number: number,
): string => {
switch (number) {
case 1:
return locale.translate("one ranged combat technique")
case 2:
return locale.translate("two ranged combat techniques")
case 3:
return locale.translate("three ranged combat techniques")
case 4:
return locale.translate("four ranged combat techniques")
case 5:
return locale.translate("five ranged combat techniques")
case 6:
return locale.translate("six ranged combat techniques")
case 7:
return locale.translate("seven ranged combat techniques")
case 8:
return locale.translate("eight ranged combat techniques")
case 9:
return locale.translate("nine ranged combat techniques")
default:
return locale.translate("{0} ranged combat techniques")
}
}
const printNumberOfCombatTechniques = (
locale: LocaleEnvironment,
category: CombatTechniquesTargetGroup,
number: number,
): string => {
switch (category) {
case "All":
return printNumberOfAllCombatTechniques(locale, number)
case "Close":
return printNumberOfCloseCombatTechniques(locale, number)
case "Ranged":
return printNumberOfRangedCombatTechniques(locale, number)
default:
return assertExhaustive(category)
}
}
/**
* Get the translation of a rated minimum number prerequisite.
*/
export const printRatedMinimumNumberPrerequisite = (
getSkillById: GetById.Static.Skill,
getPropertyById: GetById.Static.Property,
getAspectById: GetById.Static.Aspect,
locale: LocaleEnvironment,
prerequisite: RatedMinimumNumberPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
switch (prerequisite.targets.tag) {
case "Skills": {
const skills = prerequisite.targets.skills.list
.map(
ref =>
locale.translateMap(getSkillById(ref.id.skill)?.translations)?.name,
)
.filter(isNotNullish)
return {
value: locale.translate(
"{0} on at least SR {1}: {2}", // zwei der folgenden Talente mindestens FW 10:
printNumberOfTheFollowingSkills(locale, prerequisite.number),
prerequisite.value,
locale.joinConjunctionList(skills),
),
sentenceType: undefined,
isMeta: false,
}
}
case "CombatTechniques": {
// de-DE: "Fernkampfwert 10"
// en-US: "Ranged Combat 10"
// nl-BE: "Schiet/werpwaarde 10"
// fr-FR: "Ranged Combat 10"
// it-IT: "Una tecnica di combattimento a distanza 10"
return {
value: `${printNumberOfCombatTechniques(
locale,
prerequisite.targets.combat_techniques.group,
prerequisite.number,
)} ${prerequisite.value}`,
sentenceType: undefined,
isMeta: false,
}
}
case "Spellworks": {
return {
value: locale.translate(
"{0} arcane works with the property {1} at SR {2} or higher",
prerequisite.number,
locale.translateMap(
getPropertyById(prerequisite.targets.spellworks.id.property)
?.translations,
)?.name ?? MISSING_VALUE,
prerequisite.value,
),
sentenceType: undefined,
isMeta: false,
}
}
case "Liturgies": {
return {
value: locale.translate(
"{0} liturgical chants and ceremonies with the aspect {1} at SR {2} or higher",
prerequisite.number,
locale.translateMap(
getAspectById(prerequisite.targets.liturgies.id.aspect)
?.translations,
)?.name ?? MISSING_VALUE,
prerequisite.value,
),
sentenceType: undefined,
isMeta: false,
}
}
default:
return assertExhaustive(prerequisite.targets)
}
}
@@ -0,0 +1,36 @@
import { isNotNullish } from "@optolith/helpers/nullable"
import { RatedSumPrerequisite } from "optolith-database-schema/types/prerequisites/single/RatedSumPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a rated sum prerequisite.
*/
export const printRatedSumPrerequisite = (
getSkillById: GetById.Static.Skill,
locale: LocaleEnvironment,
prerequisite: RatedSumPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const skills = prerequisite.targets
.map(
target =>
locale.translateMap(getSkillById(target.skill)?.translations)?.name,
)
.filter(isNotNullish)
return {
value: locale.translate(
"the SR for {0} combined must add up to at least {1}",
locale.joinConjunctionList(skills),
prerequisite.sum,
),
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,11 @@
import { RulePrerequisite } from "optolith-database-schema/types/prerequisites/single/RulePrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a rule prerequisite.
*/
export const printRulePrerequisite = (
_locale: LocaleEnvironment,
_prerequisite: RulePrerequisite,
): PrerequisitePart | undefined => undefined
@@ -0,0 +1,28 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { BinarySex } from "optolith-database-schema/types/_Sex"
import { SexPrerequisite } from "optolith-database-schema/types/prerequisites/single/SexPrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { PrerequisitePart } from "../part.js"
const printId = (locale: LocaleEnvironment, id: BinarySex): string => {
switch (id) {
case "Male":
return locale.translate("Male")
case "Female":
return locale.translate("Female")
default:
return assertExhaustive(id)
}
}
/**
* Get the translation of a (binary) sex prerequisite.
*/
export const printBinarySexPrerequisite = (
locale: LocaleEnvironment,
prerequisite: SexPrerequisite,
): PrerequisitePart | undefined => ({
value: printId(locale, prerequisite.id),
sentenceType: undefined,
isMeta: false,
})
@@ -0,0 +1,33 @@
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
SexualCharacteristic,
SexualCharacteristicPrerequisite,
} from "optolith-database-schema/types/prerequisites/single/SexualCharacteristicPrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { PrerequisitePart } from "../part.js"
const printId = (
locale: LocaleEnvironment,
id: SexualCharacteristic,
): string => {
switch (id) {
case "Penis":
return locale.translate("Penis")
case "Vagina":
return locale.translate("Vagina")
default:
return assertExhaustive(id)
}
}
/**
* Get the translation of a sexual characteristic prerequisite.
*/
export const printSexualCharacteristicPrerequisite = (
locale: LocaleEnvironment,
prerequisite: SexualCharacteristicPrerequisite,
): PrerequisitePart | undefined => ({
value: locale.translate("Person with {0}", printId(locale, prerequisite.id)),
sentenceType: undefined,
isMeta: false,
})
@@ -0,0 +1,34 @@
import { SocialStatusPrerequisite } from "optolith-database-schema/types/prerequisites/single/SocialStatusPrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a social status prerequisite.
*/
export const printSocialStatusPrerequisite = (
getSocialStatusById: GetById.Static.SocialStatus,
locale: LocaleEnvironment,
prerequisite: SocialStatusPrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const socialStatus = getSocialStatusById(prerequisite.id.social_status)
const socialStatusTranslation = locale.translateMap(
socialStatus?.translations,
)
if (socialStatusTranslation === undefined) {
return undefined
}
return {
label: `${locale.translate("Social Status {0} or higher")} `,
value: `*${socialStatusTranslation.name}*`,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,32 @@
import { StatePrerequisite } from "optolith-database-schema/types/prerequisites/single/StatePrerequisite"
import { GetById } from "../../../../helpers/getTypes.js"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { printDisplayOption } from "../displayOption.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a state prerequisite.
*/
export const printStatePrerequisite = (
getStateById: GetById.Static.State,
locale: LocaleEnvironment,
prerequisite: StatePrerequisite,
): PrerequisitePart | undefined => {
if (prerequisite.display_option !== undefined) {
return printDisplayOption(locale, prerequisite.display_option)
}
const state = getStateById(prerequisite.id.state)
const stateTranslation = locale.translateMap(state?.translations)
if (stateTranslation === undefined) {
return undefined
}
return {
label: `${locale.translate("State")} `,
value: `*${stateTranslation.name}*`,
sentenceType: undefined,
isMeta: false,
}
}
@@ -0,0 +1,16 @@
import { TextPrerequisite } from "optolith-database-schema/types/prerequisites/single/TextPrerequisite"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { MISSING_VALUE } from "../../unknown.js"
import { PrerequisitePart } from "../part.js"
/**
* Get the translation of a text prerequisite.
*/
export const printTextPrerequisite = (
locale: LocaleEnvironment,
prerequisite: TextPrerequisite,
): PrerequisitePart => ({
value: locale.translateMap(prerequisite.translations) ?? MISSING_VALUE,
sentenceType: prerequisite.sentence_type,
isMeta: prerequisite.is_meta ?? false,
})
@@ -1,7 +1,7 @@
import { filterNonNullable } from "@optolith/helpers/array"
import { mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import { Effect } from "optolith-database-schema/types/_ActivatableSkillEffect"
import { ActivatableSkillEffect } from "optolith-database-schema/types/_ActivatableSkillEffect"
import { LocaleEnvironment } from "../../../../helpers/locale.js"
import { EntityDescriptionSection } from "../../../../index.js"
@@ -34,7 +34,7 @@ const getContentPartsForQualityLevels = (
*/
export const getTextForEffect = (
locale: LocaleEnvironment,
effect: Effect,
effect: ActivatableSkillEffect,
): EntityDescriptionSection[] => {
switch (effect.tag) {
case "Plain":
@@ -1,8 +1,8 @@
import { mapNullable } from "@optolith/helpers/nullable"
import { assertExhaustive } from "@optolith/helpers/typeSafety"
import {
TargetCategory,
TargetCategoryIdentifier,
AffectedTargetCategories,
SpecificAffectedTargetCategoryIdentifier,
} from "optolith-database-schema/types/_ActivatableSkillTargetCategory"
import { TargetCategoryReference } from "optolith-database-schema/types/_SimpleReferences"
import { GetById } from "../../../../helpers/getTypes.js"
@@ -43,7 +43,7 @@ const getPredefinedTranslation = (
const getTargetCategoryTranslationByType = (
getTargetCategoryById: GetById.Static.TargetCategory,
locale: LocaleEnvironment,
id: TargetCategoryIdentifier,
id: SpecificAffectedTargetCategoryIdentifier,
) => {
switch (id.tag) {
case "Self":
@@ -71,7 +71,7 @@ const getTargetCategoryTranslationByType = (
export const getTargetCategoryTranslation = (
getTargetCategoryById: GetById.Static.TargetCategory,
locale: LocaleEnvironment,
values: TargetCategory,
values: AffectedTargetCategories,
): EntityDescriptionSection => ({
label: locale.translate("Target Category"),
value:
+342
View File
@@ -0,0 +1,342 @@
// TODO: Update for new identifier mappings
/**
* Used identifiers of optional rules.
*/
export enum OptionalRuleIdentifier {
MaximumAttributeScores = 8,
HigherDefenseStats = 17,
}
/**
* Used identifiers of races.
*/
export enum RaceIdentifier {
Humans = 1,
}
/**
* Used identifiers of professions.
*/
export enum ProfessionIdentifier {
OwnProfession = 0,
}
/**
* Used identifiers of eye colors.
*/
export enum EyeColorIdentifier {
Red = 19,
Purple = 20,
}
/**
* Used identifiers of hair colors.
*/
export enum HairColorIdentifier {
White = 24,
Green = 25,
}
/**
* Used identifiers of attributes.
*/
export enum AttributeIdentifier {
Courage = 1,
Sagacity = 2,
Intuition = 3,
Charisma = 4,
Dexterity = 5,
Agility = 6,
Constitution = 7,
Strength = 8,
}
/**
* Used identifiers of derived characteristics.
*/
export enum DerivedCharacteristicIdentifier {
LifePoints = 1,
ArcaneEnergy = 2,
KarmaPoints = 3,
Spirit = 4,
Toughness = 5,
Dodge = 6,
Initiative = 7,
Movement = 8,
FatePoints = 9,
WoundThreshold = 10,
}
/**
* Used identifiers of energies.
*/
export type EnergyIdentifier =
| DerivedCharacteristicIdentifier.LifePoints
| DerivedCharacteristicIdentifier.ArcaneEnergy
| DerivedCharacteristicIdentifier.KarmaPoints
/**
* Used identifiers of skill.
*/
export enum SkillIdentifier {
Flying = 1,
Gaukelei = 2,
Climbing = 3,
BodyControl = 4,
FeatOfStrength = 5,
Riding = 6,
Swimming = 7,
SelfControl = 8,
Singing = 9,
Perception = 10,
Dancing = 11,
Pickpocket = 12,
Stealth = 13,
Carousing = 14,
Persuasion = 15,
Seduction = 16,
Intimidation = 17,
Etiquette = 18,
Streetwise = 19,
Empathy = 20,
FastTalk = 21,
Disguise = 22,
Willpower = 23,
Tracking = 24,
Ropes = 25,
Fishing = 26,
Orienting = 27,
PlantLore = 28,
AnimalLore = 29,
Survival = 30,
Gambling = 31,
Geography = 32,
History = 33,
Religions = 34,
Warfare = 35,
MagicalLore = 36,
Mechanics = 37,
Math = 38,
Law = 39,
MythsAndLegends = 40,
SphereLore = 41,
Astronomy = 42,
Alchemy = 43,
Sailing = 44,
Driving = 45,
Commerce = 46,
TreatPoison = 47,
TreatDisease = 48,
TreatSoul = 49,
TreatWounds = 50,
Woodworking = 51,
PrepareFood = 52,
Leatherworking = 53,
ArtisticAbility = 54,
Metalworking = 55,
Music = 56,
PickLocks = 57,
Earthencraft = 58,
Clothworking = 59,
}
/**
* Used identifiers of skill groups.
*/
export enum SkillGroupIdentifier {
Physical = 1,
Social = 2,
Nature = 3,
Knowledge = 4,
Craft = 5,
}
/**
* Used identifiers of advantages.
*/
export enum AdvantageIdentifier {
CustomAdvantage = 0,
Aptitude = 4, // Begabung
Nimble = 9, // Flink
Blessed = 12,
Luck = 14,
ExceptionalSkill = 16,
ExceptionalCombatTechnique = 17,
IncreasedAstralPower = 20,
IncreasedKarmaPoints = 21,
IncreasedLifePoints = 22,
IncreasedSpirit = 23,
IncreasedToughness = 24,
ImmunityToPoison = 25,
ImmunityToDisease = 26,
MagicalAttunement = 29,
Rich = 33,
SociallyAdaptable = 37,
InspireConfidence = 43,
WeaponAptitude = 44,
Spellcaster = 47,
Unyielding = 51, // Eisern
HatredOf = 55,
LargeSpellSelection = 66,
LeichterGang = 85,
Preacher = 91,
Visionary = 92,
ManySermons = 93,
ManyVisions = 94,
Einkommen = 129,
}
/**
* Used identifiers of disadvantages.
*/
export enum DisadvantageIdentifier {
CustomDisadvantage = 0,
AfraidOf = 1,
Poor = 2,
Slow = 4,
NoFlyingBalm = 14,
NoFamiliar = 15,
MagicalRestriction = 21,
DecreasedArcanePower = 23,
DecreasedKarmaPoints = 24,
DecreasedLifePoints = 25,
DecreasedSpirit = 26,
DecreasedToughness = 27,
BadLuck = 28,
PersonalityFlaw = 30,
Principles = 31,
BadHabit = 33,
NegativeTrait = 34, // Schlechte Eigenschaft
Stigma = 42,
Deaf = 44, // Taub
Incompetent = 45,
Obligations = 47, // Verpflichtungen
Maimed = 48, // Verstümmelt
BrittleBones = 56, // Gläsern
SmallSpellSelection = 64,
FewerSermons = 70,
FewerVisions = 71,
}
/**
* Used identifiers of ranged combat techniques.
*/
export enum RangedCombatTechniqueIdentifier {
SpittingFire = 4,
}
/**
* Used identifiers of advanced skill special abilities.
*/
export enum AdvancedSkillSpecialAbilityIdentifier {
Fachwissen = 2,
}
/**
* Used identifiers of combat special abilities.
*/
export enum CombatSpecialAbilityIdentifier {
CombatReflexes = 12,
}
/**
* Used identifiers of general special abilities.
*/
export enum GeneralSpecialAbilityIdentifier {
SkillSpecialization = 9,
CraftInstruments = 17,
Hunter = 18,
Literacy = 27,
Language = 29,
LanguageSpecialization = 30,
FireEater = 53,
}
/**
* Used identifiers of magical special abilities.
*/
export enum MagicalSpecialAbilityIdentifier {
PropertyKnowledge = 3,
GrosseMeditation = 12,
Adaptation = 18,
Imitationszauberei = 51,
}
/**
* Used identifiers of magical traditions.
*/
export enum MagicalTraditionIdentifier {
GuildMages = 1,
Witches = 2,
Elves = 3,
Unicorn = 4,
Druids = 5,
QabalyaMages = 6,
IntuitiveMages = 7,
Savants = 8,
Illusionists = 9,
ArcaneBards = 10,
ArcaneDancers = 11,
Schelme = 13,
Zauberalchimisten = 14,
TsatuariaAnhaengerinnen = 16,
Necker = 17,
Animisten = 18,
Geoden = 19,
Zibilijas = 20,
BrobimGeoden = 21,
Darna = 23,
Runenschoepfer = 24,
}
/**
* Used identifiers of magical special abilities.
*/
export enum MagicStyleSpecialAbilityIdentifier {
ScholarDesMagierkollegsZuHoningen = 24,
MadaschwesternStil = 55,
}
/**
* Used identifiers of pact gifts.
*/
export enum PactGiftIdentifier {
DunklesAbbildDerBuendnisgabe = 3,
}
/**
* Used identifiers of aspects.
*/
export enum AspectIdentifier {
General = 1,
AllgemeinSchamanenritus = 44,
}
/**
* Used identifiers of karma special abilities.
*/
export enum KarmaSpecialAbilityIdentifier {
AspectKnowledge = 1,
MasterOfAspect = 5,
HigherOrdination = 14,
}
/**
* Used identifiers of liturgical style special abilities.
*/
export enum LiturgicalStyleSpecialAbilityIdentifier {
BirdsOfPassage = 38, // Zugvögel
HuntressesOfTheWhiteMaiden = 40, // Jägerinnen der Weißen Maid
FollowersOfTheGoldenOne = 47, // Anhänger des Güldenen
}
/**
* Used identifiers of blessed traditions.
*/
export enum BlessedTraditionIdentifier {
Praios = 1,
Phex = 5,
Firun = 9,
Rahja = 12,
}
+2
View File
@@ -9,4 +9,6 @@ export type LocaleEnvironment = {
translate: Translate
translateMap: TranslateMap
compare: Compare<string>
joinConjunctionList: (list: string[]) => string
joinDisjunctionList: (list: string[]) => string
}
@@ -0,0 +1,73 @@
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import {
joinPrerequisiteParts,
PrerequisitePart,
} from "../../../../src/entities/partial/prerequisites/part.js"
import { defaultLocaleEnvironment } from "../../../helpers/locale.js"
describe("joinPrerequisiteParts", () => {
it("should join normal parts by comma", () => {
const parts: PrerequisitePart[] = [
{ value: "A", sentenceType: undefined, isMeta: false },
{
label: "Label for ",
value: "B",
sentenceType: undefined,
isMeta: false,
},
{ value: "C", sentenceType: undefined, isMeta: false },
]
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
assert.equal(result, "A, Label for B, C")
})
it("should connect connected sentence types by semicolon", () => {
const parts: PrerequisitePart[] = [
{ value: "A", sentenceType: undefined, isMeta: false },
{ value: "B", sentenceType: "Connected", isMeta: false },
{ value: "C", sentenceType: "Connected", isMeta: false },
{ value: "D", sentenceType: undefined, isMeta: false },
{ value: "E", sentenceType: undefined, isMeta: false },
{ value: "F", sentenceType: "Connected", isMeta: false },
]
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
assert.equal(result, "A; B; C; D, E; F")
})
it("should connect standalone sentence types by periods", () => {
const parts: PrerequisitePart[] = [
{ value: "A", sentenceType: undefined, isMeta: false },
{ value: "B", sentenceType: "Standalone", isMeta: false },
{ value: "C", sentenceType: "Standalone", isMeta: false },
{ value: "D", sentenceType: undefined, isMeta: false },
{ value: "E", sentenceType: undefined, isMeta: false },
{ value: "F.", sentenceType: "Standalone", isMeta: false },
{ value: "G.", sentenceType: "Standalone", isMeta: false },
{ value: "H", sentenceType: undefined, isMeta: false },
{ value: "I", sentenceType: undefined, isMeta: false },
{ value: "J", sentenceType: "Standalone", isMeta: false },
]
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
assert.equal(result, "A. B. C. D, E. F. G. H, I. J.")
})
it("adds “none” at the beginning if all parts are declared as meta prerequisites", () => {
const parts: PrerequisitePart[] = [
{ value: "A", sentenceType: undefined, isMeta: true },
{ value: "B", sentenceType: undefined, isMeta: true },
{ value: "C", sentenceType: undefined, isMeta: true },
]
const result = joinPrerequisiteParts(defaultLocaleEnvironment, parts)
assert.equal(result, "none, A, B, C")
})
it("throws an error if the sentenceType is unknown", () => {
const parts: PrerequisitePart[] = [
// @ts-expect-error Testing invalid input
{ value: "A", sentenceType: "any", isMeta: true },
]
const block = () => joinPrerequisiteParts(defaultLocaleEnvironment, parts)
assert.throws(block)
})
})
@@ -0,0 +1,75 @@
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { printStatePrerequisite } from "../../../../../src/entities/partial/prerequisites/single/state.js"
import { GetById } from "../../../../../src/helpers/getTypes.js"
import { defaultLocaleEnvironment } from "../../../../helpers/locale.js"
describe("getStatePrerequisiteTranslation", () => {
it("returns a PrerequisitePart object for the prerequisite", () => {
const getStateById: GetById.Static.State = () => ({
id: 1,
src: [],
translations: {
"en-US": {
name: "A",
description: "Description",
},
},
})
assert.deepEqual(
printStatePrerequisite(getStateById, defaultLocaleEnvironment, {
id: {
tag: "State",
state: 1,
},
}),
{
label: "State ",
value: "*A*",
sentenceType: undefined,
isMeta: false,
},
)
assert.deepEqual(
printStatePrerequisite(
() => ({
id: 1,
src: [],
translations: {},
}),
defaultLocaleEnvironment,
{
id: {
tag: "State",
state: 1,
},
},
),
undefined,
)
assert.deepEqual(
printStatePrerequisite(getStateById, defaultLocaleEnvironment, {
id: {
tag: "State",
state: 1,
},
display_option: {
tag: "ReplaceWith",
replace_with: {
translations: {
"en-US": "Replacement",
},
},
},
}),
{
value: "Replacement",
sentenceType: undefined,
isMeta: false,
},
)
})
})
@@ -0,0 +1,38 @@
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { printTextPrerequisite } from "../../../../../src/entities/partial/prerequisites/single/text.js"
import { MISSING_VALUE } from "../../../../../src/entities/partial/unknown.js"
import { defaultLocaleEnvironment } from "../../../../helpers/locale.js"
describe("getTextPrerequisiteTranslation", () => {
it("returns a PrerequisitePart object for the prerequisite", () => {
assert.deepEqual(
printTextPrerequisite(defaultLocaleEnvironment, {
verification: "Pass",
sentence_type: undefined,
translations: {
"en-US": "A",
},
}),
{
value: "A",
sentenceType: undefined,
isMeta: false,
},
)
assert.deepEqual(
printTextPrerequisite(defaultLocaleEnvironment, {
verification: "Pass",
sentence_type: "Standalone",
is_meta: true,
translations: {},
}),
{
value: MISSING_VALUE,
sentenceType: "Standalone",
isMeta: true,
},
)
})
})
@@ -1,7 +1,7 @@
import assert from "node:assert/strict"
import { describe, it } from "node:test"
import { getCheckResultBasedValueTranslation } from "../../../../../src/entities/partial/rated/activatable/checkResultBased.js"
import { translateMock } from "../../../../../src/helpers/translate.js"
import { translateMock } from "../../../../helpers/translate.js"
describe("getTextForCheckResultBased", () => {
it("should return the value text for a check-result-based parameter of an activatable skill", () => {