diff --git a/src/cache/activatableSelectOptions.ts b/src/cache/activatableSelectOptions.ts index 63247b15..89a3c4bb 100644 --- a/src/cache/activatableSelectOptions.ts +++ b/src/cache/activatableSelectOptions.ts @@ -17,7 +17,9 @@ import type { ImprovementCost, Poison, PrerequisiteForLevel, + Property_ID, RequirableSelectOptionIdentifier, + Ritual_ID, SelectOptionCategory, SelectOptions, SelectOptionsAdventurePointsValue, @@ -28,6 +30,7 @@ import type { SkillSelectOptionCategoryPrerequisite, SpecificFromSkillSelectOptionCategoryCategory, SpecificTargetCategory, + Spell_ID, TargetCategory, TradeSecretAdventurePointsValue, } from "../../gen/types.js" @@ -67,15 +70,17 @@ const wrapPlainApValue = ( ): TradeSecretAdventurePointsValue | undefined => apValue === undefined ? undefined : Case("Fixed", apValue) -const matchesSpecificSkillishIdList = ( - id: string, - config: SpecificFromSkillSelectOptionCategoryCategory, +const matchesSpecificSkillishIdList = ( + config: SpecificFromSkillSelectOptionCategoryCategory, + matches: (required: Ref) => boolean, ): boolean => { + const isInList = config.list.some(matches) + switch (config.operation.kind) { case "Intersection": - return config.list.includes(id) + return isInList case "Difference": - return !config.list.includes(id) + return !isInList default: return assertExhaustive(config.operation) } @@ -233,14 +238,35 @@ const getDefaultSkillishFilter = ( const { specific } = category return specific === undefined ? undefined - : ({ id }) => matchesSpecificSkillishIdList(id, specific) + : ({ id }) => matchesSpecificSkillishIdList(specific, required => required === id) +} + +const getSpellworkFilter = ( + category: GenericSkillsSelectOptionCategoryCategory< + Case<"Single", Spell_ID | Ritual_ID> | Case<"Property", Property_ID> + >, +): ((instance: { id: string; content: Entity }) => boolean) | undefined => { + const { specific } = category + return specific === undefined + ? undefined + : ({ id, content: { property } }) => + matchesSpecificSkillishIdList(specific, required => { + switch (required.kind) { + case "Single": + return required.Single === id + case "Property": + return property === required.Property + default: + return assertExhaustive(required) + } + }) } -const getDerivedSkillishSelectOptions = ( +const getDerivedSkillishSelectOptions = ( database: TSONDB, entryId: ActivatableIdentifier, entity: E, - category: GenericSkillsSelectOptionCategoryCategory & { + category: GenericSkillsSelectOptionCategoryCategory & { skill_applications?: SkillApplicationOrUse[] | undefined skill_uses?: SkillApplicationOrUse[] | undefined }, @@ -248,7 +274,7 @@ const getDerivedSkillishSelectOptions = ( bindingCost?: SelectOptionsBindingCostValue ap_value?: SelectOptionsAdventurePointsValue }, - filter = getDefaultSkillishFilter(category), + filter: ((instance: { id: string; content: Entity }) => boolean) | undefined, ) => { const { prerequisites } = category const { bindingCost, ap_value } = options @@ -928,7 +954,10 @@ const getDerivedSelectOptions = ( const matchesIdRequirement = category.Skills.specific === undefined || - matchesSpecificSkillishIdList(id, category.Skills.specific) + matchesSpecificSkillishIdList( + category.Skills.specific, + required => required === id, + ) return matchesGroupRequirement && matchesIdRequirement }, @@ -940,6 +969,7 @@ const getDerivedSelectOptions = ( "Spell", category.Spells, selectOptionCategory.Skills, + getSpellworkFilter(category.Spells), ) case "Rituals": return getDerivedSkillishSelectOptions( @@ -948,6 +978,7 @@ const getDerivedSelectOptions = ( "Ritual", category.Rituals, selectOptionCategory.Skills, + getSpellworkFilter(category.Rituals), ) case "LiturgicalChants": return getDerivedSkillishSelectOptions( @@ -956,6 +987,7 @@ const getDerivedSelectOptions = ( "LiturgicalChant", category.LiturgicalChants, selectOptionCategory.Skills, + getDefaultSkillishFilter(category.LiturgicalChants), ) case "Ceremonies": return getDerivedSkillishSelectOptions( @@ -964,6 +996,7 @@ const getDerivedSelectOptions = ( "Ceremony", category.Ceremonies, selectOptionCategory.Skills, + getDefaultSkillishFilter(category.Ceremonies), ) default: return assertExhaustive(category) @@ -980,6 +1013,7 @@ const getDerivedSelectOptions = ( "CloseCombatTechnique", category.CloseCombatTechniques, selectOptionCategory.CombatTechniques, + getDefaultSkillishFilter(category.CloseCombatTechniques), ) case "RangedCombatTechniques": return getDerivedSkillishSelectOptions( @@ -988,6 +1022,7 @@ const getDerivedSelectOptions = ( "RangedCombatTechnique", category.RangedCombatTechniques, selectOptionCategory.CombatTechniques, + getDefaultSkillishFilter(category.RangedCombatTechniques), ) default: return assertExhaustive(category) diff --git a/src/types/_ActivatableSelectOptionCategory.ts b/src/types/_ActivatableSelectOptionCategory.ts index 85edbcb7..525715d8 100644 --- a/src/types/_ActivatableSelectOptionCategory.ts +++ b/src/types/_ActivatableSelectOptionCategory.ts @@ -11,6 +11,7 @@ import { SkillIdentifier, SpellIdentifier, TargetCategoryIdentifier, + PropertyIdentifier, } from "./_Identifier.js" import { ActivatableIdentifier, @@ -211,16 +212,34 @@ const SkillsSelectOptionCategory = DB.TypeAlias(import.meta.url, { }), }) +const SpellFilter = DB.Enum(import.meta.url, { + name: "SpellFilter", + values: () => ({ + Single: DB.EnumCase({ type: SpellIdentifier() }), + Property: DB.EnumCase({ type: PropertyIdentifier() }), + }), +}) + +const RitualFilter = DB.Enum(import.meta.url, { + name: "RitualFilter", + values: () => ({ + Single: DB.EnumCase({ type: RitualIdentifier() }), + Property: DB.EnumCase({ type: PropertyIdentifier() }), + }), +}) + const SkillsSelectOptionCategoryCategory = DB.Enum(import.meta.url, { name: "SkillsSelectOptionCategoryCategory", values: () => ({ Skills: DB.EnumCase({ type: DB.IncludeIdentifier(SkillSelectOptionCategoryCategory) }), Spells: DB.EnumCase({ - type: DB.GenIncludeIdentifier(GenericSkillsSelectOptionCategoryCategory, [SpellIdentifier()]), + type: DB.GenIncludeIdentifier(GenericSkillsSelectOptionCategoryCategory, [ + DB.IncludeIdentifierType(SpellFilter), + ]), }), Rituals: DB.EnumCase({ type: DB.GenIncludeIdentifier(GenericSkillsSelectOptionCategoryCategory, [ - RitualIdentifier(), + DB.IncludeIdentifierType(RitualFilter), ]), }), LiturgicalChants: DB.EnumCase({ diff --git a/src/types/prerequisites/single/PropertyPrerequisite.ts b/src/types/prerequisites/single/PropertyPrerequisite.ts new file mode 100644 index 00000000..5f0318b3 --- /dev/null +++ b/src/types/prerequisites/single/PropertyPrerequisite.ts @@ -0,0 +1,18 @@ +import * as DB from "tsondb/schema/dsl" +import { PropertyIdentifier } from "../../_Identifier.js" +import { DisplayOption } from "../DisplayOption.js" + +export const PropertyPrerequisite = DB.TypeAlias(import.meta.url, { + name: "PropertyPrerequisite", + comment: "Requires a specific property or one of a specific set of properties.", + type: () => + DB.Object({ + id: DB.Required({ + comment: "The property�s identifier.", + type: PropertyIdentifier(), + }), + display_option: DB.Optional({ + type: DB.IncludeIdentifier(DisplayOption), + }), + }), +})