show calculated skill values in dropdown, add level constraints
This commit is contained in:
parent
4b2d1b7dc0
commit
f34f2c4dc9
1 changed files with 73 additions and 100 deletions
|
|
@ -3,13 +3,21 @@
|
|||
<script lang="ts">
|
||||
import DisclosureRow from '$lib/components/ui/DisclosureRow.svelte'
|
||||
import Select from '$lib/components/ui/Select.svelte'
|
||||
import type { ArtifactSkill, ArtifactSkillInstance } from '$lib/types/api/artifact'
|
||||
import {
|
||||
calculateSkillDisplayValue,
|
||||
type ArtifactSkill,
|
||||
type ArtifactSkillInstance
|
||||
} from '$lib/types/api/artifact'
|
||||
|
||||
interface Props {
|
||||
/** Slot number (1-4) */
|
||||
slot: number
|
||||
/** Current skill configuration (null if not set) */
|
||||
skill: ArtifactSkillInstance | null
|
||||
/** All skills array (for calculating level constraints) */
|
||||
allSkills: (ArtifactSkillInstance | null)[]
|
||||
/** Artifact level (1-5) - determines total skill level budget */
|
||||
artifactLevel: number
|
||||
/** Available skills for this slot (from query) */
|
||||
availableSkills?: ArtifactSkill[]
|
||||
/** Handler to open modifier selection pane */
|
||||
|
|
@ -23,6 +31,8 @@
|
|||
const {
|
||||
slot,
|
||||
skill,
|
||||
allSkills,
|
||||
artifactLevel,
|
||||
availableSkills = [],
|
||||
onSelectModifier,
|
||||
onUpdateSkill,
|
||||
|
|
@ -38,24 +48,57 @@
|
|||
const modifierName = $derived(currentSkillDef?.name?.en ?? 'Unknown')
|
||||
|
||||
// Build strength options from the skill's baseValues
|
||||
// Display values are calculated based on current level, but stored value is the base
|
||||
const strengthOptions = $derived(() => {
|
||||
if (!currentSkillDef?.baseValues) return []
|
||||
|
||||
const growth = currentSkillDef.growth ?? 0
|
||||
const currentLevel = skill?.level ?? 1
|
||||
const suffix = currentSkillDef.suffix?.en ?? ''
|
||||
const sign = currentSkillDef.polarity === 'negative' ? '−' : '+'
|
||||
|
||||
return currentSkillDef.baseValues
|
||||
.map((v, i) => ({
|
||||
value: v ?? 0,
|
||||
label: `${v}${currentSkillDef.suffix?.en ?? ''}`
|
||||
}))
|
||||
.filter((opt) => opt.value !== 0)
|
||||
.filter((v): v is number => v !== null && v !== 0)
|
||||
.map((baseValue) => {
|
||||
const displayValue = calculateSkillDisplayValue(baseValue, growth, currentLevel)
|
||||
return {
|
||||
value: baseValue, // Store the BASE value
|
||||
label: `${sign}${displayValue}${suffix}` // Show polarity + calculated value
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Build level options (1-5)
|
||||
const levelOptions = [
|
||||
{ value: 1, label: '1' },
|
||||
{ value: 2, label: '2' },
|
||||
{ value: 3, label: '3' },
|
||||
{ value: 4, label: '4' },
|
||||
{ value: 5, label: '5' }
|
||||
]
|
||||
// Calculate the maximum level this skill can have based on:
|
||||
// - Total budget: artifactLevel + 3
|
||||
// - Levels used by other skills
|
||||
// - Each skill must have at least level 1
|
||||
const levelOptions = $derived(() => {
|
||||
const totalBudget = artifactLevel + 3
|
||||
const currentSlotIndex = slot - 1
|
||||
|
||||
// Sum levels of other skills (excluding this one)
|
||||
const otherSkillsLevelSum = allSkills.reduce((sum, s, i) => {
|
||||
if (i === currentSlotIndex || !s) return sum
|
||||
return sum + s.level
|
||||
}, 0)
|
||||
|
||||
// Count skills that are set (excluding this one) - they each need at least level 1
|
||||
const otherSetSkillsCount = allSkills.filter((s, i) => i !== currentSlotIndex && s).length
|
||||
|
||||
// Remaining budget for this skill
|
||||
// We need to reserve 1 level for each unset skill slot (since they'll need at least 1 when set)
|
||||
const unsetSlotsCount = allSkills.filter((s, i) => i !== currentSlotIndex && !s).length
|
||||
const reservedForUnset = unsetSlotsCount // Each unset slot will need at least 1 when filled
|
||||
|
||||
const maxLevel = Math.min(5, totalBudget - otherSkillsLevelSum - reservedForUnset)
|
||||
const minLevel = 1
|
||||
|
||||
const options: { value: number; label: string }[] = []
|
||||
for (let i = minLevel; i <= maxLevel; i++) {
|
||||
options.push({ value: i, label: String(i) })
|
||||
}
|
||||
return options
|
||||
})
|
||||
|
||||
function handleStrengthChange(newStrength: number) {
|
||||
onUpdateSkill({ strength: newStrength })
|
||||
|
|
@ -76,49 +119,34 @@
|
|||
{disabled}
|
||||
/>
|
||||
{:else}
|
||||
<!-- Set: Show modifier name + value/level controls -->
|
||||
<!-- Set: Show modifier name (clickable) + value/level controls -->
|
||||
<div class="skill-row-set" class:disabled>
|
||||
<div class="skill-header">
|
||||
<div class="modifier-info">
|
||||
<span class="modifier-name">{modifierName}</span>
|
||||
{#if currentSkillDef?.polarity === 'negative'}
|
||||
<span class="polarity negative">−</span>
|
||||
{:else}
|
||||
<span class="polarity positive">+</span>
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="change-btn"
|
||||
onclick={onSelectModifier}
|
||||
{disabled}
|
||||
>
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
<DisclosureRow
|
||||
label={modifierName}
|
||||
onclick={onSelectModifier}
|
||||
{disabled}
|
||||
/>
|
||||
<div class="skill-controls">
|
||||
<div class="control-group">
|
||||
<label class="control-label">Level</label>
|
||||
<Select
|
||||
value={skill.level}
|
||||
options={levelOptions()}
|
||||
contained
|
||||
onValueChange={(v) => v !== undefined && handleLevelChange(v)}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Value</label>
|
||||
<Select
|
||||
value={skill.strength}
|
||||
options={strengthOptions()}
|
||||
size="small"
|
||||
contained
|
||||
onValueChange={(v) => v !== undefined && handleStrengthChange(v)}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Level</label>
|
||||
<Select
|
||||
value={skill.level}
|
||||
options={levelOptions}
|
||||
size="small"
|
||||
contained
|
||||
onValueChange={(v) => v !== undefined && handleLevelChange(v)}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -129,7 +157,6 @@
|
|||
@use '$src/themes/spacing' as spacing;
|
||||
@use '$src/themes/typography' as typography;
|
||||
@use '$src/themes/layout' as layout;
|
||||
@use '$src/themes/effects' as effects;
|
||||
|
||||
.skill-row {
|
||||
// Minimal container for skill row
|
||||
|
|
@ -150,60 +177,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.skill-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modifier-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: spacing.$unit-half;
|
||||
}
|
||||
|
||||
.modifier-name {
|
||||
font-weight: typography.$medium;
|
||||
color: colors.$grey-20;
|
||||
}
|
||||
|
||||
.polarity {
|
||||
font-size: typography.$font-small;
|
||||
font-weight: typography.$bold;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
|
||||
&.positive {
|
||||
background: colors.$wind-bg-20;
|
||||
color: colors.$wind-text-20;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
background: colors.$error--bg--light;
|
||||
color: colors.$error;
|
||||
}
|
||||
}
|
||||
|
||||
.change-btn {
|
||||
font-size: typography.$font-small;
|
||||
color: colors.$blue;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: spacing.$unit-fourth spacing.$unit-half;
|
||||
border-radius: layout.$item-corner;
|
||||
transition: background-color effects.$duration-quick ease;
|
||||
|
||||
&:hover {
|
||||
background: colors.$grey-90;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.skill-controls {
|
||||
display: flex;
|
||||
gap: spacing.$unit-2x;
|
||||
|
|
|
|||
Loading…
Reference in a new issue