const form = document.getElementById('af-calculator'); const outputs = { shotDamage: document.querySelector('[data-output="shotDamage"]'), burstDps: document.querySelector('[data-output="burstDps"]'), sustainedDps: document.querySelector('[data-output="sustainedDps"]'), timeToEmpty: document.querySelector('[data-output="timeToEmpty"]'), magDamage: document.querySelector('[data-output="magDamage"]'), ttk: document.querySelector('[data-output="ttk"]') }; const resetButton = document.getElementById('af-reset'); const copyButton = document.getElementById('af-copy'); const numberOrNull = (value) => { const parsed = Number.parseFloat(value); return Number.isFinite(parsed) ? parsed : null; }; const formatValue = (value, suffix = '') => { if (value === null) { return '--'; } const formatted = Number.isFinite(value) ? value.toFixed(2) : '--'; return `${formatted}${suffix}`; }; const calculate = () => { const data = new FormData(form); const baseDamage = numberOrNull(data.get('baseDamage')); const projectiles = numberOrNull(data.get('projectiles')) ?? 1; const rpm = numberOrNull(data.get('rpm')); const magazine = numberOrNull(data.get('magazine')); const reload = numberOrNull(data.get('reload')); const targetHealth = numberOrNull(data.get('targetHealth')); if (!baseDamage || !rpm) { Object.values(outputs).forEach((node) => { node.textContent = '--'; }); return; } const shotDamage = baseDamage * projectiles; const shotsPerSecond = rpm / 60; const burstDps = shotDamage * shotsPerSecond; const magDamage = magazine ? shotDamage * magazine : null; const timeToEmpty = magazine ? magazine / shotsPerSecond : null; let sustainedDps = null; if (magDamage !== null && reload !== null && timeToEmpty !== null && (timeToEmpty + reload) > 0) { sustainedDps = magDamage / (timeToEmpty + reload); } let ttk = null; if (targetHealth && burstDps > 0) { ttk = targetHealth / burstDps; } outputs.shotDamage.textContent = formatValue(shotDamage); outputs.burstDps.textContent = formatValue(burstDps); outputs.sustainedDps.textContent = formatValue(sustainedDps); outputs.timeToEmpty.textContent = formatValue(timeToEmpty, 's'); outputs.magDamage.textContent = formatValue(magDamage); outputs.ttk.textContent = formatValue(ttk, 's'); }; form.addEventListener('input', calculate); resetButton.addEventListener('click', () => { form.reset(); calculate(); }); copyButton.addEventListener('click', async () => { const data = new FormData(form); const name = data.get('weaponName') || 'Unnamed loadout'; const payload = [ `Loadout: ${name}`, `Damage per shot: ${outputs.shotDamage.textContent}`, `Burst DPS: ${outputs.burstDps.textContent}`, `Sustained DPS: ${outputs.sustainedDps.textContent}`, `Time to empty: ${outputs.timeToEmpty.textContent}`, `Magazine damage: ${outputs.magDamage.textContent}`, `Time to eliminate: ${outputs.ttk.textContent}` ].join('\n'); try { await navigator.clipboard.writeText(payload); copyButton.textContent = 'Copied!'; setTimeout(() => { copyButton.textContent = 'Copy summary'; }, 2000); } catch (error) { console.error('Clipboard copy failed', error); copyButton.textContent = 'Copy failed'; setTimeout(() => { copyButton.textContent = 'Copy summary'; }, 2000); } }); calculate();