您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add information from the wiki in the weapons infobox with bonus calculations
当前为
// ==UserScript== // @name Dead Frontier Tooltip Details // @author ils94 // @namespace http://tampermonkey.net/ // @version 1.6.2 // @description Add information from the wiki in the weapons infobox with bonus calculations // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=24 // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=25 // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=28* // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=35 // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=50 // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=59 // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=82* // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=84 // @match https://fairview.deadfrontier.com/onlinezombiemmo/DF3D/DF3D_InventoryPage.php?page=31* // @match https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=32* // @license MIT // @grant none // ==/UserScript== (function() { 'use strict'; let dpsData = {}; let userStats = { totalDamage: 0, attackSpeed: 0, meleeBonuses: 0, chainsawBonuses: 0, pistolBonuses: 0, rifleBonuses: 0, shotgunBonuses: 0, smgBonuses: 0, machineGunBonuses: 0, explosiveBonuses: 0 }; let isShiftPressed = false; let presets = JSON.parse(localStorage.getItem('deadFrontierPresets') || '{}'); let lastSelectedPreset = localStorage.getItem('deadFrontierLastPreset') || ''; let tooltipVisibility = JSON.parse(localStorage.getItem('deadFrontierTooltipVisibility') || '{}'); const defaultVisibility = { avgDPS: true, avgDPSTheoretical: true, criticalDPS: true, criticalDPSTheoretical: true, damagePerHit: true, criticalDamagePerHit: true, hitsPerSecond: true, hitsPerSecondTheoretical: true }; tooltipVisibility = { ...defaultVisibility, ...tooltipVisibility }; let isTooltipContainerVisible = localStorage.getItem('deadFrontierTooltipContainerVisible') !== 'false'; document.addEventListener('keydown', (e) => { if (e.key === 'Shift') { isShiftPressed = true; } }); document.addEventListener('keyup', (e) => { if (e.key === 'Shift') { isShiftPressed = false; } }); function loadSavedStats() { const saved = localStorage.getItem('deadFrontierUserStats'); if (saved) { try { const parsed = JSON.parse(saved); Object.assign(userStats, parsed); } catch (e) { console.error('[DPS] Failed to parse saved stats:', e); } } if (lastSelectedPreset && presets[lastSelectedPreset]) { loadPreset(lastSelectedPreset); } } function savePreset(name, stats, overwrite = false) { if (presets[name] && !overwrite) { const confirmOverwrite = confirm(`Preset "${name}" already exists. Do you want to overwrite it?`); if (!confirmOverwrite) return false; } presets[name] = { ...stats }; localStorage.setItem('deadFrontierPresets', JSON.stringify(presets)); updateAllPresetDropdowns(); return true; } function loadPreset(name, inputElements = []) { if (!presets[name]) return; Object.assign(userStats, presets[name]); inputElements.forEach(({ key, inputEl }) => { inputEl.value = userStats[key] || 0; }); localStorage.setItem('deadFrontierUserStats', JSON.stringify(userStats)); localStorage.setItem('deadFrontierLastPreset', name); lastSelectedPreset = name; updateAllPresetDropdowns(); const staticInfoboxPages = ['page=25', 'page=28', 'page=50', 'page=59', 'page=84', 'page=31', 'page=32']; if (staticInfoboxPages.some(page => window.location.href.includes(page))) { injectDPSIntoStaticBoxes(); } } function deletePreset(name) { if (!presets[name]) return; if (confirm(`Are you sure you want to delete preset "${name}"?`)) { delete presets[name]; localStorage.setItem('deadFrontierPresets', JSON.stringify(presets)); if (lastSelectedPreset === name) { lastSelectedPreset = ''; localStorage.removeItem('deadFrontierLastPreset'); } updateAllPresetDropdowns(); } } function updatePresetDropdown(dropdown) { dropdown.innerHTML = '<option value="">Select Preset</option>'; Object.keys(presets).forEach(name => { const option = document.createElement('option'); option.value = name; option.textContent = name; dropdown.appendChild(option); }); if (lastSelectedPreset && presets[lastSelectedPreset]) { dropdown.value = lastSelectedPreset; } } function updateAllPresetDropdowns() { const dropdowns = document.querySelectorAll('.presetDropdown'); dropdowns.forEach(dropdown => updatePresetDropdown(dropdown)); } function createPresetDropdown() { if (window.location.href === 'https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=25') { return; } const presetContainer = document.createElement('div'); presetContainer.style.position = 'fixed'; presetContainer.style.top = '50px'; presetContainer.style.left = '5px'; presetContainer.style.backgroundColor = '#1a1a1a'; presetContainer.style.padding = '10px'; presetContainer.style.border = '2px solid #00FF00'; presetContainer.style.borderRadius = '8px'; presetContainer.style.zIndex = '1000'; presetContainer.style.color = '#00FF00'; presetContainer.style.fontFamily = 'Arial, sans-serif'; presetContainer.style.fontSize = '14px'; presetContainer.style.boxShadow = '0 4px 8px rgba(0, 255, 0, 0.3)'; presetContainer.style.display = 'flex'; presetContainer.style.flexDirection = 'column'; presetContainer.style.gap = '8px'; const dropdownContainer = document.createElement('div'); dropdownContainer.style.display = 'flex'; dropdownContainer.style.alignItems = 'center'; dropdownContainer.style.gap = '8px'; const presetLabel = document.createElement('label'); presetLabel.textContent = 'Presets:'; presetLabel.style.fontSize = '12px'; dropdownContainer.appendChild(presetLabel); const presetDropdown = document.createElement('select'); presetDropdown.className = 'presetDropdown'; presetDropdown.style.width = '120px'; presetDropdown.style.backgroundColor = '#2a2a2a'; presetDropdown.style.color = '#00FF00'; presetDropdown.style.border = '1px solid #00FF00'; presetDropdown.style.borderRadius = '4px'; presetDropdown.style.padding = '4px'; presetDropdown.style.fontSize = '12px'; updatePresetDropdown(presetDropdown); dropdownContainer.appendChild(presetDropdown); presetContainer.appendChild(dropdownContainer); const toggleButton = document.createElement('button'); toggleButton.textContent = isTooltipContainerVisible ? 'Hide Options' : 'Show Options'; toggleButton.style.backgroundColor = '#00FF00'; toggleButton.style.color = '#000'; toggleButton.style.border = 'none'; toggleButton.style.borderRadius = '5px'; toggleButton.style.padding = '10px 10px'; toggleButton.style.cursor = 'pointer'; toggleButton.style.fontSize = '12px'; toggleButton.style.fontWeight = 'bold'; toggleButton.style.minHeight = '25px'; toggleButton.style.transition = 'background-color 0.2s'; toggleButton.style.alignCenter = 'flex-start'; toggleButton.addEventListener('mouseover', () => { toggleButton.style.backgroundColor = '#00CC00'; }); toggleButton.addEventListener('mouseout', () => { toggleButton.style.backgroundColor = '#00FF00'; }); toggleButton.addEventListener('click', () => { isTooltipContainerVisible = !isTooltipContainerVisible; const tooltipContainer = document.querySelector('.tooltipVisibilityContainer'); if (tooltipContainer) { tooltipContainer.style.display = isTooltipContainerVisible ? 'block' : 'none'; } toggleButton.textContent = isTooltipContainerVisible ? 'Hide Options' : 'Show Options'; localStorage.setItem('deadFrontierTooltipContainerVisible', isTooltipContainerVisible); }); presetContainer.appendChild(toggleButton); presetDropdown.addEventListener('change', () => { const selected = presetDropdown.value; if (selected) { loadPreset(selected); } }); document.body.appendChild(presetContainer); } function createTooltipVisibilityContainer() { const container = document.createElement('div'); container.className = 'tooltipVisibilityContainer'; container.style.position = 'fixed'; container.style.top = '140px'; container.style.left = '5px'; container.style.backgroundColor = '#1a1a1a'; container.style.padding = '15px'; container.style.border = '2px solid #00FF00'; container.style.borderRadius = '8px'; container.style.zIndex = '1000'; container.style.color = '#00FF00'; container.style.fontFamily = 'Arial, sans-serif'; container.style.fontSize = '14px'; container.style.boxShadow = '0 4px 8px rgba(0, 255, 0, 0.3)'; container.style.width = '250px'; if (window.location.href === 'https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=25') { container.style.display = 'block'; } else { container.style.display = isTooltipContainerVisible ? 'block' : 'none'; } const title = document.createElement('div'); title.textContent = 'Tooltip Display Options'; title.style.fontSize = '16px'; title.style.fontWeight = 'bold'; title.style.textAlign = 'center'; title.style.marginBottom = '12px'; container.appendChild(title); const checkboxes = [{ label: 'Avg. DPS', key: 'avgDPS' }, { label: 'Avg. DPS Theoretical', key: 'avgDPSTheoretical' }, { label: 'Critical/AoE DPS', key: 'criticalDPS' }, { label: 'Critical/AoE DPS Theoretical', key: 'criticalDPSTheoretical' }, { label: 'Damage per Hit', key: 'damagePerHit' }, { label: 'Critical/AoE Damage per Hit', key: 'criticalDamagePerHit' }, { label: 'Hit(s) per Second', key: 'hitsPerSecond' }, { label: 'Hit(s) per Second Theoretical', key: 'hitsPerSecondTheoretical' } ]; checkboxes.forEach(({ label, key }) => { const labelEl = document.createElement('label'); labelEl.style.display = 'flex'; labelEl.style.alignItems = 'center'; labelEl.style.marginBottom = '8px'; labelEl.style.fontSize = '12px'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.checked = tooltipVisibility[key]; checkbox.style.marginRight = '8px'; checkbox.style.cursor = 'pointer'; checkbox.addEventListener('change', () => { tooltipVisibility[key] = checkbox.checked; localStorage.setItem('deadFrontierTooltipVisibility', JSON.stringify(tooltipVisibility)); const staticInfoboxPages = ['page=25', 'page=28', 'page=50', 'page=59', 'page=84', 'page=31', 'page=32']; if (staticInfoboxPages.some(page => window.location.href.includes(page))) { injectDPSIntoStaticBoxes(); } }); labelEl.appendChild(checkbox); labelEl.appendChild(document.createTextNode(label)); container.appendChild(labelEl); }); document.body.appendChild(container); } function createInputContainer() { if (window.location.href !== 'https://fairview.deadfrontier.com/onlinezombiemmo/index.php?page=25') { return; } const container = document.createElement('div'); container.style.position = 'fixed'; container.style.bottom = '10px'; container.style.left = '10px'; container.style.backgroundColor = '#1a1a1a'; container.style.padding = '15px'; container.style.border = '2px solid #00FF00'; container.style.borderRadius = '8px'; container.style.zIndex = '1000'; container.style.color = '#00FF00'; container.style.fontFamily = 'Arial, sans-serif'; container.style.fontSize = '14px'; container.style.boxShadow = '0 4px 8px rgba(0, 255, 0, 0.3)'; container.style.width = '250px'; const title = document.createElement('div'); title.textContent = 'Implant and Weapons Bonuses'; title.style.fontSize = '16px'; title.style.fontWeight = 'bold'; title.style.textAlign = 'center'; title.style.marginBottom = '12px'; container.appendChild(title); const presetContainer = document.createElement('div'); presetContainer.style.display = 'flex'; presetContainer.style.alignItems = 'center'; presetContainer.style.marginBottom = '12px'; const presetLabel = document.createElement('label'); presetLabel.textContent = 'Presets:'; presetLabel.style.fontSize = '12px'; presetContainer.appendChild(presetLabel); const presetDropdown = document.createElement('select'); presetDropdown.className = 'presetDropdown'; presetDropdown.style.marginLeft = 'auto'; presetDropdown.style.width = '120px'; presetDropdown.style.backgroundColor = '#2a2a2a'; presetDropdown.style.color = '#00FF00'; presetDropdown.style.border = '1px solid #00FF00'; presetDropdown.style.borderRadius = '4px'; presetDropdown.style.padding = '4px'; presetDropdown.style.fontSize = '12px'; updatePresetDropdown(presetDropdown); presetContainer.appendChild(presetDropdown); const deletePresetButton = document.createElement('button'); deletePresetButton.textContent = 'X'; deletePresetButton.style.marginLeft = '5px'; deletePresetButton.style.backgroundColor = '#FF3333'; deletePresetButton.style.color = '#000'; deletePresetButton.style.border = 'none'; deletePresetButton.style.borderRadius = '3px'; deletePresetButton.style.padding = '5px 5px'; deletePresetButton.style.cursor = 'pointer'; deletePresetButton.style.fontSize = '20px'; deletePresetButton.style.minWidth = '25px'; deletePresetButton.style.minHeight = '12px'; deletePresetButton.addEventListener('click', () => { const selected = presetDropdown.value; if (selected) { deletePreset(selected); } }); presetContainer.appendChild(deletePresetButton); container.appendChild(presetContainer); const inputs = [{ label: 'Total Inflicted Damage:', key: 'totalDamage' }, { label: 'Total Attack Speed:', key: 'attackSpeed' }, { label: 'Melee Bonuses:', key: 'meleeBonuses' }, { label: 'Chainsaw Bonuses:', key: 'chainsawBonuses' }, { label: 'Pistol Bonuses:', key: 'pistolBonuses' }, { label: 'Rifle Bonuses:', key: 'rifleBonuses' }, { label: 'Shotgun Bonuses:', key: 'shotgunBonuses' }, { label: 'SMG Bonuses:', key: 'smgBonuses' }, { label: 'Machine Gun Bonuses:', key: 'machineGunBonuses' }, { label: 'Explosive Bonuses:', key: 'explosiveBonuses' } ]; const inputElements = []; inputs.forEach(input => { const label = document.createElement('label'); label.textContent = input.label; label.style.display = 'flex'; label.style.alignItems = 'center'; label.style.marginBottom = '8px'; label.style.fontSize = '12px'; const inputEl = document.createElement('input'); inputEl.type = 'text'; inputEl.inputMode = 'decimal'; inputEl.value = userStats[input.key]; inputEl.style.width = '80px'; inputEl.style.marginLeft = 'auto'; inputEl.style.backgroundColor = '#2a2a2a'; inputEl.style.color = '#00FF00'; inputEl.style.border = '1px solid #00FF00'; inputEl.style.borderRadius = '4px'; inputEl.style.padding = '4px'; inputEl.style.fontSize = '12px'; inputEl.style.outline = 'none'; inputEl.addEventListener('input', () => { let cleaned = inputEl.value.replace(/[^0-9.]/g, ''); const parts = cleaned.split('.'); if (parts.length > 2) { cleaned = parts[0] + '.' + parts.slice(1).join(''); } inputEl.value = cleaned; userStats[input.key] = parseFloat(inputEl.value) || 0; }); label.appendChild(inputEl); container.appendChild(label); inputElements.push({ key: input.key, inputEl }); }); presetDropdown.addEventListener('change', () => { const selected = presetDropdown.value; if (selected) { loadPreset(selected, inputElements); } }); const saveButton = document.createElement('button'); saveButton.textContent = 'Save'; saveButton.style.display = 'block'; saveButton.style.margin = '10px auto 0'; saveButton.style.backgroundColor = '#00FF00'; saveButton.style.color = '#000'; saveButton.style.border = 'none'; saveButton.style.borderRadius = '5px'; saveButton.style.padding = '15px 15px'; saveButton.style.cursor = 'pointer'; saveButton.style.fontSize = '16px'; saveButton.style.fontWeight = 'bold'; saveButton.style.transition = 'background-color 0.2s, transform 0.1s'; saveButton.style.minWidth = '50px'; saveButton.style.minHeight = '25px'; saveButton.addEventListener('mouseover', () => { saveButton.style.backgroundColor = '#00CC00'; }); saveButton.addEventListener('mouseout', () => { saveButton.style.backgroundColor = '#00FF00'; }); saveButton.addEventListener('click', () => { const presetName = prompt('Enter a name for the preset:'); if (presetName && presetName.trim()) { if (savePreset(presetName.trim(), userStats)) { localStorage.setItem('deadFrontierUserStats', JSON.stringify(userStats)); localStorage.setItem('deadFrontierLastPreset', presetName.trim()); lastSelectedPreset = presetName.trim(); injectDPSIntoStaticBoxes(); console.log('[DPS] User stats saved:', userStats); } } }); container.appendChild(saveButton); document.body.appendChild(container); } function loadDPS() { if (typeof window.weaponData === 'undefined') { console.error('[DPS] External weapon data not loaded'); return; } window.weaponData.weapons.forEach(weapon => { const key = weapon.name.toLowerCase(); dpsData[key] = { name: weapon.name, category: weapon.category, dps: weapon.stats.DPS || {}, dph: weapon.stats.DPH || {}, hps: weapon.stats.HPS || {} }; }); console.log('[DPS] Loaded', Object.keys(dpsData).length, 'entries from external JSON'); loadSavedStats(); createPresetDropdown(); createTooltipVisibilityContainer(); createInputContainer(); startWatcher(); injectDPSIntoStaticBoxes(); } function parseSumExpression(expr) { if (!expr || typeof expr !== 'string') return { terms: [], total: null, base: null, multiplier: null }; // Handle # x # = # format (e.g., "10 x 4 = 40") let match = expr.match(/([\d.]+)\s*x\s*([\d.]+)\s*=\s*([\d.]+)/); if (match) { const base = parseFloat(match[1]); const multiplier = parseFloat(match[2]); const total = parseFloat(match[3]); return { terms: [base], total, base, multiplier }; } // Handle # + # + # = # format match = expr.match(/([\d.]+)\s*\+\s*([\d.]+)\s*\+\s*([\d.]+)\s*=\s*([\d.]+)/); if (match) { const terms = [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])]; const total = parseFloat(match[4]); return { terms, total, base: null, multiplier: null }; } // Handle # + # = # format match = expr.match(/([\d.]+)\s*\+\s*([\d.]+)\s*=\s*([\d.]+)/); if (match) { const terms = [parseFloat(match[1]), parseFloat(match[2])]; const total = parseFloat(match[3]); return { terms, total, base: null, multiplier: null }; } // Handle # + # format match = expr.match(/([\d.]+)\s*\+\s*([\d.]+)/); if (match) { const terms = [parseFloat(match[1]), parseFloat(match[2])]; const total = terms.reduce((sum, term) => sum + term, 0); return { terms, total, base: null, multiplier: null }; } // Handle single number const singleNumber = parseFloat(expr); if (!isNaN(singleNumber)) { return { terms: [singleNumber], total: singleNumber, base: null, multiplier: null }; } return { terms: [], total: null, base: null, multiplier: null }; } function calculateBonuses(entry) { const category = entry.category.toLowerCase(); let masteryBonus = 0; if (category.includes('melee')) masteryBonus = userStats.meleeBonuses; if (category.includes('chainsaw')) masteryBonus = userStats.chainsawBonuses; if (category.includes('pistol')) masteryBonus = userStats.pistolBonuses; if (category.includes('rifle')) masteryBonus = userStats.rifleBonuses; if (category.includes('shotgun')) masteryBonus = userStats.shotgunBonuses; if (category.includes('smg')) masteryBonus = userStats.smgBonuses; if (category.includes('machine gun')) masteryBonus = userStats.machineGunBonuses; if (category.includes('grenade launchers') || category.includes('flamethrowers')) masteryBonus = userStats.explosiveBonuses; const damageMultiplier = 1 + (userStats.totalDamage + masteryBonus) / 100; const speedMultiplier = 1 + userStats.attackSpeed / 100; const dphTotalParsed = parseSumExpression(entry.dph.total); const dphCriticalParsed = parseSumExpression(entry.dph.critical); let dphTotal; let dphTotalTerms = dphTotalParsed.terms.map(term => (term * damageMultiplier).toFixed(2)); if (dphTotalParsed.base !== null && dphTotalParsed.multiplier !== null) { const adjustedBase = (dphTotalParsed.base * damageMultiplier).toFixed(2); dphTotal = (dphTotalParsed.base * damageMultiplier * dphTotalParsed.multiplier).toFixed(2); dphTotalTerms = [adjustedBase]; } else { dphTotal = dphTotalParsed.total !== null ? (dphTotalParsed.total * damageMultiplier).toFixed(2) : 'N/A'; } return { dps: { real: entry.dps.real ? (entry.dps.real * damageMultiplier * speedMultiplier).toFixed(2) : 'N/A', theoretical: entry.dps.theoretical ? (entry.dps.theoretical * damageMultiplier * speedMultiplier).toFixed(2) : 'N/A', critical: entry.dps.critical ? (entry.dps.critical * damageMultiplier * speedMultiplier).toFixed(2) : 'N/A', theoretical_critical: entry.dps.theoretical_critical ? (entry.dps.theoretical_critical * damageMultiplier * speedMultiplier).toFixed(2) : 'N/A' }, dph: { total: dphTotal, critical: dphCriticalParsed.total !== null ? (dphCriticalParsed.total * damageMultiplier).toFixed(2) : 'N/A', totalTerms: dphTotalTerms, criticalTerms: dphCriticalParsed.terms.map(term => (term * damageMultiplier).toFixed(2)), base: dphTotalParsed.base, multiplier: dphTotalParsed.multiplier }, hps: { real: entry.hps.real ? (entry.hps.real * speedMultiplier).toFixed(2) : 'N/A', theoretical: entry.hps.theoretical ? (entry.hps.theoretical * speedMultiplier).toFixed(2) : 'N/A' } }; } function generateStatsHTML(entry) { const bonuses = calculateBonuses(entry); const dphTotalParsed = parseSumExpression(entry.dph.total); const dphDisplay = dphTotalParsed.base !== null && dphTotalParsed.multiplier !== null ? `${dphTotalParsed.base} x ${dphTotalParsed.multiplier} = ${dphTotalParsed.total}` : (dphTotalParsed.terms.length > 1 ? entry.dph.total : (entry.dph.total || 'N/A')); const dphBonusDisplay = dphTotalParsed.base !== null && dphTotalParsed.multiplier !== null ? `${(dphTotalParsed.base * (1 + (userStats.totalDamage + (userStats[entry.category.toLowerCase().replace(' ', '') + 'Bonuses'] || 0)) / 100)).toFixed(2)} x ${dphTotalParsed.multiplier} = ${bonuses.dph.total}` : (dphTotalParsed.terms.length > 1 ? `${bonuses.dph.totalTerms.join(' + ')} = ${bonuses.dph.total}` : bonuses.dph.total); const dphCriticalParsed = parseSumExpression(entry.dph.critical); const dphCriticalDisplay = dphCriticalParsed.terms.length > 1 ? entry.dph.critical : (entry.dph.critical || 'N/A'); const dphCriticalBonusDisplay = dphCriticalParsed.terms.length > 1 ? `${bonuses.dph.criticalTerms.join(' + ')} = ${bonuses.dph.critical}` : bonuses.dph.critical; const isExplosive = entry.category === 'Grenade Launchers' || entry.category === 'Flamethrowers'; const isGrenadeLauncher = entry.category === 'Grenade Launchers'; const dpsCriticalLabel = isExplosive ? 'Avg. DPS AoE' : 'Avg. DPS Critical'; const dpsCriticalTheoreticalLabel = isExplosive ? 'Avg. DPS AoE Theoretical' : 'Avg. DPS Critical Theoretical'; const dphCriticalLabel = isExplosive ? 'Damage per AoE' : 'Damage per Hit Critical'; const baseStats = []; const bonusStats = []; if (tooltipVisibility.avgDPS) { baseStats.push(`Avg. DPS: ${entry.dps.real || 'N/A'}`); bonusStats.push(`Avg. DPS: ${bonuses.dps.real}`); } if (tooltipVisibility.avgDPSTheoretical) { baseStats.push(`Avg. DPS Theoretical: ${entry.dps.theoretical || 'N/A'}`); bonusStats.push(`Avg. DPS Theoretical: ${bonuses.dps.theoretical}`); } if (tooltipVisibility.criticalDPS) { baseStats.push(`${dpsCriticalLabel}: ${entry.dps.critical || 'N/A'}`); bonusStats.push(`${dpsCriticalLabel}: ${bonuses.dps.critical}`); } if (tooltipVisibility.criticalDPSTheoretical) { baseStats.push(`${dpsCriticalTheoreticalLabel}: ${entry.dps.theoretical_critical || 'N/A'}`); bonusStats.push(`${dpsCriticalTheoreticalLabel}: ${bonuses.dps.theoretical_critical}`); } if (isGrenadeLauncher && tooltipVisibility.damagePerHit) { const targetMultipliers = [{ count: 1, multiplier: 3.5 }, { count: 2, multiplier: 2.0 }, { count: 3, multiplier: 1.5 }, { count: 4, multiplier: 1.25 }, { count: 5, multiplier: 1.0 } ]; const baseDamages = targetMultipliers.map(({ count, multiplier }) => { const baseDamage = (dphTotalParsed.total * multiplier).toFixed(2); return `${count}: ${baseDamage}`; }).join('<br>'); const bonusDamages = targetMultipliers.map(({ count, multiplier }) => { const bonusDamage = (bonuses.dph.total * multiplier).toFixed(2); return `${count}: ${bonusDamage}`; }).join('<br>'); baseStats.push(`Damage per Target:<br>${baseDamages}`); bonusStats.push(`Damage per Target:<br>${bonusDamages}`); } else { if (tooltipVisibility.damagePerHit) { baseStats.push(`Damage per Hit: ${dphDisplay}`); bonusStats.push(`Damage per Hit: ${dphBonusDisplay}`); } if (tooltipVisibility.criticalDamagePerHit && !isGrenadeLauncher) { baseStats.push(`${dphCriticalLabel}: ${dphCriticalDisplay}`); bonusStats.push(`${dphCriticalLabel}: ${dphCriticalBonusDisplay}`); } } if (tooltipVisibility.hitsPerSecond) { baseStats.push(`Hit(s) per Second: ${entry.hps.real || 'N/A'}`); bonusStats.push(`Hit(s) per Second: ${bonuses.hps.real}`); } if (tooltipVisibility.hitsPerSecondTheoretical) { baseStats.push(`Hit(s) per Second Theoretical: ${entry.hps.theoretical || 'N/A'}`); bonusStats.push(`Hit(s) per Second Theoretical: ${bonuses.hps.theoretical}`); } const statsHTML = []; if (baseStats.length > 0) { statsHTML.push('<strong>Base Stats:</strong>', ...baseStats); } if (bonusStats.length > 0) { statsHTML.push('<strong>With Bonuses:</strong>', ...bonusStats); } return statsHTML.map(line => { if (line.startsWith('<strong>')) { return `<br>${line}<br>`; } return line; }).join('<br>'); } function startWatcher() { let tooltipWindow = null; setInterval(() => { const box = document.getElementById('infoBox'); if (!box || box.style.visibility === 'hidden') { if (tooltipWindow) { tooltipWindow.remove(); tooltipWindow = null; } return; } if (!isShiftPressed) { if (tooltipWindow) { tooltipWindow.remove(); tooltipWindow = null; } return; } const nameEl = box.querySelector('.itemName'); if (!nameEl) { if (tooltipWindow) { tooltipWindow.remove(); tooltipWindow = null; } return; } const weapon = nameEl.textContent.trim(); const key = weapon.toLowerCase(); const entry = dpsData[key]; if (!entry) { console.log(`[DPS] ✗ ${weapon} (hover, no exact match)`); if (tooltipWindow) { tooltipWindow.remove(); tooltipWindow = null; } return; } if (!tooltipWindow) { tooltipWindow = document.createElement('div'); tooltipWindow.className = 'dpsTooltip'; tooltipWindow.style.position = 'absolute'; tooltipWindow.style.backgroundColor = '#1a1a1a'; tooltipWindow.style.border = '1px solid #00FF00'; tooltipWindow.style.padding = '10px'; tooltipWindow.style.color = '#00FF00'; tooltipWindow.style.fontSize = '12px'; tooltipWindow.style.zIndex = '1001'; tooltipWindow.style.borderRadius = '4px'; tooltipWindow.style.boxShadow = '0 2px 4px rgba(0, 255, 0, 0.3)'; document.body.appendChild(tooltipWindow); } const boxRect = box.getBoundingClientRect(); tooltipWindow.style.left = `${boxRect.right + 10}px`; tooltipWindow.style.top = `${boxRect.top}px`; tooltipWindow.innerHTML = generateStatsHTML(entry); console.log(`[DPS] ✔ ${weapon} (hover)`); }, 100); } function injectDPSIntoStaticBoxes() { const staticBoxes = document.querySelectorAll('.itemName'); staticBoxes.forEach(nameEl => { const parent = nameEl.parentElement; if (!parent) return; const existing = parent.querySelector('.dpsInjected'); if (existing) existing.remove(); const weapon = nameEl.textContent.trim(); const key = weapon.toLowerCase(); const entry = dpsData[key]; if (!entry) { console.log(`[DPS] ✗ ${weapon} (static, no exact match)`); return; } const statsDiv = document.createElement('div'); statsDiv.className = 'itemData dpsInjected'; statsDiv.style.color = '#00FF00'; statsDiv.style.fontSize = '12px'; statsDiv.innerHTML = generateStatsHTML(entry); parent.appendChild(statsDiv); console.log(`[DPS] ✔ ${weapon} (static)`); }); } function loadExternalScript() { const script = document.createElement('script'); script.src = 'dead_frontier_weapons.js'; script.onload = () => { console.log('[DPS] External JSON script loaded'); loadDPS(); }; script.onerror = () => { console.error('[DPS] Failed to load external JSON script'); }; document.head.appendChild(script); } loadExternalScript(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址