+
OS PROTOCOL QUALITY TYPE UNIQUE PRIMARY UNIQUE TALENT STATE
+ {#each data.protocols as protocol}
+
{protocol.name} LEVEL 1–50 · PRIMARY 2× MAX
{protocol.quality} {protocol.primary_attribute_type}{protocol.primaryEffect ? `${stat(protocol.primaryEffect.stat_attribute_id)?.name ?? 'UNKNOWN'} · ${format(protocol.primaryEffect.base_value, protocol.primaryEffect.stat_attribute_id)} → ${format(levelValue(protocol.primaryEffect.base_value, 50), protocol.primaryEffect.stat_attribute_id)}` : 'NOT CONFIGURED'} {protocol.talent?.name ?? 'NOT CONFIGURED'} ◆◆◆◆◆ 5 UPGRADES · 2× MAX
{Number(protocol.active) === 1 ? 'ACTIVE' : 'ARCHIVED'} edit(protocol)}>EDIT
+ {:else}
NO OS PROTOCOLS REGISTERED Configure shared secondary profiles, then add the first protocol.
{/each}
+
diff --git a/src/routes/admin/weapons/+page.svelte b/src/routes/admin/weapons/+page.svelte
index 92e3bb0..f937a06 100644
--- a/src/routes/admin/weapons/+page.svelte
+++ b/src/routes/admin/weapons/+page.svelte
@@ -3,10 +3,13 @@
import SearchableAssignment from '$lib/components/SearchableAssignment.svelte';
import RegistryPagination from '$lib/components/RegistryPagination.svelte';
import KpiFilters from '$lib/components/KpiFilters.svelte';
+ import GameIcon from '$lib/components/GameIcon.svelte';
+ import type { GameIconName } from '$lib/game-icons';
let { data, form } = $props();
const categories = ['Shotgun', 'Rifle', 'MMR', 'AR', 'LMG', 'SMG'];
const damageTypes = ['Blast', 'Pierce', 'Shredding'];
const ranges = [['damage', 'DMG', 'DAMAGE'], ['firepower', 'FP', 'FIREPOWER'], ['toughness', 'TGH', 'TOUGHNESS'], ['engineering', 'ENG', 'ENGINEERING']] as const;
+ const iconName = (value: string): GameIconName => value.toLowerCase() as GameIconName;
const blankTier = (tier: number) => ({ tier, damage_min: 0, damage_max: 0, firepower_min: 0, firepower_max: 0, toughness_min: 0, toughness_max: 0, engineering_min: 0, engineering_max: 0 });
const blankExoticTalent = () => ({ id: '', name: '', descriptionTemplate: '', triggerIds: [], effects: [{ statId: '', valueI: '', valueII: '' }] });
const blank = () => ({ id: '', name: '', category: 'AR', is_exotic: 0, native_tier: 0, damage_type: 'Pierce', rpm: 0, magazine: 0, reload_speed: 0, accuracy: 0, stability: 0, exotic_talent_id: '', exoticTalent: blankExoticTalent(), active: 1, tiers: [blankTier(0), blankTier(1), blankTier(2)], talent1Ids: [], talent2Ids: [], augmentEffects: [{ stat_attribute_id: '', value: 0, sort_order: 1 }], baseBonuses: [{ stat_attribute_id: '', value: 0, sort_order: 1 }] });
@@ -81,7 +84,7 @@
WEAPON CLASS FIXED ATTRIBUTES TIER RANGE TALENT POOLS AUGMENTS STATE
{#each data.weapons as weapon}
-
{weapon.name} {Number(weapon.is_exotic) ? 'EXOTIC · ' : ''}{weapon.damage_type.toUpperCase()} DAMAGE · {weapon.baseBonuses.length} BASE BONUSES
{weapon.category} · T{weapon.native_tier} {weapon.rpm} RPM · {weapon.magazine} MAG · {weapon.reload_speed} RELOAD {weapon.tiers.length}/3 CONFIGURED {weapon.talent1Ids.length} / {weapon.talent2Ids.length} `${statLabel(String(effect.stat_attribute_id))} +${effect.value}`).join(', ')}>{Number(weapon.is_exotic) ? '6' : '5'} SLOTS {Number(weapon.active) === 1 ? 'ACTIVE' : 'ARCHIVED'} edit(weapon)}>EDIT
+
{weapon.name} {Number(weapon.is_exotic) ? 'EXOTIC · ' : ''}{weapon.damage_type.toUpperCase()} DAMAGE · {weapon.baseBonuses.length} BASE BONUSES
{weapon.category} · T{weapon.native_tier} {weapon.rpm} RPM · {weapon.magazine} MAG · {weapon.reload_speed} RELOAD {weapon.tiers.length}/3 CONFIGURED {weapon.talent1Ids.length} / {weapon.talent2Ids.length} `${statLabel(String(effect.stat_attribute_id))} +${effect.value}`).join(', ')}>{Number(weapon.is_exotic) ? '6' : '5'} SLOTS {Number(weapon.active) === 1 ? 'ACTIVE' : 'ARCHIVED'} edit(weapon)}>EDIT
{:else}
NO WEAPONS REGISTERED Add active weapon talents and attributes, then create the first armament definition.
{/each}
diff --git a/src/routes/builder/+page.svelte b/src/routes/builder/+page.svelte
index a4120d9..0448c8d 100644
--- a/src/routes/builder/+page.svelte
+++ b/src/routes/builder/+page.svelte
@@ -1,5 +1,6 @@