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
This commit is contained in:
Justin Edmund 2025-12-31 00:16:32 -08:00
parent 5d7e8334f9
commit c612aeab74
8 changed files with 215 additions and 30 deletions

View file

@ -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<WeaponStatModifier[]> {
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<WeaponStatModifier[]>(url, {
method: 'GET',
cacheTTL: 3600000 // Cache for 1 hour - reference data rarely changes
})
}
/**
* Gets AX skills only
*/
async getAxSkills(): Promise<WeaponStatModifier[]> {
return this.getWeaponStatModifiers('ax')
}
/**
* Gets befoulments only
*/
async getBefoulments(): Promise<WeaponStatModifier[]> {
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')
}
}

View file

@ -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
}

View file

@ -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()
})

View file

@ -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
}
/**

View file

@ -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

View file

@ -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

View file

@ -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
}
/**

View file

@ -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
}