From f8c03dffcc77365cb1865a4bca4e4973666009e7 Mon Sep 17 00:00:00 2001 From: yoimiya-kokomi <592981798@qq.com> Date: Tue, 23 Aug 2022 04:53:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=83=A8=E5=88=86=E4=BC=A4?= =?UTF-8?q?=E5=AE=B3=E8=AE=A1=E7=AE=97=E5=BA=95=E5=B1=82=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Profile.js | 2 +- components/profile-data/index.js | 0 config/system/character.js | 8 +- models/ProfileArtis.js | 26 +- models/ProfileData.js | 5 +- models/ProfileDmg.js | 131 ++++++--- models/ProfileReq.js | 10 +- models/index.js | 3 +- models/profile-lib/Calc.js | 440 +------------------------------ models/profile-lib/DmgAttr.js | 184 +++++++++++++ models/profile-lib/DmgBuffs.js | 87 ++++++ models/profile-lib/DmgCalc.js | 162 ++++++++++++ tools/dmg-calc-tool.js | 16 +- 13 files changed, 578 insertions(+), 496 deletions(-) delete mode 100644 components/profile-data/index.js create mode 100644 models/profile-lib/DmgAttr.js create mode 100644 models/profile-lib/DmgBuffs.js create mode 100644 models/profile-lib/DmgCalc.js diff --git a/components/Profile.js b/components/Profile.js index fc9f61e8..8e72521a 100644 --- a/components/Profile.js +++ b/components/Profile.js @@ -1,8 +1,8 @@ import fs from 'fs' import lodash from 'lodash' +import { Character, ProfileReq, ProfileData } from '../models/index.js' import Miao from './profile-data/miao.js' import Enka from './profile-data/enka.js' -import { Character, ProfileReq, ProfileData } from '../models/index.js' const _path = process.cwd() const userPath = `${_path}/data/UserData/` diff --git a/components/profile-data/index.js b/components/profile-data/index.js deleted file mode 100644 index e69de29b..00000000 diff --git a/config/system/character.js b/config/system/character.js index 85676572..d021109d 100644 --- a/config/system/character.js +++ b/config/system/character.js @@ -92,7 +92,7 @@ export const characters = { gongji: ['普契涅拉', '公鸡', '鸽子'], duizhang: ['卡皮塔诺', '队长'], nilu: ['妮露'], - nahida: ['纳西妲', '草神'] + nahida: ['纳西妲', '草神', '小吉祥', '小吉祥草王', '花神', '草萝莉'] } /* @@ -103,14 +103,14 @@ export const wifeData = { // 老婆&女朋友:成女、少女 girlfriend: `琴, 丽莎, 荧, 芭芭拉, 安柏, 香菱, 北斗, 凝光, 菲谢尔, 诺艾尔, 甘雨, 莫娜, 刻晴, 砂糖, 辛焱, 罗莎莉亚, 胡桃, 烟绯, 优菈, 神里绫华, 宵宫, 雷电将军, 珊瑚宫心海, 九条裟罗, 八重神子, 埃洛伊, 申鹤, 云堇, 夜兰, 久岐忍, 柯莱, 多莉, 伐难, - 女士, 萍姥姥, 柯莱, 多莉, 仆人, 少女, 妮露, 纳西妲`, + 女士, 萍姥姥, 柯莱, 多莉, 仆人, 少女, 妮露`, // 老公&男朋友:成男、少男 boyfriend: `空, 凯亚, 迪卢克, 雷泽, 温迪, 行秋, 魈, 钟离, 班尼特, 达达利亚, 重云, 阿贝多, 枫原万叶, 托马, 五郎, 荒泷一斗, 鹿野院平藏, 神里绫人, 提纳里, 散兵, 白术, 提纳里, 富人, 博士, 丑角, 公鸡, 队长`, // 女儿:萝莉 - daughter: '可莉, 七七, 迪奥娜, 早柚, 派蒙, 瑶瑶', + daughter: '可莉, 七七, 迪奥娜, 早柚, 派蒙, 瑶瑶, 纳西妲', // 儿子:正太 son: '' @@ -126,7 +126,7 @@ export const abbr = { 荒泷一斗: '一斗', 八重神子: '八重', 九条裟罗: '九条', - 罗莎莉亚: '罗莎', + 罗莎莉亚: '修女', 鹿野院平藏: '平藏', 松籁响起之时: '松籁', diff --git a/models/ProfileArtis.js b/models/ProfileArtis.js index 7267c7a5..950d055b 100644 --- a/models/ProfileArtis.js +++ b/models/ProfileArtis.js @@ -129,6 +129,30 @@ export default class ProfileArtis extends Base { return ret } + getSetData () { + let setCount = {} + this.forEach((arti, idx) => { + setCount[arti.set] = (setCount[arti.set] || 0) + 1 + }) + let sets = {} + let names = [] + for (let set in setCount) { + if (setCount[set] >= 2) { + sets[set] = setCount[set] >= 4 ? 4 : 2 + names.push(Artifact.getArtiBySet(set)) + } + } + return { sets, names } + } + + get sets () { + return this.getSetData().sets + } + + get names () { + return this.getSetData().names + } + getCharCfg () { let char = Character.get(this.charid) let name = char.name @@ -178,4 +202,4 @@ export default class ProfileArtis extends Base { } return charCfg[name] } -} \ No newline at end of file +} diff --git a/models/ProfileData.js b/models/ProfileData.js index fd3fae28..b38a4f28 100644 --- a/models/ProfileData.js +++ b/models/ProfileData.js @@ -116,7 +116,10 @@ export default class ProfileData extends Base { // 计算当前profileData的伤害信息 async calcDmg ({ enemyLv = 91, mode = 'profile', dmgIdx = 0 }) { if (!this.dmg) { - this.dmg = new ProfileDmg(this) + let ds = this.getData('id,level,attr,cons,artis:artis.sets') + ds.talent = lodash.mapValues(this.talent, 'level') + ds.weapon = Data.getData(this.weapon, 'name,affix') + this.dmg = new ProfileDmg(ds) } return await this.dmg.calcData({ enemyLv, mode, dmgIdx }) } diff --git a/models/ProfileDmg.js b/models/ProfileDmg.js index b9e87d59..0af31b9e 100644 --- a/models/ProfileDmg.js +++ b/models/ProfileDmg.js @@ -1,64 +1,117 @@ +import fs from 'fs' +import lodash from 'lodash' import Base from './Base.js' import { Character } from './index.js' -import lodash from 'lodash' import { attrMap } from './profile-lib/calc-meta.js' -import Calc from './profile-lib/Calc.js' +import DmgBuffs from './profile-lib/DmgBuffs.js' +import DmgAttr from './profile-lib/DmgAttr.js' +import DmgCalc from './profile-lib/DmgCalc.js' export default class ProfileDmg extends Base { - constructor (profile = false) { + constructor (profile = {}) { super() this.profile = profile if (profile && profile.id) { let { id } = profile this.char = Character.get(id) } + if (!this.char) { + return false + } + } + + // 获取天赋数据 + talent () { + let char = this.char + let profile = this.profile + let ret = {} + let talentData = profile.talent || {} + lodash.forEach(['a', 'e', 'q'], (key) => { + let level = lodash.isNumber(talentData[key]) ? talentData[key] : (talentData[key].level || 1) + + let map = {} + + lodash.forEach(char.talent[key].tables, (tr) => { + let val = tr.values[level - 1] + // eslint-disable-next-line no-control-regex + val = val.replace(/[^\x00-\xff]/g, '').trim() + let valArr = [] + let valArr2 = [] + lodash.forEach(val.split('/'), (v, idx) => { + let valNum = 0 + lodash.forEach(v.split('+'), (v) => { + v = v.split('*') + let v1 = v[0].replace('%', '').trim() + valNum += v1 * (v[1] || 1) + valArr2.push(v1) + }) + valArr.push(valNum) + }) + + if (isNaN(valArr[0])) { + map[tr.name] = false + } else if (valArr.length === 1) { + map[tr.name] = valArr[0] + } else { + map[tr.name] = valArr + } + map[tr.name + '2'] = valArr2 + }) + ret[key] = map + }) + return ret + } + + // 获取buff列表 + getBuffs (buffs) { + return DmgBuffs.getBuffs(this.profile, buffs) + } + + async getCalcRule () { + const _path = process.cwd() + const cfgPath = `${_path}/plugins/miao-plugin/resources/meta/character/${this.char?.name}/calc.js` + let cfg = {} + if (fs.existsSync(cfgPath)) { + cfg = await import(`file://${cfgPath}`) + return { + details: cfg.details || false, // 计算详情 + buffs: cfg.buffs || [], // 角色buff + defParams: cfg.defParams || {}, // 默认参数,一般为空 + defDmgIdx: cfg.defDmgIdx || -1, // 默认详情index + mainAttr: cfg.mainAttr || 'atk,cpct,cdmg', // 伤害属性 + enemyName: cfg.enemyName || '小宝' // 敌人名称 + } + } + return false } async calcData ({ enemyLv = 91, mode = 'profile', dmgIdx = 0 }) { if (!this.char || !this.profile) { return false } - let { profile, char } = this - let charCalcData = await Calc.getCharCalcRule(this.char.name) + let { profile } = this + let charCalcData = await this.getCalcRule() if (!charCalcData) { return false } - let talent = Calc.talent(profile, char) + let { buffs, details, defParams, mainAttr, defDmgIdx, enemyName } = charCalcData + + let talent = this.talent() let meta = { cons: profile.cons * 1, talent } - - let { buffs, details, defParams, mainAttr, defDmgIdx, enemyName } = charCalcData + let { id, weapon, attr } = profile defParams = defParams || {} - let originalAttr = Calc.attr(profile) + let originalAttr = DmgAttr.getAttr({ id, weapon, attr, char: this.char }) - let weaponBuffs = await Calc.weapon(profile.weapon.name) - let reliBuffs = await Calc.reliquaries(profile.artis?.artis || {}) - buffs = lodash.concat(buffs, weaponBuffs, reliBuffs) - let mKey = { - zf: '蒸发', - rh: '融化', - ks: '扩散' - } - lodash.forEach(buffs, (buff, idx) => { - if (lodash.isString(buff) && mKey[buff]) { - buff = { - title: `元素精通:${mKey[buff]}伤害提高[${buff}]%`, - mastery: buff - } - buffs[idx] = buff - } - buff.sort = lodash.isUndefined(buff.sort) ? 1 : buff.sort - }) + buffs = this.getBuffs(buffs) - buffs = lodash.sortBy(buffs, ['sort']) - - let { msg } = Calc.calcAttr({ originalAttr, buffs, meta, params: defParams || {} }) + let { msg } = DmgAttr.calcAttr({ originalAttr, buffs, meta, params: defParams || {} }) let ret = [] let detailMap = [] @@ -67,21 +120,21 @@ export default class ProfileDmg extends Base { lodash.forEach(details, (detail, detailSysIdx) => { if (lodash.isFunction(detail)) { - let { attr } = Calc.calcAttr({ originalAttr, buffs, meta }) - let ds = lodash.merge({ talent }, Calc.getDs(attr, meta)) + let { attr } = DmgAttr.calcAttr({ originalAttr, buffs, meta }) + let ds = lodash.merge({ talent }, DmgAttr.getDs(attr, meta)) detail = detail({ ...ds, attr, profile }) } let params = lodash.merge({}, defParams, detail.params || {}) - let { attr } = Calc.calcAttr({ originalAttr, buffs, meta, params, talent: detail.talent || '' }) - if (detail.check && !detail.check(Calc.getDs(attr, meta, params))) { + let { attr } = DmgAttr.calcAttr({ originalAttr, buffs, meta, params, talent: detail.talent || '' }) + if (detail.check && !detail.check(DmgAttr.getDs(attr, meta, params))) { return } if (detail.cons && meta.cons < detail.cons * 1) { return } - let ds = lodash.merge({ talent }, Calc.getDs(attr, meta, params)) + let ds = lodash.merge({ talent }, DmgAttr.getDs(attr, meta, params)) - let dmg = Calc.getDmgFn({ ds, attr, profile, enemyLv, showDetail: detail.showDetail }) + let dmg = DmgCalc.getDmgFn({ ds, attr, level: profile.level, enemyLv, showDetail: detail.showDetail }) let basicDmgRet if (detail.dmg) { @@ -123,7 +176,7 @@ export default class ProfileDmg extends Base { rowData.push({ type: 'na' }) return } - let { attr } = Calc.calcAttr({ + let { attr } = DmgAttr.calcAttr({ originalAttr, buffs, meta, @@ -132,8 +185,8 @@ export default class ProfileDmg extends Base { reduceAttr, talent: detail.talent || '' }) - let ds = lodash.merge({ talent }, Calc.getDs(attr, meta, params)) - let dmg = Calc.getDmgFn({ ds, attr, profile, enemyLv }) + let ds = lodash.merge({ talent }, DmgAttr.getDs(attr, meta, params)) + let dmg = DmgCalc.getDmgFn({ ds, attr, level: profile.level, enemyLv }) if (detail.dmg) { let dmgCalcRet = detail.dmg(ds, dmg) rowData.push({ diff --git a/models/ProfileReq.js b/models/ProfileReq.js index d8aef85e..838e156b 100644 --- a/models/ProfileReq.js +++ b/models/ProfileReq.js @@ -63,8 +63,14 @@ export default class ProfileReq extends Base { this.msg('开始获取数据,可能会需要一定时间~') await sleep(100) // 发起请求 - let req = await fetch(reqParam.url, reqParam.params || {}) - let data = await req.json() + let data = {} + try { + let req = await fetch(reqParam.url, reqParam.params || {}) + data = await req.json() + } catch (e) { + console.log('面板请求错误', e) + data = {} + } data = await Serv.response(data, this) // 设置CD cdTime = Serv.getCdTime(data) diff --git a/models/index.js b/models/index.js index 20e88226..9ddfb435 100644 --- a/models/index.js +++ b/models/index.js @@ -1,3 +1,4 @@ +import Base from './Base.js' import Character from './Character.js' import Artifact from './Artifact.js' import Avatars from './Avatars.js' @@ -8,4 +9,4 @@ import ProfileData from './ProfileData.js' import ProfileArtis from './ProfileArtis.js' import ProfileDmg from './ProfileDmg.js' -export { Abyss, Character, Artifact, Avatars, ProfileServ, ProfileReq, ProfileData, ProfileArtis, ProfileDmg } +export { Base, Abyss, Character, Artifact, Avatars, ProfileServ, ProfileReq, ProfileData, ProfileArtis, ProfileDmg } diff --git a/models/profile-lib/Calc.js b/models/profile-lib/Calc.js index 33a5afb7..12e833d0 100644 --- a/models/profile-lib/Calc.js +++ b/models/profile-lib/Calc.js @@ -1,321 +1,9 @@ -import fs from 'fs' import lodash from 'lodash' -import { Format } from '../../components/index.js' import { Character } from '../index.js' -import { eleBaseDmg, eleMap, attrMap } from './calc-meta.js' +import { eleBaseDmg, eleMap, } from './calc-meta.js' import Mastery from './Mastery.js' let Calc = { - - async getCharCalcRule (name) { - const _path = process.cwd() - const cfgPath = `${_path}/plugins/miao-plugin/resources/meta/character/${name}/calc.js` - - let details - let buffs = [] - let defParams = {} - let defDmgIdx = -1 - let mainAttr = 'atk,cpct,cdmg' - let enemyName = '小宝' - if (fs.existsSync(cfgPath)) { - let fileData = await import(`file://${cfgPath}`) - details = fileData.details || false - buffs = fileData.buffs || [] - defParams = fileData.defParams || {} - if (fileData.defDmgIdx) { - defDmgIdx = fileData.defDmgIdx - } - if (fileData.mainAttr) { - mainAttr = fileData.mainAttr - } - if (fileData.enemyName) { - enemyName = fileData.enemyName - } - } - - if (details) { - return { details, buffs, defParams, defDmgIdx, mainAttr, enemyName } - } - return false - }, - - // 获取基础属性 - attr (profile) { - let ret = {} - let { attr } = profile - - // 基础属性 - lodash.forEach('atk,def,hp'.split(','), (key) => { - ret[key] = { - base: attr[`${key}Base`] * 1 || 0, - plus: attr[key] * 1 - attr[`${key}Base`] * 1 || 0, - pct: 0 - } - }) - - lodash.forEach('mastery,recharge,cpct,cdmg,heal,dmg,phy'.split(','), (key) => { - ret[key] = { - base: attr[key] * 1 || 0, - plus: 0, - pct: 0, - inc: 0 // 护盾增效&治疗增效 - } - }) - - // 技能属性记录 - lodash.forEach('a,a2,a3,e,q'.split(','), (key) => { - ret[key] = { - pct: 0, // 倍率加成 - multi: 0, // 独立倍率乘区加成 - - plus: 0, // 伤害值提高 - dmg: 0, // 伤害提高 - cpct: 0, // 暴击提高 - cdmg: 0, // 爆伤提高 - - def: 0, // 防御降低 - ignore: 0 // 无视防御 - } - }) - - ret.enemy = { - def: 0, // 降低防御 - ignore: 0, // 无视防御 - phy: 0 // 物理防御 - } - - ret.shield = { - base: 100, // 基础 - plus: 0, // 护盾强效 - inc: 100 // 吸收倍率 - } - - let char = Character.get(profile) - ret.weapon = profile.weapon - ret.weaponType = char.weaponType - ret.element = eleMap[char.elem.toLowerCase()] - ret.refine = (profile.weapon.affix * 1 - 1) || 0 - - ret.multi = 0 - - ret.zf = 0 - ret.rh = 0 - ret.gd = 0 - ret.ks = 0 - - ret.kx = 0 - ret.fykx = 0 - - return ret - }, - - // 获取天赋数据 - talent (profile, char) { - let ret = {} - - let talentData = profile.talent || {} - - lodash.forEach(['a', 'e', 'q'], (key) => { - let td = talentData[key] || {} - let lv = (td.level || td.level_current || 1) * 1 - - let map = {} - - lodash.forEach(char.talent[key].tables, (tr) => { - let val = tr.values[lv - 1] - val = val.replace(/[^\x00-\xff]/g, '').trim() - let valArr = [] - let valArr2 = [] - lodash.forEach(val.split('/'), (v, idx) => { - let valNum = 0 - lodash.forEach(v.split('+'), (v) => { - v = v.split('*') - let v1 = v[0].replace('%', '').trim() - valNum += v1 * (v[1] || 1) - valArr2.push(v1) - }) - valArr.push(valNum) - }) - - if (isNaN(valArr[0])) { - map[tr.name] = false - } else if (valArr.length === 1) { - map[tr.name] = valArr[0] - } else { - map[tr.name] = valArr - } - map[tr.name + '2'] = valArr2 - }) - ret[key] = map - }) - return ret - }, - - getDs (attr, meta, params) { - return { - ...meta, - attr, - params, - refine: attr.refine, - weaponType: attr.weaponType, - weapon: attr.weapon, - element: eleMap[attr.element] || attr.element, - calc (ds) { - return (ds.base || 0) + (ds.plus || 0) + ((ds.base || 0) * (ds.pct || 0) / 100) - } - } - }, - - calcAttr ({ originalAttr, buffs, meta, params = {}, incAttr = '', reduceAttr = '', talent = '' }) { - let attr = lodash.merge({}, originalAttr) - let msg = [] - - if (incAttr && attrMap[incAttr]) { - let aCfg = attrMap[incAttr] - attr[incAttr][aCfg.type] += aCfg.val - } - if (reduceAttr && attrMap[reduceAttr]) { - let aCfg = attrMap[reduceAttr] - attr[reduceAttr][aCfg.type] -= aCfg.val - } - - lodash.forEach(buffs, (buff) => { - let ds = Calc.getDs(attr, meta, params) - - ds.currentTalent = talent - - let mKey = { - zf: '蒸发', - rh: '融化', - ks: '扩散' - } - if (lodash.isString(buff) && mKey[buff]) { - buff = { - zf: { - title: `元素精通:${mKey[buff]}伤害提高[${buff}]%`, - mastery: buff - } - } - } - - // 如果存在rule,则进行计算 - if (buff.check && !buff.check(ds)) { - return - } - if (buff.cons) { - if (ds.cons * 1 < buff.cons * 1) { - return - } - } - - let title = buff.title - - if (buff.mastery) { - let mastery = Math.max(0, attr.mastery.base + attr.mastery.plus) - // let masteryNum = 2.78 * mastery / (mastery + 1400) * 100; - buff.data = buff.data || {} - lodash.forEach(buff.mastery.split(','), (key) => { - buff.data[key] = Mastery.getMultiple(key, mastery) - // buff.data[key] = masteryNum; - }) - } - - lodash.forEach(buff.data, (val, key) => { - if (lodash.isFunction(val)) { - val = val(ds) - } - - title = title.replace(`[${key}]`, Format.comma(val, 1)) - // 技能提高 - let tRet = /^(a|a2|a3|e|q)(Def|Ignore|Dmg|Plus|Pct|Cpct|Cdmg|Multi)$/.exec(key) - if (tRet) { - attr[tRet[1]][tRet[2].toLowerCase()] += val * 1 || 0 - return - } - let aRet = /^(hp|def|atk|mastery|cpct|cdmg|heal|recharge|dmg|phy|shield)(Plus|Pct|Inc)?$/.exec(key) - if (aRet) { - attr[aRet[1]][aRet[2] ? aRet[2].toLowerCase() : 'plus'] += val * 1 || 0 - return - } - if (key === 'enemyDef') { - attr.enemy.def += val * 1 || 0 - return - } - - if (['zf', 'rh', 'kx', 'gd', 'ks', 'fykx'].includes(key)) { - attr[key] += val * 1 || 0 - } - }) - msg.push(title) - }) - - return { - attr, msg - } - }, - - async weapon (weaponName) { - const _path = process.cwd() - const cfgPath = `${_path}/plugins/miao-plugin/resources/meta/weapons/calc.js` - - let weapons = {} - if (fs.existsSync(cfgPath)) { - let fileData = await import(`file://${cfgPath}`) - weapons = fileData.weapons || {} - } - - let weaponCfg = weapons[weaponName] || [] - if (lodash.isPlainObject(weaponCfg)) { - weaponCfg = [weaponCfg] - } - - lodash.forEach(weaponCfg, (ds) => { - if (!/:/.test(ds.title)) { - ds.title = `${weaponName}:${ds.title}` - } - if (ds.refine) { - ds.data = ds.data || {} - lodash.forEach(ds.refine, (r, key) => { - ds.data[key] = ({ refine }) => r[refine] * (ds.buffCount || 1) - }) - } - }) - - return weaponCfg - }, - - async reliquaries (sets) { - const _path = process.cwd() - const cfgPath = `${_path}/plugins/miao-plugin/resources/meta/reliquaries/calc.js` - - let buffs = {} - if (fs.existsSync(cfgPath)) { - let fileData = await import(`file://${cfgPath}`) - buffs = fileData.buffs || {} - } - - let setMap = {} - - lodash.forEach(sets, (set) => { - if (set && set.set) { - let name = set.set - setMap[name] = (setMap[name] || 0) + 1 - } - }) - - let retBuffs = [] - - lodash.forEach(setMap, (count, setName) => { - if (count >= 2 && buffs[setName + 2]) { - retBuffs.push(buffs[setName + 2]) - } - if (count >= 4 && buffs[setName + 4]) { - retBuffs.push(buffs[setName + 4]) - } - }) - return retBuffs - }, - getDmgFn ({ ds, attr, profile, enemyLv, showDetail = false }) { let { calc } = ds @@ -449,132 +137,6 @@ let Calc = { } return dmgFn - }, - - async calcData ({ profile, char, enemyLv = 91, mode = 'profile', dmgIdx = 0 }) { - let charCalcData = await Calc.getCharCalcRule(char.name) - - if (!charCalcData) { - return false - } - let talent = Calc.talent(profile, char) - - let meta = { - cons: profile.cons * 1, - talent - } - - let { buffs, details, defParams, mainAttr, defDmgIdx, enemyName } = charCalcData - - defParams = defParams || {} - - let originalAttr = Calc.attr(profile) - - let weaponBuffs = await Calc.weapon(profile.weapon.name) - let reliBuffs = await Calc.reliquaries(profile.artis) - buffs = lodash.concat(buffs, weaponBuffs, reliBuffs) - - lodash.forEach(buffs, (buff) => { - buff.sort = lodash.isUndefined(buff.sort) ? 1 : buff.sort - }) - - buffs = lodash.sortBy(buffs, ['sort']) - - let { msg } = Calc.calcAttr({ originalAttr, buffs, meta, params: defParams || {} }) - - let ret = [] - let detailMap = [] - let dmgRet = [] - let dmgDetail = {} - - lodash.forEach(details, (detail, detailSysIdx) => { - if (lodash.isFunction(detail)) { - let { attr } = Calc.calcAttr({ originalAttr, buffs, meta }) - let ds = lodash.merge({ talent }, Calc.getDs(attr, meta)) - detail = detail({ ...ds, attr, profile }) - } - let params = lodash.merge({}, defParams, detail.params || {}) - let { attr } = Calc.calcAttr({ originalAttr, buffs, meta, params, talent: detail.talent || '' }) - if (detail.check && !detail.check(Calc.getDs(attr, meta, params))) { - return - } - if (detail.cons && meta.cons < detail.cons * 1) { - return - } - let ds = lodash.merge({ talent }, Calc.getDs(attr, meta, params)) - - let dmg = Calc.getDmgFn({ ds, attr, profile, enemyLv, showDetail: detail.showDetail }) - let basicDmgRet - - if (detail.dmg) { - basicDmgRet = detail.dmg(ds, dmg) - detail.userIdx = detailMap.length - detailMap.push(detail) - ret.push({ - title: detail.title, - ...basicDmgRet - }) - } - }) - - if (mode === 'dmg') { - let detail - if (dmgIdx && detailMap[dmgIdx - 1]) { - detail = detailMap[dmgIdx - 1] - } else if (!lodash.isUndefined(defDmgIdx) && details[defDmgIdx]) { - detail = details[defDmgIdx] - } else { - detail = detailMap[0] - } - - dmgDetail = { - title: detail.title, - userIdx: detail.userIdx, - basicRet: lodash.merge({}, ret[detail.userIdx]), - attr: [] - } - - mainAttr = mainAttr.split(',') - let params = lodash.merge({}, defParams, detail.params || {}) - let basicDmg = dmgDetail.basicRet - lodash.forEach(mainAttr, (reduceAttr) => { - dmgDetail.attr.push(attrMap[reduceAttr]) - let rowData = [] - lodash.forEach(mainAttr, (incAttr) => { - if (incAttr === reduceAttr) { - rowData.push({ type: 'na' }) - return - } - let { attr } = Calc.calcAttr({ - originalAttr, - buffs, - meta, - params, - incAttr, - reduceAttr, - talent: detail.talent || '' - }) - let ds = lodash.merge({ talent }, Calc.getDs(attr, meta, params)) - let dmg = Calc.getDmgFn({ ds, attr, profile, enemyLv }) - if (detail.dmg) { - let dmgCalcRet = detail.dmg(ds, dmg) - rowData.push({ - type: dmgCalcRet.avg === basicDmg.avg ? 'avg' : (dmgCalcRet.avg > basicDmg.avg ? 'gt' : 'lt'), - ...dmgCalcRet - }) - } - }) - dmgRet.push(rowData) - }) - } - - return { - ret, - msg, - dmgRet, - enemyName, - dmgCfg: dmgDetail - } } } diff --git a/models/profile-lib/DmgAttr.js b/models/profile-lib/DmgAttr.js new file mode 100644 index 00000000..cdd7f288 --- /dev/null +++ b/models/profile-lib/DmgAttr.js @@ -0,0 +1,184 @@ +/* +* 伤害计算 - 属性计算 +* */ +import { attrMap, eleMap } from './calc-meta.js' +import lodash from 'lodash' +import Mastery from './Mastery.js' +import { Format } from '../../components/index.js' + +let DmgAttr = { + // 计算并返回指定属性值 + getAttrValue (ds) { + return (ds.base || 0) + (ds.plus || 0) + ((ds.base || 0) * (ds.pct || 0) / 100) + }, + + // 获取profile对应attr属性值 + getAttr ({ id, attr, weapon, char }) { + let ret = {} + + // 基础属性 + lodash.forEach('atk,def,hp'.split(','), (key) => { + ret[key] = { + base: attr[`${key}Base`] * 1 || 0, + plus: attr[key] * 1 - attr[`${key}Base`] * 1 || 0, + pct: 0 + } + }) + + lodash.forEach('mastery,recharge,cpct,cdmg,heal,dmg,phy'.split(','), (key) => { + ret[key] = { + base: attr[key] * 1 || 0, // 基础值 + plus: 0, // 加成值 + pct: 0, // 百分比加成 + inc: 0 // 提高:护盾增效&治疗增效 + } + }) + + // 技能属性记录 + lodash.forEach('a,a2,a3,e,q'.split(','), (key) => { + ret[key] = { + pct: 0, // 倍率加成 + multi: 0, // 独立倍率乘区加成,宵宫E等 + + plus: 0, // 伤害值提高 + dmg: 0, // 伤害提高 + cpct: 0, // 暴击提高 + cdmg: 0, // 爆伤提高 + + def: 0, // 防御降低 + ignore: 0 // 无视防御 + } + }) + + ret.enemy = { + def: 0, // 降低防御 + ignore: 0, // 无视防御 + phy: 0 // 物理防御 + } + + ret.shield = { + base: 100, // 基础 + plus: 0, // 护盾强效 + inc: 100 // 吸收倍率 + } + + ret.weapon = weapon // 武器 + ret.weaponType = char.weaponType // 武器类型 + ret.element = eleMap[char.elem.toLowerCase()] // 元素类型 + ret.refine = ((weapon.affix || ret.refine || 1) * 1 - 1) || 0 // 武器精炼 + ret.multi = 0 // 倍率独立乘区 + ret.zf = 0 // 蒸发 + ret.rh = 0 // 融化 + ret.gd = 0 // 感电 + ret.ks = 0 // 扩散 + ret.kx = 0 // 敌人抗性降低 + ret.fykx = 0 // 敌人反应抗性降低 + + return ret + }, + + // 获取数据集 + getDs (attr, meta, params) { + return { + ...meta, + attr, + params, + refine: attr.refine, + weaponType: attr.weaponType, + weapon: attr.weapon, + element: eleMap[attr.element] || attr.element, + // 计算属性 + calc: DmgAttr.getAttrValue + } + }, + + // 计算属性 + calcAttr ({ originalAttr, buffs, meta, params = {}, incAttr = '', reduceAttr = '', talent = '' }) { + let attr = lodash.merge({}, originalAttr) + let msg = [] + + if (incAttr && attrMap[incAttr]) { + let aCfg = attrMap[incAttr] + attr[incAttr][aCfg.type] += aCfg.val + } + if (reduceAttr && attrMap[reduceAttr]) { + let aCfg = attrMap[reduceAttr] + attr[reduceAttr][aCfg.type] -= aCfg.val + } + + lodash.forEach(buffs, (buff) => { + let ds = DmgAttr.getDs(attr, meta, params) + + ds.currentTalent = talent + + let mKey = { + zf: '蒸发', + rh: '融化', + ks: '扩散' + } + if (lodash.isString(buff) && mKey[buff]) { + buff = { + zf: { + title: `元素精通:${mKey[buff]}伤害提高[${buff}]%`, + mastery: buff + } + } + } + + // 如果存在rule,则进行计算 + if (buff.check && !buff.check(ds)) { + return + } + if (buff.cons) { + if (ds.cons * 1 < buff.cons * 1) { + return + } + } + + let title = buff.title + + if (buff.mastery) { + let mastery = Math.max(0, attr.mastery.base + attr.mastery.plus) + // let masteryNum = 2.78 * mastery / (mastery + 1400) * 100; + buff.data = buff.data || {} + lodash.forEach(buff.mastery.split(','), (key) => { + buff.data[key] = Mastery.getMultiple(key, mastery) + // buff.data[key] = masteryNum; + }) + } + + lodash.forEach(buff.data, (val, key) => { + if (lodash.isFunction(val)) { + val = val(ds) + } + + title = title.replace(`[${key}]`, Format.comma(val, 1)) + // 技能提高 + let tRet = /^(a|a2|a3|e|q)(Def|Ignore|Dmg|Plus|Pct|Cpct|Cdmg|Multi)$/.exec(key) + if (tRet) { + attr[tRet[1]][tRet[2].toLowerCase()] += val * 1 || 0 + return + } + let aRet = /^(hp|def|atk|mastery|cpct|cdmg|heal|recharge|dmg|phy|shield)(Plus|Pct|Inc)?$/.exec(key) + if (aRet) { + attr[aRet[1]][aRet[2] ? aRet[2].toLowerCase() : 'plus'] += val * 1 || 0 + return + } + if (key === 'enemyDef') { + attr.enemy.def += val * 1 || 0 + return + } + + if (['zf', 'rh', 'kx', 'gd', 'ks', 'fykx'].includes(key)) { + attr[key] += val * 1 || 0 + } + }) + msg.push(title) + }) + + return { + attr, msg + } + } +} +export default DmgAttr diff --git a/models/profile-lib/DmgBuffs.js b/models/profile-lib/DmgBuffs.js new file mode 100644 index 00000000..867a06e9 --- /dev/null +++ b/models/profile-lib/DmgBuffs.js @@ -0,0 +1,87 @@ +/* +* 伤害计算 - Buff计算 +* */ +import lodash from 'lodash' + +let weaponBuffs = {} +let artisBuffs = {} + +let DmgBuffs = { + // 圣遗物Buff + getArtisBuffs (artis) { + let buffs = artisBuffs + let setMap = {} + lodash.forEach(artis, (arti, name) => { + if (lodash.isNumber(arti)) { + setMap[name] = arti + } else { + if (arti && arti.set) { + let name = arti.set + setMap[name] = (setMap[name] || 0) + 1 + } + } + }) + let retBuffs = [] + lodash.forEach(setMap, (count, setName) => { + if (count >= 2 && buffs[setName + 2]) { + retBuffs.push(buffs[setName + 2]) + } + if (count >= 4 && buffs[setName + 4]) { + retBuffs.push(buffs[setName + 4]) + } + }) + return retBuffs + }, + + // 武器Buff + getWeaponBuffs (weaponName) { + let weaponCfg = weaponBuffs[weaponName] || [] + if (lodash.isPlainObject(weaponCfg)) { + weaponCfg = [weaponCfg] + } + lodash.forEach(weaponCfg, (ds) => { + if (!/:/.test(ds.title)) { + ds.title = `${weaponName}:${ds.title}` + } + if (ds.refine) { + ds.data = ds.data || {} + lodash.forEach(ds.refine, (r, key) => { + ds.data[key] = ({ refine }) => r[refine] * (ds.buffCount || 1) + }) + } + }) + return weaponCfg + }, + + getBuffs (profile, buffs = []) { + let weaponBuffs = DmgBuffs.getWeaponBuffs(profile.weapon?.name || '') + let artisBuffs = DmgBuffs.getArtisBuffs(profile.artis || {}) + buffs = lodash.concat(buffs, weaponBuffs, artisBuffs) + let mKey = { + zf: '蒸发', + rh: '融化', + ks: '扩散' + } + lodash.forEach(buffs, (buff, idx) => { + if (lodash.isString(buff) && mKey[buff]) { + buff = { + title: `元素精通:${mKey[buff]}伤害提高[${buff}]%`, + mastery: buff + } + buffs[idx] = buff + } + buff.sort = lodash.isUndefined(buff.sort) ? 1 : buff.sort + }) + buffs = lodash.sortBy(buffs, ['sort']) + return buffs + } +} + +async function init () { + const _path = `file://${process.cwd()}/plugins/miao-plugin/resources/meta` + weaponBuffs = (await import(`${_path}/weapons/calc.js`)).weapons || {} + artisBuffs = (await import(`${_path}/reliquaries/calc.js`)).buffs || {} +} + +await init() +export default DmgBuffs diff --git a/models/profile-lib/DmgCalc.js b/models/profile-lib/DmgCalc.js new file mode 100644 index 00000000..444387ab --- /dev/null +++ b/models/profile-lib/DmgCalc.js @@ -0,0 +1,162 @@ +/* +* 伤害计算 - 计算伤害 +* */ +import { eleBaseDmg } from './calc-meta.js' +import Mastery from './Mastery.js' + +let DmgCalc = { + calcRet (fnArgs = {}, data = {}) { + let { + pctNum, // 技能倍率 + talent, // 天赋类型 + ele, // 元素反应 + basicNum, // 基础数值 + mode // 模式 + } = fnArgs + let { + ds, // 数据集 + attr, // 属性 + level, // 面板数据 + enemyLv, // 敌人等级 + showDetail = false // 是否展示详情 + } = data + let calc = ds.calc + + let { atk, dmg, phy, cdmg, cpct } = attr + // 攻击区 + let atkNum = calc(atk) + + // 倍率独立乘区 + let multiNum = attr.multi / 100 + + // 增伤区 + let dmgNum = (1 + dmg.base / 100 + dmg.plus / 100) + + if (ele === 'phy') { + dmgNum = (1 + phy.base / 100 + phy.plus / 100) + } + + let cpctNum = cpct.base / 100 + cpct.plus / 100 + + // 爆伤区 + let cdmgNum = cdmg.base / 100 + cdmg.plus / 100 + + let enemyDef = attr.enemy.def / 100 + let enemyIgnore = attr.enemy.ignore / 100 + + let plusNum = 0 + + if (talent && attr[talent]) { + pctNum = pctNum / 100 + + let ds = attr[talent] + + pctNum += ds.pct / 100 + dmgNum += ds.dmg / 100 + cpctNum += ds.cpct / 100 + cdmgNum += ds.cdmg / 100 + enemyDef += ds.def / 100 + enemyIgnore += ds.ignore / 100 + multiNum += ds.multi / 100 + plusNum += ds.plus + } + + // 防御区 + let defNum = (level + 100) / ((level + 100) + (enemyLv + 100) * (1 - enemyDef) * (1 - enemyIgnore)) + + // 抗性区 + let kx = attr.kx + if (talent === 'fy') { + kx = attr.fykx + } + kx = 10 - (kx || 0) + let kNum = 0.9 + if (kx >= 0) { + kNum = (100 - kx) / 100 + } else { + kNum = 1 - kx / 200 + } + + // 反应区 + let eleNum = 1 + let eleBase = 0 + + if (ele === 'ks' || ele === 'gd') { + eleBase = eleBaseDmg[level] || 0 + } + + if (ele === 'phy') { + // do nothing + } else if (ele) { + eleNum = Mastery.getBasePct(ele, attr.element) + + if (attr[ele]) { + eleNum = eleNum * (1 + attr[ele] / 100) + } + } + + cpctNum = Math.max(0, Math.min(1, cpctNum)) + if (cpctNum === 0) { + cdmgNum = 0 + } + + let ret = {} + if (mode === 'basic') { + ret = { + dmg: basicNum * dmgNum * (1 + cdmgNum) * defNum * kNum * eleNum, + avg: basicNum * dmgNum * (1 + cpctNum * cdmgNum) * defNum * kNum * eleNum + } + } else if (eleBase) { + ret = { + avg: eleBase * kNum * eleNum + } + } else { + // 计算最终伤害 + ret = { + dmg: (atkNum * pctNum * (1 + multiNum) + plusNum) * dmgNum * (1 + cdmgNum) * defNum * kNum * eleNum, + avg: (atkNum * pctNum * (1 + multiNum) + plusNum) * dmgNum * (1 + cpctNum * cdmgNum) * defNum * kNum * eleNum + } + } + if (showDetail) { + console.log(attr, { atkNum, pctNum, multiNum, plusNum, dmgNum, cpctNum, cdmgNum, defNum, eleNum, kNum }, ret) + } + + return ret + }, + getDmgFn (data) { + let { showDetail, attr } = data + let { calc } = attr + + let dmgFn = function (pctNum = 0, talent = false, ele = false, basicNum = 0, mode = 'talent') { + return DmgCalc.calcRet({ pctNum, talent, ele, basicNum, mode }, data) + } + + dmgFn.basic = function (basicNum = 0, talent = false, ele = false) { + return dmgFn(0, talent, ele, basicNum, 'basic') + } + + // 计算治疗 + dmgFn.heal = function (num) { + if (showDetail) { + console.log(num, calc(attr.heal), attr.heal.inc) + } + return { + avg: num * (1 + calc(attr.heal) / 100 + attr.heal.inc / 100) + } + } + + // 计算护盾 + dmgFn.shield = function (num) { + return { + avg: num * (calc(attr.shield) / 100) * (attr.shield.inc / 100) + } + } + // 扩散方法 + dmgFn.ks = function () { + return dmgFn(0, 'fy', 'ks') + } + + return dmgFn + } +} +export default DmgCalc diff --git a/tools/dmg-calc-tool.js b/tools/dmg-calc-tool.js index f6098a72..03987ea4 100644 --- a/tools/dmg-calc-tool.js +++ b/tools/dmg-calc-tool.js @@ -1,10 +1,10 @@ -import Calc from '../models/profile-lib/Calc.js' -import { Character } from '../models/index.js' -import Miao from '../components/profile-data/miao.js' +import { Data } from '../components/index.js' +import { ProfileDmg } from '../models/index.js' export async function calcDmg (inputData, enemyLv = 86) { - let profile = Miao.getAvatarDetail(inputData) - console.log(profile) - let char = Character.get(profile) - return await Calc.calcData({ profile, char, enemyLv }) -} \ No newline at end of file + let dmg = new ProfileDmg(inputData) + let ret = await dmg.calcData({ enemyLv }) + ret = Data.getData(ret, 'ret,msg,enemyName') + ret.enemyLevel = enemyLv + return ret +}