## Summary - Add weapon stat modifier types and API layer - Rewrite AX skill components to fetch modifiers from API instead of local data - Add befoulment select component for weapon editing - Update weapon edit views to use new modifier system - Remove old hardcoded ax skill definitions This is the frontend counterpart to the API weapon stat modifiers feature. AX skills and befoulments are now fetched from the API instead of being hardcoded in the frontend. ## Test plan - [ ] Edit a weapon with AX skills, verify dropdown shows correct options - [ ] Add/remove AX skills on a weapon, verify saves correctly - [ ] Add befoulment to a weapon, verify UI and save work - [ ] Verify existing weapons with AX skills display correctly
150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party'
|
|
import { seriesHasWeaponKeys } from '$lib/utils/weaponSeries'
|
|
|
|
export interface ModificationStatus {
|
|
hasModifications: boolean
|
|
hasAwakening: boolean
|
|
hasWeaponKeys: boolean
|
|
hasAxSkills: boolean
|
|
hasBefoulment: boolean
|
|
hasRings: boolean
|
|
hasEarring: boolean
|
|
hasPerpetuity: boolean
|
|
hasTranscendence: boolean
|
|
hasUncapLevel: boolean
|
|
hasElement: boolean
|
|
hasQuickSummon: boolean
|
|
hasFriendSummon: boolean
|
|
}
|
|
|
|
export function detectModifications(
|
|
type: 'character' | 'weapon' | 'summon',
|
|
item: GridCharacter | GridWeapon | GridSummon | undefined
|
|
): ModificationStatus {
|
|
const status: ModificationStatus = {
|
|
hasModifications: false,
|
|
hasAwakening: false,
|
|
hasWeaponKeys: false,
|
|
hasAxSkills: false,
|
|
hasBefoulment: false,
|
|
hasRings: false,
|
|
hasEarring: false,
|
|
hasPerpetuity: false,
|
|
hasTranscendence: false,
|
|
hasUncapLevel: false,
|
|
hasElement: false,
|
|
hasQuickSummon: false,
|
|
hasFriendSummon: false
|
|
}
|
|
|
|
if (!item) return status
|
|
|
|
if (type === 'character') {
|
|
const char = item as GridCharacter
|
|
|
|
status.hasAwakening = !!char.awakening
|
|
status.hasRings = !!(char.overMastery && char.overMastery.length > 0)
|
|
status.hasEarring = !!char.aetherialMastery
|
|
status.hasPerpetuity = !!char.perpetuity
|
|
status.hasTranscendence = !!(char.transcendenceStep && char.transcendenceStep > 0)
|
|
status.hasUncapLevel = char.uncapLevel !== undefined && char.uncapLevel !== null
|
|
|
|
status.hasModifications =
|
|
status.hasAwakening ||
|
|
status.hasRings ||
|
|
status.hasEarring ||
|
|
status.hasPerpetuity ||
|
|
status.hasTranscendence ||
|
|
status.hasUncapLevel
|
|
} else if (type === 'weapon') {
|
|
const weapon = item as GridWeapon
|
|
|
|
status.hasAwakening = !!weapon.awakening
|
|
status.hasWeaponKeys = !!(weapon.weaponKeys && weapon.weaponKeys.length > 0)
|
|
status.hasAxSkills = !!(weapon.ax && weapon.ax.length > 0)
|
|
status.hasBefoulment = !!weapon.befoulment?.modifier
|
|
status.hasTranscendence = !!(weapon.transcendenceStep && weapon.transcendenceStep > 0)
|
|
status.hasUncapLevel = weapon.uncapLevel !== undefined && weapon.uncapLevel !== null
|
|
status.hasElement = !!(weapon.element && weapon.weapon?.element === 0)
|
|
|
|
status.hasModifications =
|
|
status.hasAwakening ||
|
|
status.hasWeaponKeys ||
|
|
status.hasAxSkills ||
|
|
status.hasBefoulment ||
|
|
status.hasTranscendence ||
|
|
status.hasUncapLevel ||
|
|
status.hasElement
|
|
} else if (type === 'summon') {
|
|
const summon = item as GridSummon
|
|
|
|
status.hasTranscendence = !!(summon.transcendenceStep && summon.transcendenceStep > 0)
|
|
status.hasUncapLevel = summon.uncapLevel !== undefined && summon.uncapLevel !== null
|
|
status.hasQuickSummon = !!summon.quickSummon
|
|
status.hasFriendSummon = !!summon.friend
|
|
|
|
status.hasModifications =
|
|
status.hasTranscendence ||
|
|
status.hasUncapLevel ||
|
|
status.hasQuickSummon ||
|
|
status.hasFriendSummon
|
|
}
|
|
|
|
return status
|
|
}
|
|
|
|
export function hasAnyModification(
|
|
type: 'character' | 'weapon' | 'summon',
|
|
item: GridCharacter | GridWeapon | GridSummon | undefined
|
|
): boolean {
|
|
return detectModifications(type, item).hasModifications
|
|
}
|
|
|
|
/**
|
|
* Check if a weapon CAN be modified (has modifiable properties)
|
|
* This is different from hasModifications which checks if it HAS been modified
|
|
*/
|
|
export function canWeaponBeModified(gridWeapon: GridWeapon | undefined): boolean {
|
|
if (!gridWeapon?.weapon) return false
|
|
|
|
const weapon = gridWeapon.weapon
|
|
|
|
// Element can be changed (element = 0 means "any element")
|
|
const canChangeElement = weapon.element === 0
|
|
|
|
// Weapon keys (series-specific) - use utility function that handles both formats
|
|
const hasWeaponKeys = seriesHasWeaponKeys(weapon.series)
|
|
|
|
// AX skills or Befoulment - check augmentType from series
|
|
const augmentType = weapon.series?.augmentType ?? 'none'
|
|
const hasAugments = augmentType !== 'none'
|
|
|
|
// Awakening (maxAwakeningLevel > 0 means it can have awakening)
|
|
const hasAwakening = (weapon.maxAwakeningLevel ?? 0) > 0
|
|
|
|
return canChangeElement || hasWeaponKeys || hasAugments || hasAwakening
|
|
}
|
|
|
|
/**
|
|
* Check if a character CAN be modified (has modifiable properties)
|
|
* This is different from hasModifications which checks if it HAS been modified
|
|
*/
|
|
export function canCharacterBeModified(gridCharacter: GridCharacter | undefined): boolean {
|
|
if (!gridCharacter?.character) return false
|
|
|
|
const character = gridCharacter.character
|
|
|
|
// Awakening (maxAwakeningLevel > 0 means it can have awakening)
|
|
const hasAwakening = (character.maxAwakeningLevel ?? 0) > 0
|
|
|
|
// All characters can have rings (Over Mastery)
|
|
const canHaveRings = true
|
|
|
|
// All characters can have earrings (Aetherial Mastery)
|
|
const canHaveEarring = true
|
|
|
|
// Perpetuity is only for non-MC characters (position > 0)
|
|
const canHavePerpetuity = gridCharacter.position > 0
|
|
|
|
return hasAwakening || canHaveRings || canHaveEarring || canHavePerpetuity
|
|
}
|