// ==UserScript== // @name [Pokechill] 伤害计算器 // @namespace https://play-pokechill.github.io/ // @version 1.2 // @description 计算伤害 // @author 人民当家做主 (修正 by GPT) // @license MIT // @icon https://play-pokechill.github.io/img/icons/icon.png // @match https://play-pokechill.github.io/* // @match https://g1tyx.github.io/play-pokechill/* // @grant none // ==/UserScript== (function () { 'use strict'; // 等待游戏核心对象加载 const MAX_WAIT = 15000; const CHECK_INTERVAL = 300; let waited = 0; const interval = setInterval(() => { if (document.readyState === 'complete' && typeof pkmn !== 'undefined' && typeof move !== 'undefined' && typeof ability !== 'undefined' && typeof item !== 'undefined' && typeof format !== 'undefined' && typeof openMenu !== 'undefined') { clearInterval(interval); initCalculator(); } else { waited += CHECK_INTERVAL; if (waited >= MAX_WAIT) { clearInterval(interval); console.warn('[伤害计算器] 游戏数据加载超时'); initCalculator(); } } }, CHECK_INTERVAL); // 默认出招时间(来自游戏) const defaultPlayerMoveTimer = typeof window.defaultPlayerMoveTimer !== 'undefined' ? window.defaultPlayerMoveTimer : 2; function initCalculator() { console.log('[伤害计算器] 初始化...'); // 汉化字典 const DICT = window.EN_CN_DICT || {}; // 获取中文名称(与第一个脚本完全一致) function getChinese(englishId) { if (!englishId) return ''; let formatted = format(englishId); return DICT[englishId] || DICT[formatted] || formatted; } // 根据显示名称查找ID(用于输入匹配) function findIdByDisplayName(category, displayName) { if (!displayName) return null; let list; if (category === 'pkmn') list = Object.keys(pkmn).filter(id => !pkmn[id].hidden); else if (category === 'move') list = Object.keys(move); else if (category === 'ability') list = Object.keys(ability); else if (category === 'item') list = Object.keys(item).filter(id => item[id].type === 'held' && !item[id].hidden); else return null; const lowerDisplay = displayName.toLowerCase(); for (const id of list) { const chinese = getChinese(id).toLowerCase(); if (chinese === lowerDisplay || id.toLowerCase() === lowerDisplay) return id; } return null; } // 创建带中文选项的datalist(与第一个脚本一致) function createDataListWithChinese(id, items, inputElement) { const datalist = document.createElement('datalist'); datalist.id = id; for (const key of items) { const option = document.createElement('option'); option.value = getChinese(key); option.dataset.id = key; datalist.appendChild(option); } document.body.appendChild(datalist); inputElement.addEventListener('input', function () { const val = this.value; const options = datalist.options; for (let opt of options) { if (opt.value === val) { this.dataset.id = opt.dataset.id; return; } } const foundId = findIdByDisplayName(id.replace('-list', ''), val); // 注意ID后缀可能不同,这里假设为 '-list' if (foundId) { this.dataset.id = foundId; this.value = getChinese(foundId); } else { this.dataset.id = ''; } }); inputElement.addEventListener('change', function () { if (this.dataset.id) return; const val = this.value; const options = datalist.options; for (let opt of options) { if (opt.value === val) { this.dataset.id = opt.dataset.id; return; } } }); } // 修正的能力等级倍率:防御方正等级应增加防御(伤害减少) function getStageMult(val, isDef = false) { if (val === 0) return 1; if (val === 1) return isDef ? 1.5 : 1.5; if (val === 2) return isDef ? 2 : 2; if (val === -1) return isDef ? 2/3 : 2/3; if (val === -2) return isDef ? 0.5 : 0.5; return 1; } // 道具倍率直接调用 power 方法 function getItemMultiplier(itemId, level) { const it = item[itemId]; if (!it || typeof it.power !== 'function') return 1; return it.power(level); } // 获取隐藏特性 function getHiddenAbility(pkmnId) { const pk = pkmn[pkmnId]; if (pk && pk.hiddenAbility) { return pk.hiddenAbility.id; } return null; } // 天气倍率(与游戏一致) function getWeatherTypeMult(weather, moveType) { if (!weather) return 1; switch (weather) { case 'sunny': if (moveType === 'fire') return 1.5; if (moveType === 'water') return 0.5; break; case 'rainy': if (moveType === 'water') return 1.5; if (moveType === 'fire') return 0.5; break; case 'sandstorm': if (moveType === 'rock' || moveType === 'ground') return 1.5; break; case 'hail': if (moveType === 'ice') return 1.5; break; case 'electricTerrain': if (moveType === 'electric' || moveType === 'steel') return 1.5; break; case 'grassyTerrain': if (moveType === 'grass' || moveType === 'bug') return 1.5; break; case 'mistyTerrain': if (moveType === 'fairy' || moveType === 'psychic') return 1.5; break; case 'foggy': if (moveType === 'ghost' || moveType === 'dark') return 1.5; break; default: return 1; } return 1; } // 属性克制表(严格按照Pokechill:克制1.5倍,抵抗0.5倍,免疫0) const typeChartMap = { normal: { rock: 0.5, steel: 0.5, ghost: 0 }, fire: { fire: 0.5, water: 0.5, grass: 1.5, ice: 1.5, bug: 1.5, rock: 0.5, dragon: 0.5, steel: 1.5 }, water: { fire: 1.5, water: 0.5, grass: 0.5, ground: 1.5, rock: 1.5, dragon: 0.5 }, electric: { water: 1.5, electric: 0.5, grass: 0.5, ground: 0, flying: 1.5, dragon: 0.5 }, grass: { fire: 0.5, water: 1.5, grass: 0.5, poison: 0.5, ground: 1.5, flying: 0.5, bug: 0.5, rock: 1.5, dragon: 0.5, steel: 0.5 }, ice: { fire: 0.5, water: 0.5, grass: 1.5, ice: 0.5, ground: 1.5, flying: 1.5, dragon: 1.5, steel: 0.5 }, fighting: { normal: 1.5, ice: 1.5, poison: 0.5, flying: 0.5, psychic: 0.5, bug: 0.5, rock: 1.5, ghost: 0, dark: 1.5, fairy: 0.5, steel: 1.5 }, poison: { grass: 1.5, poison: 0.5, ground: 0.5, rock: 0.5, ghost: 0.5, steel: 0, fairy: 1.5 }, ground: { fire: 1.5, electric: 1.5, grass: 0.5, poison: 1.5, flying: 0, bug: 0.5, rock: 1.5, steel: 1.5 }, flying: { electric: 0.5, grass: 1.5, fighting: 1.5, bug: 1.5, rock: 0.5, steel: 0.5 }, psychic: { fighting: 1.5, poison: 1.5, psychic: 0.5, dark: 0, steel: 0.5, fairy: 0.5 }, bug: { fire: 0.5, grass: 1.5, fighting: 0.5, poison: 0.5, flying: 0.5, psychic: 1.5, ghost: 0.5, dark: 1.5, steel: 0.5, fairy: 0.5 }, rock: { fire: 1.5, ice: 1.5, fighting: 0.5, ground: 0.5, flying: 1.5, bug: 1.5, steel: 0.5 }, ghost: { normal: 0, psychic: 1.5, ghost: 1.5, dark: 0.5 }, dragon: { dragon: 1.5, steel: 0.5, fairy: 0 }, dark: { fighting: 0.5, psychic: 1.5, ghost: 1.5, dark: 0.5, fairy: 0.5 }, steel: { fire: 0.5, water: 0.5, electric: 0.5, ice: 1.5, rock: 1.5, steel: 0.5, fairy: 1.5 }, fairy: { fire: 0.5, fighting: 1.5, poison: 0.5, dragon: 1.5, dark: 1.5, steel: 0.5 }, stellar: { stellar: 0 } }; // 属性克制计算(加入免疫覆盖、冰冻干燥、特性影响) function calculateTypeEffectiveness(moveType, targetTypes, defenderAbility, context) { let effectiveness = 1; for (const targetType of targetTypes) { const typeChart = typeChartMap[moveType]; if (typeChart && typeChart[targetType] !== undefined) { effectiveness *= typeChart[targetType]; } } // 冰冻干燥对水系额外×1.5(游戏内为1.5) if (moveType === 'ice' && targetTypes.includes('water')) { effectiveness *= 1.5; } // 免疫覆盖(训练场或无怜悯) const noImmunities = context.area === 'training' || context.area === 'spiralingTower'; const fieldNoMercy = context.fieldEffect?.includes('noMercy'); if (effectiveness === 0 && (noImmunities || fieldNoMercy)) { // 重新计算,将0替换为0.5 effectiveness = 1; for (const targetType of targetTypes) { let eff = typeChartMap[moveType]?.[targetType] ?? 1; if (eff === 0) eff = 0.5; effectiveness *= eff; } } // 特性影响 if (defenderAbility) { if (defenderAbility.id === 'scrappy' && targetTypes.includes('ghost') && (moveType === 'fighting' || moveType === 'normal')) { effectiveness = 1; } if (defenderAbility.id === 'tintedLens' && effectiveness < 1 && effectiveness > 0) { effectiveness = 1; } if (defenderAbility.id === 'noGuard' && effectiveness === 0) { effectiveness = 1; } if (defenderAbility.id === 'thousandArms') { effectiveness = 1.5; } } // 反转场地(如果需要) if (context.fieldEffect?.includes('reverseField')) { const map = { 0.25: 2.25, 0.5: 1.5, 0.75: 1.25, 1.25: 0.75, 1.5: 0.5, 2.25: 0.25 }; if (map[effectiveness] !== undefined) effectiveness = map[effectiveness]; } return effectiveness; } // 特性应用函数 function applyAbility(abilityObj, context) { if (!abilityObj) return { mult: 1, note: '' }; const { isPhysical, moveObj, attacker, defender, movePower, attackerHPPercent, defenderHPPercent, defenderHasStatus, faintedAllies, weather, isCritical } = context; const abId = abilityObj.id; let mult = 1; let note = ''; // ----- 攻击方增益 ----- if (abId === 'hugePower' || abId === 'purePower') { if (isPhysical) { mult = 2; note = '大力士/全力'; } } else if (abId === 'technician' && movePower <= 60) { mult = 1.5; note = '技术高手'; } else if (abId === 'ironFist' && moveObj.affectedBy?.includes('ironFist')) { mult = 1.5; note = '铁拳'; } else if (abId === 'strongJaw' && moveObj.affectedBy?.includes('strongJaw')) { mult = 2; note = '强壮之颚'; } else if (abId === 'toughClaws' && moveObj.affectedBy?.includes('toughClaws')) { mult = 2; note = '硬爪'; } else if (abId === 'sharpness' && moveObj.affectedBy?.includes('sharpness')) { mult = 1.5; note = '锋锐'; } else if (abId === 'megaLauncher' && moveObj.affectedBy?.includes('megaLauncher')) { mult = 1.5; note = ' Mega 发射器'; } else if (abId === 'reckless' && moveObj.timer > defaultPlayerMoveTimer) { mult = 1.5; note = '舍身'; } else if (abId === 'libero' && moveObj.timer < defaultPlayerMoveTimer) { mult = 2; note = '自由者'; } else if (abId === 'sheerForce' && moveObj.hitEffect) { mult = 1.25; note = '强行'; } else if (['overgrow','blaze','swarm','torrent','bastion','average', 'resolve','mistify','hexerei','glimmer','skyward', 'draconic','noxious','solid','rime','voltage'].includes(abId)) { const typeMap = { overgrow:'grass', blaze:'fire', swarm:'bug', torrent:'water', bastion:'steel', average:'normal', resolve:'fighting', mistify:'psychic', hexerei:'ghost', glimmer:'fairy', skyward:'flying', draconic:'dragon', noxious:'poison', solid:'rock', rime:'ice', voltage:'electric' }; if (moveObj.type === typeMap[abId] && attackerHPPercent < 0.5) { mult = 1.3; note = '激流/猛火等'; } } else if (abId === 'rivalry') { if (attacker.type.some(t => defender.type.includes(t))) { mult = 1.5; note = '斗争心'; } } else if (abId === 'toxicBoost' && attacker.status === 'poison' && isPhysical) { mult = 1.2; note = '剧毒boost'; } else if (abId === 'flareBoost' && attacker.status === 'burn' && !isPhysical) { mult = 1.2; note = '热暴走'; } else if (abId === 'merciless') { const isNerf = (typeof window.testAbility === 'function' && window.testAbility('active', abId) === 'nerf'); if (defenderHasStatus) { mult = isNerf ? 1.35 : 1.5; note = '不仁不义'; } } else if (abId === 'supremeOverlord') { mult = 1 + 0.15 * faintedAllies; note = '至尊将领'; } else if (abId === 'gorillaTactics') { const isNerf = (typeof window.testAbility === 'function' && window.testAbility('active', abId) === 'nerf'); mult = isNerf ? 1.35 : 1.5; note = '大力士/强壮之颚?'; } else if (abId === 'parentalBond') { mult = 1.5; note = '亲子爱'; } else if (abId === 'soulAsterism') { mult = 1 + 0.1 * (faintedAllies === 0 ? 5 : 6 - faintedAllies); note = '魂星群'; } // ----- 皮肤特性 ----- const ateAbilities = { ferrilate:'steel', refrigerate:'ice', terralate:'ground', toxilate:'poison', hydrolate:'water', pyrolate:'fire', chrysilate:'bug', galvanize:'electric', gloomilate:'dark', espilate:'psychic', aerilate:'flying', pixilate:'fairy', dragonMaw:'dragon', verdify:'grass' }; if (ateAbilities[abId] && moveObj.type === 'normal') { context.changedType = ateAbilities[abId]; mult = 1.3; note = '皮肤'; } if (abId === 'normalize' && moveObj.type !== 'normal') { context.changedType = 'normal'; mult = 1.3; note = '一般皮肤'; } // ----- 防御方减伤 ----- if (['filter','prismArmor','solidRock'].includes(abId)) { const typeMult = context.typeEffectiveness; if (typeMult > 1) { mult = 0.75; note = '过滤/棱镜/坚石'; } } else if (abId === 'multiscale' && defenderHPPercent >= 0.99) { mult = 0.5; note = '多重鳞片'; } else if (abId === 'wonderGuard') { if (context.typeEffectiveness <= 1) { mult = 0.2; note = '神奇守护'; } } else if (abId === 'levitate' && moveObj.type === 'ground') { mult = 0; note = '浮游'; } else if (abId === 'thickFat' && (moveObj.type === 'fire' || moveObj.type === 'ice')) { mult = 0.5; note = '厚脂肪'; } else if (abId === 'flashFire' && moveObj.type === 'fire') { mult = 0; note = '引火'; } else if (abId === 'waterAbsorb' && moveObj.type === 'water') { mult = 0; note = '储水'; } else if (abId === 'voltAbsorb' && moveObj.type === 'electric') { mult = 0; note = '蓄电'; } else if (abId === 'lightningRod' && moveObj.type === 'electric') { mult = 0; note = '避雷针'; } else if (abId === 'motorDrive' && moveObj.type === 'electric') { mult = 0; note = '电气引擎'; } else if (abId === 'sapSipper' && moveObj.type === 'grass') { mult = 0; note = '食草'; } else if (abId === 'stormDrain' && moveObj.type === 'water') { mult = 0; note = '引水'; } if (mult !== 1) { return { mult, note }; } return { mult: 1, note: '' }; } // ---------- 创建UI ---------- const panel = document.createElement('div'); panel.id = 'pokechill-damage-calc'; panel.innerHTML = `
伤害计算器
6
星级5(固定)
100
100
6
`; document.body.appendChild(panel); // 样式 const style = document.createElement('style'); style.textContent = ` #pokechill-damage-calc { position: fixed; top: 80px; right: 20px; width: 540px; max-width: 95%; background: #3068b0; border: 4px solid #f8d030; border-radius: 16px; box-shadow: 0 12px 30px rgba(0,0,0,0.8); z-index: 10000; color: #f8f8f8; font-family: 'Winky Sans', 'Segoe UI', sans-serif; font-size: 13px; user-select: none; backdrop-filter: blur(2px); } #pokechill-damage-calc .calc-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 14px; background: #204080; border-radius: 12px 12px 0 0; cursor: move; border-bottom: 2px solid #f8d030; } #pokechill-damage-calc .calc-title { font-weight: bold; color: #f8d030; display: flex; align-items: center; gap: 6px; font-size: 15px; text-shadow: 2px 2px 0 #183060; } #pokechill-damage-calc .calc-header-buttons { display: flex; gap: 6px; } #pokechill-damage-calc .calc-toggle-btn { background: #f8d030; border: 2px solid #c0a020; color: #204080; font-size: 16px; cursor: pointer; padding: 4px 8px; border-radius: 6px; transition: 0.1s; line-height: 1; font-weight: bold; } #pokechill-damage-calc .calc-toggle-btn:hover { background: #ffdf70; border-color: #e0b020; } #pokechill-damage-calc .calc-body { padding: 12px; max-height: 70vh; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #f8d030 #204080; } .calc-section { background: rgba(32, 64, 128, 0.7); border-radius: 10px; margin-bottom: 10px; border: 1px solid #f8d030; } .calc-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 10px; } .calc-field { display: flex; flex-direction: column; gap: 2px; } .calc-field label { display: flex; align-items: center; gap: 4px; color: #f8e0a0; font-size: 12px; text-shadow: 1px 1px 0 #183060; } .field-icon { font-size: 14px; } input[type="text"], select { width: 95%; padding: 4px 6px; background: #f0f0f0; border: 2px solid #f8d030; color: #204080; border-radius: 4px; font-size: 12px; outline: none; font-weight: bold; } input[type="text"]:focus, select:focus { border-color: #ffb0b0; box-shadow: 0 0 0 2px rgba(255,176,176,0.5); } .slider-group { display: flex; align-items: center; gap: 6px; width: 100%; } input[type="range"] { flex: 1; height: 6px; background: #f8d030; border-radius: 3px; -webkit-appearance: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; background: #f8f8f8; border: 2px solid #204080; border-radius: 50%; cursor: pointer; } .slider-group span { min-width: 20px; text-align: center; background: #f8d030; padding: 1px 0; border-radius: 3px; color: #204080; font-size: 12px; font-weight: bold; } .checkbox-field { justify-content: center; flex-direction: row; align-items: center; } .checkbox-field label { display: flex; align-items: center; gap: 4px; cursor: pointer; } input[type="checkbox"] { width: 14px; height: 14px; accent-color: #f8d030; } .calc-actions { display: flex; gap: 8px; justify-content: center; margin: 10px 0 6px; } .calc-btn { border: none; padding: 6px 14px; border-radius: 20px; font-weight: bold; font-size: 13px; cursor: pointer; transition: 0.15s; display: inline-flex; align-items: center; gap: 4px; border: 2px solid; } .calc-btn.primary { background: #f8d030; color: #204080; border-color: #c0a020; box-shadow: 0 3px 0 #a08010; } .calc-btn.primary:hover { background: #ffdf70; transform: translateY(-1px); box-shadow: 0 4px 0 #a08010; } .calc-btn.primary:active { transform: translateY(1px); box-shadow: 0 2px 0 #a08010; } .calc-btn.secondary { background: #c0c0c0; color: #204080; border-color: #a0a0a0; box-shadow: 0 3px 0 #808080; } .calc-btn.secondary:hover { background: #d0d0d0; transform: translateY(-1px); box-shadow: 0 4px 0 #808080; } .calc-result { background: rgba(32, 64, 128, 0.8); border-radius: 8px; padding: 10px; margin-top: 10px; border: 2px solid #f8d030; animation: fadeIn 0.3s; } .calc-result-title { font-size: 13px; font-weight: bold; color: #f8d030; margin-bottom: 8px; text-align: center; text-shadow: 1px 1px 0 #183060; } .calc-total { font-size: 18px; font-weight: bold; text-align: center; margin-top: 8px; color: #f8f8f8; background: #204080; padding: 4px; border-radius: 20px; border: 1px solid #f8d030; } #calc-breakdown { font-size: 11px; line-height: 1.5; color: #f0f0f0; max-height: 150px; overflow-y: auto; padding-right: 4px; } #calc-breakdown div { border-bottom: 1px dotted #f8d030; padding: 2px 0; } #calc-breakdown span { color: #f8d030; font-weight: bold; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @media (max-width: 600px) { #pokechill-damage-calc { width: 95%; right: 2.5%; top: 60px; } .calc-grid { grid-template-columns: 1fr; } input[type="text"], select { width: 80%; } } `; document.head.appendChild(style); // 获取元素 const attackerInput = document.getElementById('calc-attacker'); const defenderInput = document.getElementById('calc-defender'); const moveInput = document.getElementById('calc-move'); const abilityInput = document.getElementById('calc-ability'); const itemInput = document.getElementById('calc-item'); const atkIvSlider = document.getElementById('calc-atk-iv'); const defIvSlider = document.getElementById('calc-def-iv'); const atkIvVal = document.getElementById('calc-atk-iv-val'); const defIvVal = document.getElementById('calc-def-iv-val'); const shinyCheck = document.getElementById('calc-shiny'); const crossCheck = document.getElementById('calc-cross'); const atkStage = document.getElementById('calc-atk-stage'); const satkStage = document.getElementById('calc-satk-stage'); const defStage = document.getElementById('calc-def-stage'); const sdefStage = document.getElementById('calc-sdef-stage'); const attackerStatus = document.getElementById('calc-attacker-status'); const weatherSelect = document.getElementById('calc-weather'); const toggleBtn = panel.querySelector('.calc-toggle-btn'); const copyBtn = document.getElementById('calc-copy-result'); const hiddenAbilityDisplay = document.getElementById('calc-hidden-ability-display'); // 滑块联动 atkIvSlider.addEventListener('input', () => { atkIvVal.innerText = atkIvSlider.value; }); defIvSlider.addEventListener('input', () => { defIvVal.innerText = defIvSlider.value; }); // 构建数据列表 const pkmnIds = Object.keys(pkmn).filter(id => !pkmn[id].hidden); const moveIds = Object.keys(move); const abilityIds = Object.keys(ability); const itemIds = Object.keys(item).filter(id => item[id].type === 'held' && !item[id].hidden); createDataListWithChinese('pkmn-list', pkmnIds, attackerInput); createDataListWithChinese('pkmn-list', pkmnIds, defenderInput); createDataListWithChinese('move-list', moveIds, moveInput); createDataListWithChinese('ability-list', abilityIds, abilityInput); createDataListWithChinese('item-list', itemIds, itemInput); // 攻击方输入变化时自动更新隐藏特性(使用汉化显示) attackerInput.addEventListener('change', function() { const id = this.dataset.id; if (id) { const hiddenId = getHiddenAbility(id); hiddenAbilityDisplay.textContent = hiddenId ? getChinese(hiddenId) : '无'; } else { hiddenAbilityDisplay.textContent = '无'; } }); // 拖动 let isDragging = false, offsetX, offsetY; const header = panel.querySelector('.calc-header'); header.addEventListener('mousedown', (e) => { if (e.target.tagName === 'BUTTON') return; isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; panel.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; panel.style.right = 'auto'; }); document.addEventListener('mouseup', () => { isDragging = false; panel.style.cursor = 'default'; }); // 折叠/展开 let isCollapsed = false; toggleBtn.addEventListener('click', () => { isCollapsed = !isCollapsed; const body = panel.querySelector('.calc-body'); const result = panel.querySelector('#calc-result'); if (isCollapsed) { toggleBtn.textContent = '︾'; body.style.display = 'none'; if (result.style.display !== 'none') { result.style.display = 'none'; result.__wasVisibleBeforeCollapse = true; } } else { toggleBtn.textContent = '︽'; body.style.display = 'block'; if (result.__wasVisibleBeforeCollapse) { result.style.display = 'block'; delete result.__wasVisibleBeforeCollapse; } } }); // 重置 document.getElementById('calc-reset').addEventListener('click', () => { attackerInput.value = ''; attackerInput.dataset.id = ''; defenderInput.value = ''; defenderInput.dataset.id = ''; moveInput.value = ''; moveInput.dataset.id = ''; abilityInput.value = ''; abilityInput.dataset.id = ''; itemInput.value = ''; itemInput.dataset.id = ''; atkIvSlider.value = 6; atkIvVal.innerText = '6'; defIvSlider.value = 6; defIvVal.innerText = '6'; shinyCheck.checked = false; crossCheck.checked = false; atkStage.value = '0'; satkStage.value = '0'; defStage.value = '0'; sdefStage.value = '0'; attackerStatus.value = 'none'; weatherSelect.value = ''; document.getElementById('calc-attacker-hp').value = 100; document.getElementById('calc-attacker-hp-val').innerText = '100'; document.getElementById('calc-defender-hp').value = 100; document.getElementById('calc-defender-hp-val').innerText = '100'; document.getElementById('calc-defender-status').checked = false; document.getElementById('calc-fainted-allies').value = 0; hiddenAbilityDisplay.textContent = '无'; document.getElementById('calc-result').style.display = 'none'; }); // 计算按钮 document.getElementById('calc-btn').addEventListener('click', () => { const attackerId = attackerInput.dataset.id; const defenderId = defenderInput.dataset.id; const moveId = moveInput.dataset.id; const abilityId = abilityInput.dataset.id; const itemId = itemInput.dataset.id; const itemLevel = 5; const atkIv = parseInt(atkIvSlider.value, 10); const defIv = parseInt(defIvSlider.value, 10); const isShiny = shinyCheck.checked; const isCross = crossCheck.checked; const hiddenAbilityId = getHiddenAbility(attackerId); const attackerHPPercent = parseFloat(document.getElementById('calc-attacker-hp').value) / 100; const defenderHPPercent = parseFloat(document.getElementById('calc-defender-hp').value) / 100; const defenderHasStatus = document.getElementById('calc-defender-status').checked; const faintedAllies = parseInt(document.getElementById('calc-fainted-allies').value, 10) || 0; // 使用汉化显示隐藏特性名称 if (hiddenAbilityId) { hiddenAbilityDisplay.textContent = getChinese(hiddenAbilityId); } else { hiddenAbilityDisplay.textContent = '无'; } const atkStageVal = parseFloat(atkStage.value); const satkStageVal = parseFloat(satkStage.value); const defStageVal = parseFloat(defStage.value); const sdefStageVal = parseFloat(sdefStage.value); const attackerStatusVal = attackerStatus.value; const weatherVal = weatherSelect.value; const attacker = pkmn[attackerId]; const defender = pkmn[defenderId]; const moveObj = move[moveId]; if (!attacker || !defender || !moveObj) { alert('请正确填写宝可梦和招式'); return; } if (moveObj.power === 0) { alert('变化招式没有伤害'); return; } const abilityObj = ability[abilityId]; const hiddenAbilityObj = hiddenAbilityId ? ability[hiddenAbilityId] : null; const split = moveObj.split; const isPhysical = split === 'physical'; let attackerBaseStar = isPhysical ? attacker.bst.atk : attacker.bst.satk; let defenderBaseStar = isPhysical ? defender.bst.def : defender.bst.sdef; const atkStageMult = getStageMult(atkStageVal, false); const satkStageMult = getStageMult(satkStageVal, false); const defStageMult = getStageMult(defStageVal, true); const sdefStageMult = getStageMult(sdefStageVal, true); let attackerStar = attackerBaseStar * (isPhysical ? atkStageMult : satkStageMult); let defenderStar = defenderBaseStar * (isPhysical ? defStageMult : sdefStageMult); const atkFactor = Math.pow(1.1, atkIv); const defFactor = Math.pow(1.1, defIv); const attackerLevel = 100; const levelMult = 1 + 100 * 0.1; let baseDamage = moveObj.power + Math.max(0, (attackerStar * 30 * atkFactor) - (defenderStar * 30 * defFactor) ); let damage = baseDamage * levelMult; let breakdown = []; breakdown.push(`基础威力: ${moveObj.power}`); breakdown.push(`攻击星: ${attackerBaseStar.toFixed(1)} x能力${(isPhysical ? atkStageVal : satkStageVal)} = ${attackerStar.toFixed(1)} (x30 x${atkFactor.toFixed(2)}) = ${(attackerStar * 30 * atkFactor).toFixed(0)}`); breakdown.push(`防御星: ${defenderBaseStar.toFixed(1)} x能力${(isPhysical ? defStageVal : sdefStageVal)} = ${defenderStar.toFixed(1)} (x30 x${defFactor.toFixed(2)}) = ${(defenderStar * 30 * defFactor).toFixed(0)}`); breakdown.push(`差值: ${(attackerStar * 30 * atkFactor - defenderStar * 30 * defFactor).toFixed(0)}`); breakdown.push(`基础伤害: ${baseDamage.toFixed(0)}`); breakdown.push(`等级系数 x${levelMult.toFixed(2)}`); let totalMult = 1; let multLog = '1'; // 上下文 const context = { isPhysical, moveObj, attacker, defender, movePower: moveObj.power, attackerHPPercent, defenderHPPercent, defenderHasStatus, faintedAllies, weather: weatherVal, typeEffectiveness: 1, area: '', fieldEffect: null, adaptabilitySTAB: false, changedType: null }; // 应用特性 let abilityMult = 1; if (abilityObj) { const res = applyAbility(abilityObj, context); abilityMult *= res.mult; if (res.note) breakdown.push(`特性 ${res.note} x${res.mult.toFixed(2)}`); if (context.changedType) moveObj.type = context.changedType; } if (hiddenAbilityObj) { const res = applyAbility(hiddenAbilityObj, context); abilityMult *= res.mult; if (res.note) breakdown.push(`隐藏特性 ${res.note} x${res.mult.toFixed(2)}`); if (context.changedType) moveObj.type = context.changedType; } // 属性克制 let typeMult = calculateTypeEffectiveness(moveObj.type, defender.type, hiddenAbilityObj || abilityObj, context); context.typeEffectiveness = typeMult; damage *= typeMult; totalMult *= typeMult; multLog += ` x${typeMult.toFixed(2)} (克制)`; breakdown.push(`属性克制 x${typeMult.toFixed(2)}`); // STAB let stab = 1; if (attacker.type.includes(moveObj.type)) { stab = 1.5; // 基础本系加成 if (attacker.type.length === 1) stab += 0.2; // 单属性额外 +0.2 // 适应力特性:额外 +0.2(只需判断普通或隐藏是否拥有适应力) const hasAdaptability = (abilityObj && abilityObj.id === 'adaptability') || (hiddenAbilityObj && hiddenAbilityObj.id === 'adaptability'); if (hasAdaptability) stab += 0.2; damage *= stab; totalMult *= stab; multLog += ` x${stab.toFixed(2)} (STAB)`; breakdown.push(`STAB x${stab.toFixed(2)}`); } else { breakdown.push(`STAB x1 (无本系)`); } // 天气 const weatherMult = getWeatherTypeMult(weatherVal, moveObj.type); if (weatherMult !== 1) { damage *= weatherMult; totalMult *= weatherMult; multLog += ` x${weatherMult.toFixed(2)} (天气)`; breakdown.push(`天气 x${weatherMult.toFixed(2)}`); } // 闪光 if (isShiny) { damage *= 1.15; totalMult *= 1.15; multLog += ` x1.15 (闪光)`; breakdown.push(`闪光 x1.15`); } // 交叉之力 if (isCross) { damage *= 1.3; totalMult *= 1.3; multLog += ` x1.3 (交叉)`; breakdown.push(`交叉之力 x1.3`); } // 攻击方状态 if (attackerStatusVal === 'burn' && isPhysical) { damage /= 1.5; totalMult /= 1.5; multLog += ` ÷1.5 (烧伤)`; breakdown.push(`烧伤 ÷1.5`); } if (attackerStatusVal === 'poison' && !isPhysical) { damage /= 1.5; totalMult /= 1.5; multLog += ` ÷1.5 (中毒)`; breakdown.push(`中毒 ÷1.5`); } // 道具 let itemMult = 1; if (itemId) { itemMult = getItemMultiplier(itemId, itemLevel); if (itemMult > 1) { damage *= itemMult; totalMult *= itemMult; multLog += ` x${itemMult.toFixed(2)} (道具)`; breakdown.push(`道具 x${itemMult.toFixed(2)} (星级5)`); } else { breakdown.push(`道具: 无伤害加成`); } } // 应用特性总倍率 damage *= abilityMult; totalMult *= abilityMult; const finalDamage = Math.round(damage); // 显示结果 if (isCollapsed) { isCollapsed = false; panel.querySelector('.calc-body').style.display = 'block'; const result = panel.querySelector('#calc-result'); if (result.__wasVisibleBeforeCollapse) { result.style.display = 'block'; delete result.__wasVisibleBeforeCollapse; } toggleBtn.textContent = '︽'; } document.getElementById('calc-result').style.display = 'block'; document.getElementById('calc-total').innerText = finalDamage.toLocaleString(); const breakdownDiv = document.getElementById('calc-breakdown'); breakdownDiv.innerHTML = ''; breakdown.forEach(line => { const p = document.createElement('div'); p.innerHTML = line.replace(/x([\d.]+)/g, 'x$1'); breakdownDiv.appendChild(p); }); const multLine = document.createElement('div'); multLine.style.marginTop = '6px'; multLine.style.borderTop = '1px dashed #f8d030'; multLine.style.paddingTop = '4px'; multLine.innerHTML = `总倍率: ${multLog} = ${totalMult.toFixed(3)}`; breakdownDiv.appendChild(multLine); }); // 复制报告(简化版) copyBtn.addEventListener('click', () => { alert('复制功能暂未实现,请手动复制结果。'); }); console.log('[伤害计算器v2.1] 初始化完成,遵循Pokechill 1.5倍克制规则,汉化已启用'); } })();