From c612aeab7451bdca475893649751c05b9146d7cd Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 31 Dec 2025 00:16:32 -0800 Subject: [PATCH] add weapon stat modifier types and API layer - new WeaponStatModifier, AugmentSkill, Befoulment types - replace hasAxSkills with augmentType enum - add ax/befoulment fields to GridWeapon and CollectionWeapon - add getWeaponStatModifiers API methods and query options - update party schema for new response structure --- src/lib/api/adapters/entity.adapter.ts | 41 ++++++++++++++++- src/lib/api/queries/entity.queries.ts | 47 ++++++++++++++++++- src/lib/api/schemas/party.ts | 52 ++++++++++++++++----- src/lib/types/api/collection.ts | 15 ++++-- src/lib/types/api/entities.ts | 6 --- src/lib/types/api/party.ts | 9 ++-- src/lib/types/api/weaponSeries.ts | 14 ++++-- src/lib/types/api/weaponStatModifier.ts | 61 +++++++++++++++++++++++++ 8 files changed, 215 insertions(+), 30 deletions(-) create mode 100644 src/lib/types/api/weaponStatModifier.ts diff --git a/src/lib/api/adapters/entity.adapter.ts b/src/lib/api/adapters/entity.adapter.ts index c60c68c0..80da378c 100644 --- a/src/lib/api/adapters/entity.adapter.ts +++ b/src/lib/api/adapters/entity.adapter.ts @@ -17,6 +17,7 @@ import type { CreateWeaponSeriesPayload, UpdateWeaponSeriesPayload } from '$lib/types/api/weaponSeries' +import type { WeaponStatModifier } from '$lib/types/api/weaponStatModifier' import type { CharacterSeriesRef, CharacterSeries, @@ -731,10 +732,47 @@ export class EntityAdapter extends BaseAdapter { }) } + // ============================================ + // Weapon Stat Modifier Methods (AX Skills & Befoulments) + // ============================================ + + /** + * Gets all weapon stat modifiers (AX skills and befoulments) + * @param category - Optional filter: 'ax' or 'befoulment' + */ + async getWeaponStatModifiers(category?: 'ax' | 'befoulment'): Promise { + const searchParams = new URLSearchParams() + if (category) { + searchParams.set('category', category) + } + + const queryString = searchParams.toString() + const url = queryString ? `/weapon_stat_modifiers?${queryString}` : '/weapon_stat_modifiers' + + return this.request(url, { + method: 'GET', + cacheTTL: 3600000 // Cache for 1 hour - reference data rarely changes + }) + } + + /** + * Gets AX skills only + */ + async getAxSkills(): Promise { + return this.getWeaponStatModifiers('ax') + } + + /** + * Gets befoulments only + */ + async getBefoulments(): Promise { + return this.getWeaponStatModifiers('befoulment') + } + /** * Clears entity cache */ - clearEntityCache(type?: 'weapons' | 'characters' | 'summons' | 'weapon_keys' | 'weapon_series') { + clearEntityCache(type?: 'weapons' | 'characters' | 'summons' | 'weapon_keys' | 'weapon_series' | 'weapon_stat_modifiers') { if (type) { this.clearCache(`/${type}`) } else { @@ -744,6 +782,7 @@ export class EntityAdapter extends BaseAdapter { this.clearCache('/summons') this.clearCache('/weapon_keys') this.clearCache('/weapon_series') + this.clearCache('/weapon_stat_modifiers') } } diff --git a/src/lib/api/queries/entity.queries.ts b/src/lib/api/queries/entity.queries.ts index ce764096..b7033720 100644 --- a/src/lib/api/queries/entity.queries.ts +++ b/src/lib/api/queries/entity.queries.ts @@ -173,6 +173,46 @@ export const entityQueries = { enabled: !!idOrSlug, staleTime: 1000 * 60 * 60, // 1 hour gcTime: 1000 * 60 * 60 * 24 // 24 hours + }), + + /** + * Weapon stat modifiers query options (AX skills and befoulments) + * + * @param category - Optional filter: 'ax' or 'befoulment' + * @returns Query options for fetching weapon stat modifiers + */ + weaponStatModifiers: (category?: 'ax' | 'befoulment') => + queryOptions({ + queryKey: ['weaponStatModifiers', category ?? 'all'] as const, + queryFn: () => entityAdapter.getWeaponStatModifiers(category), + staleTime: 1000 * 60 * 60, // 1 hour - reference data + gcTime: 1000 * 60 * 60 * 24 // 24 hours + }), + + /** + * AX skills only query options + * + * @returns Query options for fetching AX skills + */ + axSkills: () => + queryOptions({ + queryKey: ['weaponStatModifiers', 'ax'] as const, + queryFn: () => entityAdapter.getAxSkills(), + staleTime: 1000 * 60 * 60, // 1 hour + gcTime: 1000 * 60 * 60 * 24 // 24 hours + }), + + /** + * Befoulments only query options + * + * @returns Query options for fetching befoulments + */ + befoulments: () => + queryOptions({ + queryKey: ['weaponStatModifiers', 'befoulment'] as const, + queryFn: () => entityAdapter.getBefoulments(), + staleTime: 1000 * 60 * 60, // 1 hour + gcTime: 1000 * 60 * 60 * 24 // 24 hours }) } @@ -210,5 +250,10 @@ export const entityKeys = { allCharacterSeries: () => ['characterSeries'] as const, summonSeriesList: () => ['summonSeries', 'list'] as const, summonSeries: (idOrSlug: string) => ['summonSeries', idOrSlug] as const, - allSummonSeries: () => ['summonSeries'] as const + allSummonSeries: () => ['summonSeries'] as const, + weaponStatModifiers: (category?: 'ax' | 'befoulment') => + ['weaponStatModifiers', category ?? 'all'] as const, + allWeaponStatModifiers: () => ['weaponStatModifiers'] as const, + axSkills: () => ['weaponStatModifiers', 'ax'] as const, + befoulments: () => ['weaponStatModifiers', 'befoulment'] as const } diff --git a/src/lib/api/schemas/party.ts b/src/lib/api/schemas/party.ts index 0f23d274..71424070 100644 --- a/src/lib/api/schemas/party.ts +++ b/src/lib/api/schemas/party.ts @@ -198,11 +198,36 @@ const WeaponSeriesRefSchema = z.object({ }), has_weapon_keys: z.boolean().optional(), has_awakening: z.boolean().optional(), - has_ax_skills: z.boolean().optional(), + augment_type: z.enum(['none', 'ax', 'befoulment']).optional(), extra: z.boolean().optional(), element_changeable: z.boolean().optional() }) +// Weapon stat modifier schema (for AX skills and befoulments) +const WeaponStatModifierSchema = z.object({ + id: z.string(), + slug: z.string(), + name_en: z.string(), + name_jp: z.string(), + category: z.enum(['ax', 'befoulment']), + stat: z.string(), + polarity: z.number(), + suffix: z.string().nullable() +}) + +// AX skill with modifier object and strength +const AugmentSkillSchema = z.object({ + modifier: WeaponStatModifierSchema, + strength: z.number() +}) + +// Befoulment with modifier, strength, and exorcism level +const BefoulmentSchema = z.object({ + modifier: WeaponStatModifierSchema, + strength: z.number(), + exorcism_level: z.number() +}) + // Item schemas const WeaponSchema = z.object({ id: z.string(), @@ -282,28 +307,33 @@ const GridWeaponSchema = z.object({ transcendence_step: z.number().nullish().default(0), transcendence_level: z.number().nullish().default(0), // Alias for compatibility element: z.number().nullish(), - + // Weapon keys weapon_key1_id: z.string().nullish(), weapon_key2_id: z.string().nullish(), weapon_key3_id: z.string().nullish(), weapon_key4_id: z.string().nullish(), weapon_keys: z.array(z.any()).nullish(), // Populated by API with key details - + // Awakening awakening_id: z.string().nullish(), awakening_level: z.number().nullish().default(1), awakening: z.any().nullish(), // Populated by API with awakening details - - // AX modifiers - ax_modifier1: z.number().nullish(), - ax_strength1: z.number().nullish(), - ax_modifier2: z.number().nullish(), - ax_strength2: z.number().nullish(), - + + // AX skills (new format with full modifier objects) + ax: z.array(AugmentSkillSchema).nullish(), + + // Befoulment (for Odiant weapons) + befoulment: BefoulmentSchema.nullish(), + // Nested weapon data (populated by API) weapon: WeaponSchema.nullish(), - + + // Collection link fields + collection_weapon_id: z.string().nullish(), + out_of_sync: z.boolean().nullish(), + orphaned: z.boolean().nullish(), + created_at: z.string().nullish(), updated_at: z.string().nullish() }) diff --git a/src/lib/types/api/collection.ts b/src/lib/types/api/collection.ts index 9613ad58..cce79d7f 100644 --- a/src/lib/types/api/collection.ts +++ b/src/lib/types/api/collection.ts @@ -2,6 +2,7 @@ // These define user-owned items with customizations import type { Character, Weapon, Summon, JobAccessory, Awakening } from './entities' +import type { AugmentSkill, Befoulment } from './weaponStatModifier' /** * Extended mastery modifier (used for rings and earrings) @@ -43,7 +44,10 @@ export interface CollectionWeapon { uncapLevel: number transcendenceStep: number element?: number // For element-changeable weapons - ax?: Array<{ modifier: number; strength: number }> + /** AX skills with full modifier objects */ + ax?: AugmentSkill[] + /** Befoulment for Odiant weapons */ + befoulment?: Befoulment awakening: { type: Awakening level: number @@ -122,10 +126,15 @@ export interface CollectionWeaponInput { weaponKey4Id?: string awakeningId?: string awakeningLevel?: number - axModifier1?: number + // AX skills (uses FK IDs for API payload) + axModifier1Id?: string axStrength1?: number - axModifier2?: number + axModifier2Id?: string axStrength2?: number + // Befoulment (for Odiant weapons) + befoulmentModifierId?: string + befoulmentStrength?: number + exorcismLevel?: number } /** diff --git a/src/lib/types/api/entities.ts b/src/lib/types/api/entities.ts index dade4861..31047a8e 100644 --- a/src/lib/types/api/entities.ts +++ b/src/lib/types/api/entities.ts @@ -259,12 +259,6 @@ export interface WeaponKey { order: number } -// SimpleAxSkill for weapon AX skills -export interface SimpleAxSkill { - modifier: number - strength: number -} - // Guidebook entity export interface Guidebook { id: string diff --git a/src/lib/types/api/party.ts b/src/lib/types/api/party.ts index dbfa54b9..de224d17 100644 --- a/src/lib/types/api/party.ts +++ b/src/lib/types/api/party.ts @@ -12,10 +12,10 @@ import type { Guidebook, User, Awakening, - WeaponKey, - SimpleAxSkill + WeaponKey } from './entities' import type { GridArtifact, CollectionArtifact } from './artifact' +import type { AugmentSkill, Befoulment } from './weaponStatModifier' // Grid item types - these are the junction tables between Party and entities @@ -29,7 +29,10 @@ export interface GridWeapon { element?: number weapon: Weapon // Named properly, not "object" weaponKeys?: WeaponKey[] - ax?: SimpleAxSkill[] + /** AX skills with full modifier objects */ + ax?: AugmentSkill[] + /** Befoulment for Odiant weapons */ + befoulment?: Befoulment awakening?: { type?: Awakening level?: number diff --git a/src/lib/types/api/weaponSeries.ts b/src/lib/types/api/weaponSeries.ts index b9f26d8f..fc301b99 100644 --- a/src/lib/types/api/weaponSeries.ts +++ b/src/lib/types/api/weaponSeries.ts @@ -7,6 +7,8 @@ * @module types/api/weaponSeries */ +import type { AugmentType } from './weaponStatModifier' + /** * Embedded series reference on weapons. * This is the structure returned in weapon.series field. @@ -18,7 +20,8 @@ export interface WeaponSeriesRef { name: { en: string; ja: string } hasWeaponKeys: boolean hasAwakening: boolean - hasAxSkills: boolean + /** Type of augment this series supports: "ax", "befoulment", or "none" */ + augmentType: AugmentType extra: boolean elementChangeable: boolean } @@ -37,7 +40,8 @@ export interface WeaponSeries { elementChangeable: boolean hasWeaponKeys: boolean hasAwakening: boolean - hasAxSkills: boolean + /** Type of augment this series supports: "ax", "befoulment", or "none" */ + augmentType: AugmentType // Only included in :full view (show endpoint) weaponCount?: number } @@ -54,7 +58,7 @@ export interface WeaponSeriesInput { element_changeable: boolean has_weapon_keys: boolean has_awakening: boolean - has_ax_skills: boolean + augment_type: AugmentType } /** @@ -69,7 +73,7 @@ export interface CreateWeaponSeriesPayload { element_changeable?: boolean has_weapon_keys?: boolean has_awakening?: boolean - has_ax_skills?: boolean + augment_type?: AugmentType } /** @@ -84,7 +88,7 @@ export interface UpdateWeaponSeriesPayload { element_changeable?: boolean has_weapon_keys?: boolean has_awakening?: boolean - has_ax_skills?: boolean + augment_type?: AugmentType } /** diff --git a/src/lib/types/api/weaponStatModifier.ts b/src/lib/types/api/weaponStatModifier.ts new file mode 100644 index 00000000..a6dd6547 --- /dev/null +++ b/src/lib/types/api/weaponStatModifier.ts @@ -0,0 +1,61 @@ +/** + * Weapon Stat Modifier Types + * + * Type definitions for the unified AX skill and Befoulment system. + * These types represent modifiers from the weapon_stat_modifiers API endpoint. + * + * @module types/api/weaponStatModifier + */ + +/** + * Augment type enum for weapon series. + * Determines whether a weapon series supports AX skills, befoulments, or neither. + */ +export type AugmentType = 'none' | 'ax' | 'befoulment' + +/** + * WeaponStatModifier from the API. + * Represents an AX skill or befoulment modifier definition. + */ +export interface WeaponStatModifier { + /** Unique identifier */ + id: string + /** URL-safe identifier (e.g., "ax_atk", "bef_atk_down") */ + slug: string + /** English display name */ + nameEn: string + /** Japanese display name */ + nameJp: string + /** Category: "ax" for AX skills, "befoulment" for negative modifiers */ + category: 'ax' | 'befoulment' + /** The stat this modifier affects (e.g., "atk", "def", "hp") */ + stat: string + /** Polarity: 1 for buffs (positive), -1 for debuffs (negative) */ + polarity: 1 | -1 + /** Display suffix for values (e.g., "%") */ + suffix: string | null +} + +/** + * AX Skill with its modifier and strength value. + * Used in GridWeapon and CollectionWeapon for weapons with AX skills. + */ +export interface AugmentSkill { + /** The modifier definition */ + modifier: WeaponStatModifier + /** The strength/value of this skill (e.g., 3.0 for 3% ATK) */ + strength: number +} + +/** + * Befoulment with modifier, strength, and exorcism level. + * Used for Odiant weapons with negative stat modifiers. + */ +export interface Befoulment { + /** The befoulment modifier definition */ + modifier: WeaponStatModifier + /** The strength/value of this befoulment */ + strength: number + /** Exorcism level (0-5) - higher levels reduce the negative effect */ + exorcismLevel: number +}