72 lines
6.2 KiB
Svelte
72 lines
6.2 KiB
Svelte
<script lang="ts">
|
||
import { gearSlots } from '$lib/builder/loadout';
|
||
import AgentIdentityLink from '$lib/components/AgentIdentityLink.svelte';
|
||
import BuildShareControl from '$lib/components/BuildShareControl.svelte';
|
||
import LoadoutStatsSummary from '$lib/components/builder/LoadoutStatsSummary.svelte';
|
||
import PlannedCombatModel from '$lib/components/builder/PlannedCombatModel.svelte';
|
||
let { data } = $props();
|
||
const gear = (id: string) => data.gear.find((item) => item.id === id);
|
||
const weapon = (id: string) => data.weapons.find((item) => item.id === id);
|
||
const chip = (id: string) => data.chips.find((item) => item.id === id);
|
||
const protocol = (id: string) => data.protocols.find((item) => item.id === id);
|
||
const brand = (id: string) => data.brands.find((item) => item.id === id);
|
||
const attribute = (id: string) => data.attributes.find((item) => item.id === id);
|
||
const number = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 });
|
||
const rollLine = (rolls: Array<{ statId: string; value: number }>) => rolls
|
||
.filter((roll) => roll.statId)
|
||
.map((roll) => `${attribute(roll.statId)?.name ?? 'Attribute'} ${number.format(roll.value)}${attribute(roll.statId)?.unit ?? ''}`)
|
||
.join(' · ');
|
||
const coreLine = (selection: { firepower: number; toughness: number; engineering: number }) =>
|
||
`FP ${number.format(selection.firepower)} · TGH ${number.format(selection.toughness)} · ENG ${number.format(selection.engineering)}`;
|
||
const description = $derived(`${data.build.title}, a Division Resurgence Build by ${data.build.author}. View its equipped weapons, gear, Skill Chips, and known Agent stats on SHD.build.`);
|
||
const canonicalUrl = $derived(new URL(`/builds/${encodeURIComponent(data.build.slug)}`, data.publicOrigin).toString());
|
||
const previewUrl = $derived(new URL(`${canonicalUrl}/preview.png`).toString());
|
||
</script>
|
||
|
||
<svelte:head>
|
||
<title>{data.build.title} — SHD.build</title>
|
||
<meta name="description" content={description}/>
|
||
<link rel="canonical" href={canonicalUrl}/>
|
||
<meta property="og:type" content="website"/>
|
||
<meta property="og:site_name" content="SHD.build"/>
|
||
<meta property="og:title" content={`${data.build.title} — SHD.build`}/>
|
||
<meta property="og:description" content={description}/>
|
||
<meta property="og:url" content={canonicalUrl}/>
|
||
{#if data.build.visibility !== 'private'}
|
||
<meta property="og:image" content={previewUrl}/>
|
||
<meta property="og:image:width" content="1200"/>
|
||
<meta property="og:image:height" content="630"/>
|
||
<meta property="og:image:type" content="image/png"/>
|
||
<meta property="og:image:alt" content={`Build preview for ${data.build.title}`}/>
|
||
<meta name="twitter:card" content="summary_large_image"/>
|
||
<meta name="twitter:image" content={previewUrl}/>
|
||
{/if}
|
||
{#if data.build.visibility !== 'public'}<meta name="robots" content="noindex,nofollow"/>{/if}
|
||
</svelte:head>
|
||
<main class="app-page shared-build-page">
|
||
<header class="shared-build-command">
|
||
<div><p class="eyebrow"><span></span> SHD LOADOUT ARCHIVE / {data.build.visibility.toUpperCase()}</p><h1>{data.build.title}</h1><AgentIdentityLink size="compact" linked={data.build.profileAvailable} agent={{ id: data.build.ownerId, displayName: data.build.author, publicSlug: data.build.authorPublicSlug, avatarUrl: data.build.authorAvatarUrl }} meta={`UPDATED ${new Date(data.build.updatedAt).toLocaleDateString()}`}/></div>
|
||
<div>{#if data.build.owner}<a class="secondary" href={`/guides/new?build=${encodeURIComponent(data.build.id)}`}>CREATE GUIDE FOR BUILD</a><a class="secondary" href={`/builder?build=${data.build.slug}`}>EDIT LOADOUT</a>{/if}<a class="primary" href="/builder">NEW BUILD +</a></div>
|
||
</header>
|
||
<BuildShareControl title={data.build.title} slugOrUrl={canonicalUrl} visibility={data.build.visibility}/>
|
||
<div class="shared-build-intelligence">
|
||
<LoadoutStatsSummary summary={data.summary} attributes={data.attributes} talents={data.talents} headingId="shared-known-stats-heading" layout="page" />
|
||
<PlannedCombatModel headingId="shared-planned-model-heading" headingLevel="h2" variant="compact" />
|
||
</div>
|
||
<div class="shared-build-grid">
|
||
<section>
|
||
<header><span>WEAPONS</span><b>02 SLOTS</b></header>
|
||
{#each data.loadout.weapons as selection, index}{@const item=weapon(selection.itemId)}<article><small>{index ? 'SECONDARY' : 'PRIMARY'}</small><strong>{item?.name ?? 'EMPTY SLOT'}</strong><span>{item ? `${item.category} · ${item.damage_type} · T${selection.tier} · LEVEL ${selection.level}` : 'NOT CONFIGURED'}</span>{#if item}<div class="shared-item-facts"><b>{number.format(selection.damage)} DAMAGE</b><b>{item.rpm} RPM</b><b>{item.magazine} ROUNDS</b><b>{item.reload_speed}s RELOAD</b></div><em>{coreLine(selection)}{rollLine(selection.rolls) ? ` · ${rollLine(selection.rolls)}` : ''}</em>{/if}</article>{/each}
|
||
</section>
|
||
<section>
|
||
<header><span>GEAR</span><b>06 SLOTS</b></header>
|
||
{#each gearSlots as slot}{@const selection=data.loadout.gear[slot]}{@const item=gear(selection.itemId)}<article><small>{slot.toUpperCase()}</small><strong>{item?.name ?? 'EMPTY SLOT'}</strong><span>{item ? `${brand(selection.brandId)?.name ?? 'NO BRAND'} · T${item.tier} · LEVEL ${selection.level}` : 'NOT CONFIGURED'}</span>{#if item}<div class="shared-item-facts"><b>{number.format(selection.health)} HEALTH</b>{#if item.base_armor_min !== null}<b>{number.format(selection.armor)} ARMOR</b>{/if}</div><em>{coreLine(selection)}{rollLine(selection.rolls) ? ` · ${rollLine(selection.rolls)}` : ''}</em>{/if}</article>{/each}
|
||
</section>
|
||
<section>
|
||
<header><span>AGENT SYSTEMS</span><b>10 SLOTS</b></header>
|
||
{#each data.loadout.chips as selection, index}{@const item=chip(selection.setId)}<article><small>SKILL CHIP 0{index+1}</small><strong>{item?.name ?? 'EMPTY SLOT'}</strong><span>{item ? selection.quality : 'NOT CONFIGURED'}</span>{#if item}<em>{rollLine([selection.primary, ...selection.secondary]) || 'ATTRIBUTES NOT CONFIGURED'}</em>{/if}</article>{/each}
|
||
{#if protocol(data.loadout.protocol.itemId)}{@const protocolItem=protocol(data.loadout.protocol.itemId)}<article><small>OS PROTOCOL</small><strong>{protocolItem.name}</strong><span>LEVEL {data.loadout.protocol.level} · {data.loadout.protocol.talentUpgrades}/5 UPGRADES</span></article>{:else}<article><small>OS PROTOCOL</small><strong>EMPTY SLOT</strong><span>NOT CONFIGURED</span></article>{/if}
|
||
</section>
|
||
</div>
|
||
</main>
|