60 lines
4.6 KiB
Svelte
60 lines
4.6 KiB
Svelte
<script lang="ts">
|
|
import type { SkillCalculation } from '$lib/calculations/skill';
|
|
|
|
let {
|
|
result,
|
|
abilityNames = {},
|
|
resolverHint = 'Complete the required Specialization ability data.'
|
|
}: {
|
|
result: SkillCalculation;
|
|
abilityNames?: Record<string, string>;
|
|
resolverHint?: string;
|
|
} = $props();
|
|
const number = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 });
|
|
</script>
|
|
|
|
<article class:incomplete={result.status === 'INSUFFICIENT_DATA'} class="skill-calculation" aria-live="polite">
|
|
<header>
|
|
<div><p>SKILL OUTPUT</p><h3>Specialization abilities</h3></div>
|
|
<b>{result.status === 'OK' ? 'CALCULATED' : 'INSUFFICIENT DATA'}</b>
|
|
</header>
|
|
{#if result.status === 'INSUFFICIENT_DATA'}
|
|
<div class="missing" role="status">
|
|
<strong>SKILL OUTPUT BLOCKED</strong>
|
|
<span>{result.missing[0]?.replaceAll(':', ' · ') ?? 'Missing ability data'}</span>
|
|
<small>{result.missing.length > 1 ? `+${result.missing.length - 1} additional requirement${result.missing.length === 2 ? '' : 's'}. ` : ''}{resolverHint}</small>
|
|
</div>
|
|
{:else}
|
|
<div class="combined">
|
|
<span><small>SINGLE-TARGET DPS</small><b>{number.format(result.combined.singleTargetDps)}</b></span>
|
|
<span><small>ALL-TARGET DPS</small><b>{number.format(result.combined.aggregateDps)}</b></span>
|
|
<span><small>ENCOUNTER DAMAGE</small><b>{number.format(result.combined.aggregateEncounterDamage)}</b></span>
|
|
</div>
|
|
<div class="ability-results">
|
|
{#each result.abilities as ability}
|
|
<div class:disabled={!ability.enabled}>
|
|
<strong>{abilityNames[ability.abilityId] ?? ability.abilityId}</strong>
|
|
<span>{ability.enabled ? `${ability.activationCount} ACTIVATIONS` : 'OFF'}</span>
|
|
<small>{number.format(ability.damage.singleTargetDps)} SINGLE DPS</small>
|
|
<small>{number.format(ability.damage.aggregateDps)} TOTAL DPS</small>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
{#if result.status === 'OK'}<footer>SKILLS CAN CRIT AND MULTI-SHOT · SKILLS DO NOT HEADSHOT · ENCOUNTER TRIGGERS FOLLOW THE PRIMARY WEAPON TIMELINE</footer>{/if}
|
|
</article>
|
|
|
|
<style>
|
|
.skill-calculation{display:grid;gap:var(--shd-gap-md);padding:var(--shd-gap-md);border:1px solid var(--shd-border);border-top:2px solid var(--shd-info);background:var(--shd-surface)}
|
|
.skill-calculation.incomplete{gap:6px;padding:8px;border-top-color:var(--shd-danger)}
|
|
header{display:flex;justify-content:space-between;align-items:start;gap:var(--shd-gap-md)}
|
|
header p,h3{margin:0}header p{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:1.1px}h3{text-transform:uppercase;font-size:var(--shd-type-body);letter-spacing:.7px}
|
|
header>b{padding:5px 7px;border:1px solid var(--shd-info);color:var(--shd-info);font-size:var(--shd-type-telemetry);letter-spacing:.8px}.incomplete header h3{font-size:10px}.incomplete header>b{padding:3px 5px;border-color:var(--shd-danger);color:var(--shd-danger);font-size:8px}
|
|
.combined{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--shd-border)}.combined span{display:grid;gap:4px;padding:9px;border-left:1px solid var(--shd-border)}.combined span:first-child{border-left:0}.combined b{color:var(--shd-info);font-size:20px}
|
|
small{color:var(--shd-text-muted);font-size:var(--shd-type-telemetry);font-weight:900;letter-spacing:.7px}
|
|
.ability-results{display:grid;border:1px solid var(--shd-border)}.ability-results>div{display:grid;grid-template-columns:minmax(140px,1fr) repeat(3,auto);gap:var(--shd-gap-sm);padding:7px 8px;border-top:1px solid var(--shd-border);align-items:center}.ability-results>div:first-child{border-top:0}.ability-results>div.disabled{opacity:.55}.ability-results span{color:var(--shd-orange);font-size:var(--shd-type-telemetry);font-weight:900}
|
|
.missing{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:3px 8px;padding:7px 8px;border:1px solid var(--shd-danger);border-left-width:2px;background:#171111}.missing strong{color:var(--shd-danger);font-size:9px;letter-spacing:.8px;white-space:nowrap}.missing>span{overflow:hidden;color:var(--shd-text);font-size:var(--shd-type-telemetry);font-weight:800;text-overflow:ellipsis;white-space:nowrap}.missing>small{grid-column:1/-1;color:var(--shd-text-dim);font-size:9px;letter-spacing:.35px}
|
|
footer{color:var(--shd-text-dim);font-size:var(--shd-type-telemetry);font-weight:800;letter-spacing:.6px}
|
|
@media(max-width:700px){header,.combined{display:grid;grid-template-columns:1fr}.skill-calculation.incomplete>header{display:flex}.combined span{border-left:0;border-top:1px solid var(--shd-border)}.combined span:first-child{border-top:0}.ability-results>div{grid-template-columns:1fr 1fr}.ability-results strong{grid-column:1/-1}}
|
|
</style>
|