您需要先安装一个扩展,例如 篡改猴、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.4.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; // Track Shift key state document.addEventListener('keydown', (e) => { if (e.key === 'Shift') { isShiftPressed = true; } }); document.addEventListener('keyup', (e) => { if (e.key === 'Shift') { isShiftPressed = false; } }); // Load saved stats from localStorage 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); } } } function createInputContainer() { // Only create container if on page=25 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'; // Add the title 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 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' } ]; 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); }); 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', () => { localStorage.setItem('deadFrontierUserStats', JSON.stringify(userStats)); 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(); // Load saved stats for all pages createInputContainer(); // Only creates container on page=25 startWatcher(); injectDPSIntoStaticBoxes(); } function parseSumExpression(expr) { if (!expr || typeof expr !== 'string') return { terms: [], total: null }; // Match three-term sums like "96 + 32 + 32 = 160" or "12.12 + 28.28 + 60.6 = 101" let 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 }; } // Match two-term sums with equals like "100 + 50 = 150" 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 }; } // Match two-term sums without equals like "25 + 25" 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 }; } // Fallback: try to parse as a single number (e.g., "50") const singleNumber = parseFloat(expr); if (!isNaN(singleNumber)) { return { terms: [singleNumber], total: singleNumber }; } return { terms: [], total: 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; // Parse DPH total and critical const dphTotalParsed = parseSumExpression(entry.dph.total); const dphCriticalParsed = parseSumExpression(entry.dph.critical); 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: dphTotalParsed.total !== null ? (dphTotalParsed.total * damageMultiplier).toFixed(2) : 'N/A', critical: dphCriticalParsed.total !== null ? (dphCriticalParsed.total * damageMultiplier).toFixed(2) : 'N/A', totalTerms: dphTotalParsed.terms.map(term => (term * damageMultiplier).toFixed(2)), criticalTerms: dphCriticalParsed.terms.map(term => (term * damageMultiplier).toFixed(2)) }, 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); // Format DPH display based on whether it's a multiplier format or sum format const dphTotalParsed = parseSumExpression(entry.dph.total); const dphDisplay = entry.dph.base && entry.dph.multiplier ? `${entry.dph.base} x ${entry.dph.multiplier} = ${entry.dph.total}` : (dphTotalParsed.terms.length > 1 ? entry.dph.total : (entry.dph.total || 'N/A')); const dphBonusDisplay = entry.dph.base && entry.dph.multiplier ? `${(entry.dph.base * (1 + (userStats.totalDamage + (userStats[entry.category.toLowerCase().replace(' ', '') + 'Bonuses'] || 0)) / 100)).toFixed(2)} x ${entry.dph.multiplier} = ${bonuses.dph.total}` : (dphTotalParsed.terms.length > 1 ? `${bonuses.dph.totalTerms.join(' + ')} = ${bonuses.dph.total}` : bonuses.dph.total); // Format critical DPH display 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; // Determine labels based on weapon category const isExplosive = entry.category === 'Grenade Launchers' || entry.category === 'Flamethrowers'; 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 Hit AoE' : 'Damage per Hit Critical'; let statsHTML = ` <strong>Base Stats:</strong><br> <br>Avg. DPS: ${entry.dps.real || 'N/A'}<br> Avg. DPS Theoretical: ${entry.dps.theoretical || 'N/A'}<br> ${dpsCriticalLabel}: ${entry.dps.critical || 'N/A'}<br> ${dpsCriticalTheoreticalLabel}: ${entry.dps.theoretical_critical || 'N/A'}<br> Damage per Hit: ${dphDisplay}<br> ${dphCriticalLabel}: ${dphCriticalDisplay}<br> Hit(s) per Second: ${entry.hps.real || 'N/A'}<br> Hit(s) per Second Theoretical: ${entry.hps.theoretical || 'N/A'}<br> <br><strong>With Bonuses:</strong><br> <br>Avg. DPS: ${bonuses.dps.real}<br> Avg. DPS Theoretical: ${bonuses.dps.theoretical}<br> ${dpsCriticalLabel}: ${bonuses.dps.critical}<br> ${dpsCriticalTheoreticalLabel}: ${bonuses.dps.theoretical_critical}<br> Damage per Hit: ${dphBonusDisplay}<br> ${dphCriticalLabel}: ${dphCriticalBonusDisplay}<br> Hit(s) per Second: ${bonuses.hps.real}<br> Hit(s) per Second Theoretical: ${bonuses.hps.theoretical}<br> `; return statsHTML; } 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或关注我们的公众号极客氢云获取最新地址