add maxExorcismLevel to weapons (#452)

## Summary
- add `maxExorcismLevel` to Weapon type
- add field to WeaponStatsSection in database pages (with min=1
validation)
- include in new/edit/import page payloads
- make BefoulmentSelect exorcism options dynamic (0 to maxExorcismLevel)
- pass maxExorcismLevel to BefoulmentSelect from edit panes

## Test plan
- [ ] verify maxExorcismLevel field appears in weapon stats section
- [ ] verify min=1 validation works
- [ ] verify exorcism dropdown shows correct options based on weapon's
maxExorcismLevel
This commit is contained in:
Justin Edmund 2026-01-04 14:43:53 -08:00 committed by GitHub
parent 36e3b39a20
commit 90e5ded942
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 45 additions and 14 deletions

View file

@ -302,6 +302,7 @@
onChange={(bef) => {
befoulment = bef
}}
maxExorcismLevel={weaponData?.maxExorcismLevel}
/>
</div>
</DetailsSection>

View file

@ -328,6 +328,7 @@
onChange={(bef) => {
befoulment = bef
}}
maxExorcismLevel={weaponData?.maxExorcismLevel}
/>
</div>
</DetailsSection>

View file

@ -12,9 +12,11 @@
onChange?: (befoulment: Befoulment | null) => void
/** Language for display */
locale?: 'en' | 'ja'
/** Maximum exorcism level for this weapon (from weapon's maxExorcismLevel) */
maxExorcismLevel?: number | null
}
let { currentBefoulment = null, onChange, locale = 'en' }: Props = $props()
let { currentBefoulment = null, onChange, locale = 'en', maxExorcismLevel = null }: Props = $props()
const { befoulments, findBefoulment, isLoading } = useWeaponStatModifiers()
@ -44,15 +46,14 @@
return items
})
// Exorcism level options (0-5)
const exorcismOptions: Array<{ value: number; label: string }> = [
{ value: 0, label: 'Level 0 (Full Effect)' },
{ value: 1, label: 'Level 1' },
{ value: 2, label: 'Level 2' },
{ value: 3, label: 'Level 3' },
{ value: 4, label: 'Level 4' },
{ value: 5, label: 'Level 5 (Fully Exorcised)' }
]
// Exorcism level options (0 to maxExorcismLevel, fallback to 5)
const exorcismOptions = $derived.by(() => {
const max = maxExorcismLevel ?? 5
return Array.from({ length: max + 1 }, (_, i) => ({
value: i,
label: `Level ${i}`
}))
})
// Get suffix for display
function getSuffix(modifier: WeaponStatModifier | undefined): string {

View file

@ -28,7 +28,9 @@
onchange,
width,
linkUrl,
hasLinkButton = false
hasLinkButton = false,
min,
max
}: {
label: string
/** Secondary label displayed below the main label */
@ -48,6 +50,10 @@
linkUrl?: string | null
/** Whether to show the link button (disabled when linkUrl is empty) */
hasLinkButton?: boolean
/** Minimum value for number inputs */
min?: number
/** Maximum value for number inputs */
max?: number
} = $props()
// For checkbox type, derive the checked state from value
@ -110,6 +116,8 @@
contained={true}
{placeholder}
alignRight={true}
{min}
{max}
/>
{:else if type === 'date'}
<DatePicker bind:value={value as string | null} contained={true} {placeholder} />

View file

@ -158,6 +158,15 @@
type="number"
placeholder="0"
/>
<DetailItem
label="Max Exorcism Level"
sublabel="For befoulment weapons only"
bind:value={editData.maxExorcismLevel}
editable={true}
type="number"
placeholder="—"
min={1}
/>
{:else}
<DetailItem label="Max Level" value={weapon.maxLevel} />
{#if weapon.maxSkillLevel}
@ -166,5 +175,8 @@
{#if weapon.maxAwakeningLevel}
<DetailItem label="Max Awakening Level" value={weapon.maxAwakeningLevel} />
{/if}
{#if weapon.maxExorcismLevel != null}
<DetailItem label="Max Exorcism Level" value={weapon.maxExorcismLevel} />
{/if}
{/if}
</DetailsContainer>

View file

@ -20,6 +20,7 @@ export interface Weapon {
maxLevel: number
maxSkillLevel: number
maxAwakeningLevel: number
maxExorcismLevel?: number | null
/** Weapon series - object with slug/name/flags */
series: WeaponSeriesRef | null
ax: boolean

View file

@ -87,6 +87,7 @@
maxLevel: 100,
maxSkillLevel: 10,
maxAwakeningLevel: 0,
maxExorcismLevel: null as number | null,
flb: false,
ulb: false,
transcendence: false,
@ -134,6 +135,7 @@
maxLevel: weapon.maxLevel || 100,
maxSkillLevel: weapon.maxSkillLevel || 10,
maxAwakeningLevel: weapon.maxAwakeningLevel || 0,
maxExorcismLevel: weapon.maxExorcismLevel ?? null,
flb: weapon.uncap?.flb || false,
ulb: weapon.uncap?.ulb || false,
transcendence: weapon.uncap?.transcendence || false,
@ -186,6 +188,7 @@
max_level: editData.maxLevel,
max_skill_level: editData.maxSkillLevel,
max_awakening_level: editData.maxAwakeningLevel,
max_exorcism_level: editData.maxExorcismLevel,
flb: editData.flb,
ulb: editData.ulb,
transcendence: editData.transcendence,

View file

@ -137,9 +137,10 @@
maxLevel: parsedData?.maxLevel ?? 100,
maxSkillLevel: 10,
maxAwakeningLevel: 0,
flb: parsedData?.flb ?? false,
ulb: parsedData?.ulb ?? false,
transcendence: parsedData?.transcendence ?? false,
maxExorcismLevel: null as number | null,
flb: suggestions?.flb ?? false,
ulb: suggestions?.ulb ?? false,
transcendence: suggestions?.transcendence ?? false,
extraPrerequisite: '' as number | '',
extra: false,
limit: false,
@ -277,6 +278,7 @@
max_level: formData.maxLevel,
max_skill_level: formData.maxSkillLevel,
max_awakening_level: formData.maxAwakeningLevel,
max_exorcism_level: formData.maxExorcismLevel,
flb: formData.flb,
ulb: formData.ulb,
transcendence: formData.transcendence,

View file

@ -75,6 +75,7 @@
maxLevel: 100,
maxSkillLevel: 10,
maxAwakeningLevel: 0,
maxExorcismLevel: null as number | null,
// Uncap
flb: false,
@ -184,6 +185,7 @@
max_level: editData.maxLevel,
max_skill_level: editData.maxSkillLevel,
max_awakening_level: editData.maxAwakeningLevel,
max_exorcism_level: editData.maxExorcismLevel,
// Uncap
flb: editData.flb,