integrate befoulment into weapon edit views

- wire BefoulmentSelect into EditWeaponSidebar and WeaponEditPane
- update hasAxSkills logic to use series.augmentType
- add befoulment to save payloads and handlers
- add hasBefoulment to modificationDetector
- update TeamView to display befoulment with exorcism level
- fix story mocks
This commit is contained in:
Justin Edmund 2025-12-31 00:34:11 -08:00
parent afdeacd11e
commit 21e948be7e
6 changed files with 122 additions and 26 deletions

View file

@ -10,7 +10,7 @@
*/
import { onMount } from 'svelte'
import type { CollectionWeapon } from '$lib/types/api/collection'
import type { AugmentSkill } from '$lib/types/api/weaponStatModifier'
import type { AugmentSkill, Befoulment } from '$lib/types/api/weaponStatModifier'
import {
useUpdateCollectionWeapon,
useRemoveWeaponFromCollection
@ -89,7 +89,8 @@
level: weapon.awakening.level
}
: null,
axSkills: (weapon.ax as AugmentSkill[]) ?? []
axSkills: (weapon.ax as AugmentSkill[]) ?? [],
befoulment: (weapon.befoulment as Befoulment) ?? null
})
// Element name for theming
@ -143,15 +144,22 @@
}
// AX skills
if (updates.axModifier1 !== undefined) {
input.axModifier1 = updates.axModifier1
if (updates.axModifier1Id !== undefined) {
input.axModifier1Id = updates.axModifier1Id
input.axStrength1 = updates.axStrength1
}
if (updates.axModifier2 !== undefined) {
input.axModifier2 = updates.axModifier2
if (updates.axModifier2Id !== undefined) {
input.axModifier2Id = updates.axModifier2Id
input.axStrength2 = updates.axStrength2
}
// Befoulment
if (updates.befoulmentModifierId !== undefined) {
input.befoulmentModifierId = updates.befoulmentModifierId
input.befoulmentStrength = updates.befoulmentStrength
input.exorcismLevel = updates.exorcismLevel
}
const updatedWeapon = await updateMutation.mutateAsync({
id: weapon.id,
input

View file

@ -11,12 +11,13 @@
* - Awakening (for weapons with awakening support)
*/
import type { Weapon, Awakening } from '$lib/types/api/entities'
import type { AugmentSkill } from '$lib/types/api/weaponStatModifier'
import type { AugmentSkill, Befoulment } from '$lib/types/api/weaponStatModifier'
import DetailsSection from '$lib/components/sidebar/details/DetailsSection.svelte'
import Select from '$lib/components/ui/Select.svelte'
import WeaponKeySelect from '$lib/components/sidebar/edit/WeaponKeySelect.svelte'
import AwakeningSelect from '$lib/components/sidebar/edit/AwakeningSelect.svelte'
import AxSkillSelect from '$lib/components/sidebar/edit/AxSkillSelect.svelte'
import BefoulmentSelect from '$lib/components/sidebar/edit/BefoulmentSelect.svelte'
import UncapIndicator from '$lib/components/uncap/UncapIndicator.svelte'
import { getElementIcon } from '$lib/utils/images'
import { seriesHasWeaponKeys, getSeriesSlug } from '$lib/utils/weaponSeries'
@ -33,6 +34,7 @@
level: number
} | null
axSkills: AugmentSkill[]
befoulment?: Befoulment | null
}
export interface WeaponEditUpdates {
@ -51,6 +53,9 @@
axStrength1?: number
axModifier2Id?: string
axStrength2?: number
befoulmentModifierId?: string
befoulmentStrength?: number
exorcismLevel?: number
}
interface Props {
@ -74,6 +79,7 @@
let selectedAwakening = $state<Awakening | undefined>(currentValues.awakening?.type)
let awakeningLevel = $state(currentValues.awakening?.level ?? 1)
let axSkills = $state<AugmentSkill[]>(currentValues.axSkills ?? [])
let befoulment = $state<Befoulment | null>(currentValues.befoulment ?? null)
// Re-initialize when currentValues changes
$effect(() => {
@ -86,6 +92,7 @@
selectedAwakening = currentValues.awakening?.type
awakeningLevel = currentValues.awakening?.level ?? 1
axSkills = currentValues.axSkills ?? []
befoulment = currentValues.befoulment ?? null
})
// Derived conditions
@ -106,8 +113,10 @@
const hasWeaponKeys = $derived(seriesHasWeaponKeys(series))
const keySlotCount = $derived(seriesSlug ? (WEAPON_KEY_SLOTS[seriesSlug] ?? 2) : 0)
const hasAxSkills = $derived(weaponData?.ax === true)
const axType = $derived(weaponData?.axType ?? 1)
// Augment type from series determines AX skills vs befoulment
const augmentType = $derived(series?.augmentType ?? 'none')
const hasAxSkills = $derived(augmentType === 'ax')
const hasBefoulment = $derived(augmentType === 'befoulment')
const hasAwakening = $derived((weaponData?.maxAwakeningLevel ?? 0) > 0)
const availableAwakenings = $derived(weaponData?.awakenings ?? [])
@ -195,6 +204,15 @@
}
}
// Befoulment
if (hasBefoulment) {
if (befoulment?.modifier?.id) {
updates.befoulmentModifierId = befoulment.modifier.id
updates.befoulmentStrength = befoulment.strength
updates.exorcismLevel = befoulment.exorcismLevel
}
}
onSave?.(updates)
}
</script>
@ -267,7 +285,6 @@
<DetailsSection title="AX Skills">
<div class="section-content">
<AxSkillSelect
{axType}
currentSkills={axSkills}
onChange={(skills) => {
axSkills = skills
@ -277,6 +294,19 @@
</DetailsSection>
{/if}
{#if hasBefoulment}
<DetailsSection title="Befoulment">
<div class="section-content">
<BefoulmentSelect
currentBefoulment={befoulment}
onChange={(bef) => {
befoulment = bef
}}
/>
</div>
</DetailsSection>
{/if}
{#if hasAwakening && availableAwakenings.length > 0}
<DetailsSection title="Awakening">
<div class="section-content">

View file

@ -2,13 +2,14 @@
import type { GridWeapon } from '$lib/types/api/party'
import type { WeaponKey } from '$lib/api/adapters/entity.adapter'
import type { Awakening } from '$lib/types/api/entities'
import type { AugmentSkill } from '$lib/types/api/weaponStatModifier'
import type { AugmentSkill, Befoulment } from '$lib/types/api/weaponStatModifier'
import DetailsSection from './details/DetailsSection.svelte'
import ItemHeader from './details/ItemHeader.svelte'
import Select from '$lib/components/ui/Select.svelte'
import WeaponKeySelect from './edit/WeaponKeySelect.svelte'
import AwakeningSelect from './edit/AwakeningSelect.svelte'
import AxSkillSelect from './edit/AxSkillSelect.svelte'
import BefoulmentSelect from './edit/BefoulmentSelect.svelte'
import Button from '$lib/components/ui/Button.svelte'
import Icon from '$lib/components/Icon.svelte'
import { getElementIcon } from '$lib/utils/images'
@ -55,6 +56,9 @@
// AX skill state - initialize from existing AX skills
let axSkills = $state<AugmentSkill[]>(weapon.ax ?? [])
// Befoulment state - initialize from existing befoulment
let befoulment = $state<Befoulment | null>(weapon.befoulment ?? null)
// Weapon data shortcuts
const weaponData = $derived(weapon.weapon)
const canChangeElement = $derived(weaponData?.element === 0)
@ -77,8 +81,10 @@
const hasWeaponKeys = $derived(seriesHasWeaponKeys(series))
const keySlotCount = $derived(seriesSlug ? (WEAPON_KEY_SLOTS[seriesSlug] ?? 2) : 0)
const hasAxSkills = $derived(weaponData?.ax === true)
const axType = $derived(weaponData?.axType ?? 1)
// Augment type from series determines AX skills vs befoulment
const augmentType = $derived(series?.augmentType ?? 'none')
const hasAxSkills = $derived(augmentType === 'ax')
const hasBefoulment = $derived(augmentType === 'befoulment')
const hasAwakening = $derived((weaponData?.maxAwakeningLevel ?? 0) > 0)
const availableAwakenings = $derived(weaponData?.awakenings ?? [])
@ -125,6 +131,9 @@
axStrength1?: number | null
axModifier2Id?: string | null
axStrength2?: number | null
befoulmentModifierId?: string | null
befoulmentStrength?: number | null
exorcismLevel?: number | null
}
function handleSave() {
@ -188,6 +197,21 @@
}
}
// Befoulment - send modifier ID, strength, and exorcism level
if (hasBefoulment) {
const originalBef = weapon.befoulment
if (befoulment?.modifier?.id !== originalBef?.modifier?.id) {
updates.befoulmentModifierId = befoulment?.modifier?.id ?? null
}
if (befoulment?.strength !== originalBef?.strength) {
updates.befoulmentStrength = befoulment?.strength ?? null
}
if (befoulment?.exorcismLevel !== originalBef?.exorcismLevel) {
updates.exorcismLevel = befoulment?.exorcismLevel ?? null
}
}
// Only call onSave if there are actual updates
if (Object.keys(updates).length > 0) {
onSave?.(updates as Partial<GridWeapon>)
@ -206,6 +230,7 @@
selectedAwakening = weapon.awakening?.type
awakeningLevel = weapon.awakening?.level ?? 1
axSkills = weapon.ax ?? []
befoulment = weapon.befoulment ?? null
onCancel?.()
}
</script>
@ -286,7 +311,6 @@
<DetailsSection title="AX Skills">
<div class="ax-skills-wrapper">
<AxSkillSelect
{axType}
currentSkills={axSkills}
onChange={(skills) => {
axSkills = skills
@ -296,6 +320,19 @@
</DetailsSection>
{/if}
{#if hasBefoulment}
<DetailsSection title="Befoulment">
<div class="befoulment-wrapper">
<BefoulmentSelect
currentBefoulment={befoulment}
onChange={(bef) => {
befoulment = bef
}}
/>
</div>
</DetailsSection>
{/if}
{#if hasAwakening && availableAwakenings.length > 0}
<DetailsSection title="Awakening">
<div class="awakening-select-wrapper">
@ -428,7 +465,8 @@
padding: spacing.$unit;
}
.ax-skills-wrapper {
.ax-skills-wrapper,
.befoulment-wrapper {
padding: spacing.$unit;
}

View file

@ -105,17 +105,32 @@
</DetailsSection>
{/if}
{#if modificationStatus.hasAxSkills && weapon.ax}
{#if modificationStatus.hasAxSkills && weapon.ax?.length}
<DetailsSection title="AX Skills">
{#each weapon.ax as axSkill}
<DetailRow
label={formatAxSkill(axSkill).split('+')[0]?.trim() ?? ''}
value={`+${axSkill.strength}${axSkill.modifier <= 2 ? '' : '%'}`}
/>
{#if axSkill.modifier?.id}
<DetailRow
label={axSkill.modifier.nameEn}
value={`+${axSkill.strength}${axSkill.modifier.suffix ?? ''}`}
/>
{/if}
{/each}
</DetailsSection>
{/if}
{#if modificationStatus.hasBefoulment && weapon.befoulment?.modifier}
<DetailsSection title="Befoulment">
<DetailRow
label={weapon.befoulment.modifier.nameEn}
value={`${weapon.befoulment.strength}${weapon.befoulment.modifier.suffix ?? ''}`}
/>
<DetailRow
label="Exorcism Level"
value={`${weapon.befoulment.exorcismLevel ?? 0}`}
/>
</DetailsSection>
{/if}
{#if modificationStatus.hasElement && weapon.element}
<DetailsSection title="Element Override">
<DetailRow label="Weapon Element">

View file

@ -6,6 +6,7 @@ export interface ModificationStatus {
hasAwakening: boolean
hasWeaponKeys: boolean
hasAxSkills: boolean
hasBefoulment: boolean
hasRings: boolean
hasEarring: boolean
hasPerpetuity: boolean
@ -25,6 +26,7 @@ export function detectModifications(
hasAwakening: false,
hasWeaponKeys: false,
hasAxSkills: false,
hasBefoulment: false,
hasRings: false,
hasEarring: false,
hasPerpetuity: false,
@ -60,6 +62,7 @@ export function detectModifications(
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)
@ -68,6 +71,7 @@ export function detectModifications(
status.hasAwakening ||
status.hasWeaponKeys ||
status.hasAxSkills ||
status.hasBefoulment ||
status.hasTranscendence ||
status.hasUncapLevel ||
status.hasElement
@ -111,13 +115,14 @@ export function canWeaponBeModified(gridWeapon: GridWeapon | undefined): boolean
// Weapon keys (series-specific) - use utility function that handles both formats
const hasWeaponKeys = seriesHasWeaponKeys(weapon.series)
// AX skills
const hasAxSkills = weapon.ax === true
// 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 || hasAxSkills || hasAwakening
return canChangeElement || hasWeaponKeys || hasAugments || hasAwakening
}
/**

View file

@ -8,7 +8,7 @@ const mockOmegaSeries = {
name: { en: 'Omega', ja: 'マグナ' },
hasWeaponKeys: false,
hasAwakening: true,
hasAxSkills: true,
augmentType: 'ax' as const,
extra: false,
elementChangeable: false
};
@ -19,7 +19,7 @@ const mockOpusSeries = {
name: { en: 'Opus', ja: 'オプス' },
hasWeaponKeys: true,
hasAwakening: true,
hasAxSkills: false,
augmentType: 'none' as const,
extra: false,
elementChangeable: false
};
@ -30,7 +30,7 @@ const mockDraconicSeries = {
name: { en: 'Draconic', ja: 'ドラゴニック' },
hasWeaponKeys: true,
hasAwakening: false,
hasAxSkills: false,
augmentType: 'none' as const,
extra: false,
elementChangeable: false
};