add modifier utils for weapon keys, awakenings, and stat modifications
This commit is contained in:
parent
47d64d6fe1
commit
f090d2fe41
3 changed files with 314 additions and 0 deletions
99
src/lib/utils/modificationDetector.ts
Normal file
99
src/lib/utils/modificationDetector.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import type { GridCharacter, GridWeapon, GridSummon } from '$lib/types/api/party'
|
||||||
|
|
||||||
|
export interface ModificationStatus {
|
||||||
|
hasModifications: boolean
|
||||||
|
hasAwakening: boolean
|
||||||
|
hasWeaponKeys: boolean
|
||||||
|
hasAxSkills: 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,
|
||||||
|
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.rings && char.rings.length > 0) ||
|
||||||
|
!!(char.over_mastery && char.over_mastery.length > 0)
|
||||||
|
status.hasEarring = !!char.earring || !!char.aetherial_mastery
|
||||||
|
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.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.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
|
||||||
|
}
|
||||||
115
src/lib/utils/modificationFormatters.ts
Normal file
115
src/lib/utils/modificationFormatters.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import type { SimpleAxSkill } from '$lib/types/api/entities'
|
||||||
|
|
||||||
|
const RING_STAT_NAMES: Record<number, string> = {
|
||||||
|
1: 'HP',
|
||||||
|
2: 'Attack',
|
||||||
|
3: 'Double Attack',
|
||||||
|
4: 'Triple Attack',
|
||||||
|
5: 'Elemental Attack',
|
||||||
|
6: 'Critical Hit',
|
||||||
|
7: 'Skill Damage',
|
||||||
|
8: 'Skill DMG Cap',
|
||||||
|
9: 'Ougi Damage',
|
||||||
|
10: 'Ougi DMG Cap',
|
||||||
|
11: 'Chain Burst DMG',
|
||||||
|
12: 'Chain Burst Cap',
|
||||||
|
13: 'Healing',
|
||||||
|
14: 'Healing Cap',
|
||||||
|
15: 'Stamina',
|
||||||
|
16: 'Enmity',
|
||||||
|
17: 'Debuff Success'
|
||||||
|
}
|
||||||
|
|
||||||
|
const EARRING_STAT_NAMES: Record<number, string> = {
|
||||||
|
1: 'HP',
|
||||||
|
2: 'Attack',
|
||||||
|
3: 'Defense',
|
||||||
|
4: 'Double Attack',
|
||||||
|
5: 'Triple Attack',
|
||||||
|
6: 'Elemental Attack',
|
||||||
|
7: 'Critical Hit',
|
||||||
|
8: 'Skill Damage',
|
||||||
|
9: 'Skill DMG Cap',
|
||||||
|
10: 'Ougi Damage',
|
||||||
|
11: 'Ougi DMG Cap',
|
||||||
|
12: 'Auto Attack Cap',
|
||||||
|
13: 'Chain Burst DMG',
|
||||||
|
14: 'Chain Burst Cap',
|
||||||
|
15: 'Healing',
|
||||||
|
16: 'Healing Cap'
|
||||||
|
}
|
||||||
|
|
||||||
|
const AX_SKILL_NAMES: Record<number, string> = {
|
||||||
|
1: 'Attack',
|
||||||
|
2: 'HP',
|
||||||
|
3: 'Double Attack',
|
||||||
|
4: 'Triple Attack',
|
||||||
|
5: 'C.A. DMG',
|
||||||
|
6: 'C.A. DMG Cap',
|
||||||
|
7: 'Skill DMG',
|
||||||
|
8: 'Skill DMG Cap',
|
||||||
|
9: 'Stamina',
|
||||||
|
10: 'Enmity',
|
||||||
|
11: 'Critical Hit'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatRingStat(modifier: number, strength: number): string {
|
||||||
|
const statName = RING_STAT_NAMES[modifier] || `Unknown (${modifier})`
|
||||||
|
const suffix = modifier <= 2 ? '' : '%'
|
||||||
|
return `${statName} +${strength}${suffix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatEarringStat(modifier: number, strength: number): string {
|
||||||
|
const statName = EARRING_STAT_NAMES[modifier] || `Unknown (${modifier})`
|
||||||
|
const suffix = modifier <= 3 ? '' : '%'
|
||||||
|
return `${statName} +${strength}${suffix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatAxSkill(ax: SimpleAxSkill): string {
|
||||||
|
const skillName = AX_SKILL_NAMES[ax.modifier] || `Unknown (${ax.modifier})`
|
||||||
|
const suffix = ax.modifier <= 2 ? '' : '%'
|
||||||
|
return `${skillName} +${ax.strength}${suffix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeaponKeyTitle(series?: number): string {
|
||||||
|
switch (series) {
|
||||||
|
case 2:
|
||||||
|
return 'Opus Pendulums'
|
||||||
|
case 3:
|
||||||
|
case 34:
|
||||||
|
return 'Draconic Telumas'
|
||||||
|
case 17:
|
||||||
|
return 'Ultima Keys'
|
||||||
|
case 22:
|
||||||
|
return 'Revans Emblems'
|
||||||
|
default:
|
||||||
|
return 'Weapon Keys'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatUncapLevel(level?: number | null): string {
|
||||||
|
if (level === undefined || level === null) return '0★'
|
||||||
|
return `${level}★`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTranscendenceStep(step?: number | null): string {
|
||||||
|
if (!step || step === 0) return ''
|
||||||
|
return `Stage ${step}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStatModifierIcon(type: 'ring' | 'earring', modifier: number): string | null {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElementName(element?: number | null): string {
|
||||||
|
switch (element) {
|
||||||
|
case 0: return 'Null'
|
||||||
|
case 1: return 'Wind'
|
||||||
|
case 2: return 'Fire'
|
||||||
|
case 3: return 'Water'
|
||||||
|
case 4: return 'Earth'
|
||||||
|
case 5: return 'Dark'
|
||||||
|
case 6: return 'Light'
|
||||||
|
default: return 'Unknown'
|
||||||
|
}
|
||||||
|
}
|
||||||
100
src/lib/utils/modifiers.ts
Normal file
100
src/lib/utils/modifiers.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
/**
|
||||||
|
* Utility functions for weapon and character modifiers (awakenings, weapon keys, AX skills)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Awakening, WeaponKey, SimpleAxSkill } from '$lib/types'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the image URL for an awakening type
|
||||||
|
*/
|
||||||
|
export function getAwakeningImage(awakening?: { type?: Awakening; level?: number }): string | null {
|
||||||
|
if (!awakening?.type?.slug) return null
|
||||||
|
return `/images/awakening/${awakening.type.slug}.png`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the image URL for a weapon key with proper element/proficiency/mod variants
|
||||||
|
*/
|
||||||
|
export function getWeaponKeyImage(
|
||||||
|
key: WeaponKey,
|
||||||
|
weaponElement?: number,
|
||||||
|
weaponProficiency?: number,
|
||||||
|
weaponSeries?: number,
|
||||||
|
weaponName?: { en?: string }
|
||||||
|
): string {
|
||||||
|
if (!key.slug) return ''
|
||||||
|
|
||||||
|
const baseUrl = '/images/weapon-keys/'
|
||||||
|
let filename = key.slug
|
||||||
|
|
||||||
|
// Handle element-specific telumas (Draconic weapons)
|
||||||
|
const elementalTelumas = [15008, 16001, 16002]
|
||||||
|
const granblueId = parseInt(key.granblueId || '0')
|
||||||
|
|
||||||
|
if (elementalTelumas.includes(granblueId) && weaponElement) {
|
||||||
|
filename += `-${weaponElement}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle proficiency-specific ultima keys (slot 0)
|
||||||
|
if (key.slot === 0 && weaponSeries === 17 && weaponProficiency) {
|
||||||
|
filename += `-${weaponProficiency}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle element-specific opus pendulums (slot 1)
|
||||||
|
if (weaponSeries === 2 && key.slot === 1 && weaponElement) {
|
||||||
|
const mod = weaponName?.en?.includes('Repudiation') ? 'primal' : 'magna'
|
||||||
|
const suffixes = [
|
||||||
|
'pendulum-strength',
|
||||||
|
'pendulum-zeal',
|
||||||
|
'pendulum-strife',
|
||||||
|
'chain-temperament',
|
||||||
|
'chain-restoration',
|
||||||
|
'chain-glorification'
|
||||||
|
]
|
||||||
|
|
||||||
|
if (suffixes.includes(key.slug)) {
|
||||||
|
filename += `-${mod}-${weaponElement}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${baseUrl}${filename}.png`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all weapon key images for a weapon
|
||||||
|
*/
|
||||||
|
export function getWeaponKeyImages(
|
||||||
|
keys?: WeaponKey[],
|
||||||
|
weaponElement?: number,
|
||||||
|
weaponProficiency?: number,
|
||||||
|
weaponSeries?: number,
|
||||||
|
weaponName?: { en?: string }
|
||||||
|
): Array<{ url: string; alt: string }> {
|
||||||
|
if (!keys || keys.length === 0) return []
|
||||||
|
|
||||||
|
return keys
|
||||||
|
.filter(key => key.slug)
|
||||||
|
.map(key => ({
|
||||||
|
url: getWeaponKeyImage(key, weaponElement, weaponProficiency, weaponSeries, weaponName),
|
||||||
|
alt: key.name?.en || key.slug || 'Weapon Key'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the image URL for an AX skill
|
||||||
|
* Note: Requires ax data reference implementation
|
||||||
|
*/
|
||||||
|
export function getAxSkillImage(axSkill?: { slug?: string }): string | null {
|
||||||
|
if (!axSkill?.slug) return null
|
||||||
|
return `/images/ax/${axSkill.slug}.png`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all AX skill images for a weapon
|
||||||
|
* Note: This is a placeholder until ax data structure is fully implemented
|
||||||
|
*/
|
||||||
|
export function getAxSkillImages(ax?: SimpleAxSkill[]): Array<{ url: string; alt: string }> {
|
||||||
|
// TODO: Implement when ax data reference is available
|
||||||
|
// This would need to map ax modifiers to actual ax skill data
|
||||||
|
return []
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue