调整部分伤害计算底层逻辑

This commit is contained in:
yoimiya-kokomi 2022-08-23 04:53:31 +08:00
parent e59b3ded99
commit f8c03dffcc
13 changed files with 578 additions and 496 deletions

View File

@ -1,8 +1,8 @@
import fs from 'fs' import fs from 'fs'
import lodash from 'lodash' import lodash from 'lodash'
import { Character, ProfileReq, ProfileData } from '../models/index.js'
import Miao from './profile-data/miao.js' import Miao from './profile-data/miao.js'
import Enka from './profile-data/enka.js' import Enka from './profile-data/enka.js'
import { Character, ProfileReq, ProfileData } from '../models/index.js'
const _path = process.cwd() const _path = process.cwd()
const userPath = `${_path}/data/UserData/` const userPath = `${_path}/data/UserData/`

View File

@ -92,7 +92,7 @@ export const characters = {
gongji: ['普契涅拉', '公鸡', '鸽子'], gongji: ['普契涅拉', '公鸡', '鸽子'],
duizhang: ['卡皮塔诺', '队长'], duizhang: ['卡皮塔诺', '队长'],
nilu: ['妮露'], nilu: ['妮露'],
nahida: ['纳西妲', '草神'] nahida: ['纳西妲', '草神', '小吉祥', '小吉祥草王', '花神', '草萝莉']
} }
/* /*
@ -103,14 +103,14 @@ export const wifeData = {
// 老婆&女朋友:成女、少女 // 老婆&女朋友:成女、少女
girlfriend: `琴, 丽莎, 荧, 芭芭拉, 安柏, 香菱, 北斗, 凝光, 菲谢尔, 诺艾尔, 甘雨, 莫娜, 刻晴, 砂糖, 辛焱, 罗莎莉亚, 胡桃, girlfriend: `琴, 丽莎, 荧, 芭芭拉, 安柏, 香菱, 北斗, 凝光, 菲谢尔, 诺艾尔, 甘雨, 莫娜, 刻晴, 砂糖, 辛焱, 罗莎莉亚, 胡桃,
烟绯, 优菈, 神里绫华, 宵宫, 雷电将军, 珊瑚宫心海, 九条裟罗, 八重神子, 埃洛伊, 申鹤, 云堇, 夜兰, 久岐忍, 柯莱, 多莉, 伐难, 烟绯, 优菈, 神里绫华, 宵宫, 雷电将军, 珊瑚宫心海, 九条裟罗, 八重神子, 埃洛伊, 申鹤, 云堇, 夜兰, 久岐忍, 柯莱, 多莉, 伐难,
女士, 萍姥姥, 柯莱, 多莉, 仆人, 少女, 妮露, 纳西妲`, 女士, 萍姥姥, 柯莱, 多莉, 仆人, 少女, 妮露`,
// 老公&男朋友:成男、少男 // 老公&男朋友:成男、少男
boyfriend: `空, 凯亚, 迪卢克, 雷泽, 温迪, 行秋, 魈, 钟离, 班尼特, 达达利亚, 重云, 阿贝多, 枫原万叶, 托马, 五郎, 荒泷一斗, boyfriend: `空, 凯亚, 迪卢克, 雷泽, 温迪, 行秋, 魈, 钟离, 班尼特, 达达利亚, 重云, 阿贝多, 枫原万叶, 托马, 五郎, 荒泷一斗,
鹿野院平藏, 神里绫人, 提纳里, 散兵, 白术, 提纳里, 富人, 博士, 丑角, 公鸡, 队长`, 鹿野院平藏, 神里绫人, 提纳里, 散兵, 白术, 提纳里, 富人, 博士, 丑角, 公鸡, 队长`,
// 女儿:萝莉 // 女儿:萝莉
daughter: '可莉, 七七, 迪奥娜, 早柚, 派蒙, 瑶瑶', daughter: '可莉, 七七, 迪奥娜, 早柚, 派蒙, 瑶瑶, 纳西妲',
// 儿子:正太 // 儿子:正太
son: '' son: ''
@ -126,7 +126,7 @@ export const abbr = {
荒泷一斗: '一斗', 荒泷一斗: '一斗',
八重神子: '八重', 八重神子: '八重',
九条裟罗: '九条', 九条裟罗: '九条',
罗莎莉亚: '罗莎', 罗莎莉亚: '修女',
鹿野院平藏: '平藏', 鹿野院平藏: '平藏',
松籁响起之时: '松籁', 松籁响起之时: '松籁',

View File

@ -129,6 +129,30 @@ export default class ProfileArtis extends Base {
return ret 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 () { getCharCfg () {
let char = Character.get(this.charid) let char = Character.get(this.charid)
let name = char.name let name = char.name

View File

@ -116,7 +116,10 @@ export default class ProfileData extends Base {
// 计算当前profileData的伤害信息 // 计算当前profileData的伤害信息
async calcDmg ({ enemyLv = 91, mode = 'profile', dmgIdx = 0 }) { async calcDmg ({ enemyLv = 91, mode = 'profile', dmgIdx = 0 }) {
if (!this.dmg) { 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 }) return await this.dmg.calcData({ enemyLv, mode, dmgIdx })
} }

View File

@ -1,64 +1,117 @@
import fs from 'fs'
import lodash from 'lodash'
import Base from './Base.js' import Base from './Base.js'
import { Character } from './index.js' import { Character } from './index.js'
import lodash from 'lodash'
import { attrMap } from './profile-lib/calc-meta.js' 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 { export default class ProfileDmg extends Base {
constructor (profile = false) { constructor (profile = {}) {
super() super()
this.profile = profile this.profile = profile
if (profile && profile.id) { if (profile && profile.id) {
let { id } = profile let { id } = profile
this.char = Character.get(id) 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 }) { async calcData ({ enemyLv = 91, mode = 'profile', dmgIdx = 0 }) {
if (!this.char || !this.profile) { if (!this.char || !this.profile) {
return false return false
} }
let { profile, char } = this let { profile } = this
let charCalcData = await Calc.getCharCalcRule(this.char.name) let charCalcData = await this.getCalcRule()
if (!charCalcData) { if (!charCalcData) {
return false return false
} }
let talent = Calc.talent(profile, char) let { buffs, details, defParams, mainAttr, defDmgIdx, enemyName } = charCalcData
let talent = this.talent()
let meta = { let meta = {
cons: profile.cons * 1, cons: profile.cons * 1,
talent talent
} }
let { id, weapon, attr } = profile
let { buffs, details, defParams, mainAttr, defDmgIdx, enemyName } = charCalcData
defParams = defParams || {} 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) buffs = this.getBuffs(buffs)
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 = lodash.sortBy(buffs, ['sort']) let { msg } = DmgAttr.calcAttr({ originalAttr, buffs, meta, params: defParams || {} })
let { msg } = Calc.calcAttr({ originalAttr, buffs, meta, params: defParams || {} })
let ret = [] let ret = []
let detailMap = [] let detailMap = []
@ -67,21 +120,21 @@ export default class ProfileDmg extends Base {
lodash.forEach(details, (detail, detailSysIdx) => { lodash.forEach(details, (detail, detailSysIdx) => {
if (lodash.isFunction(detail)) { if (lodash.isFunction(detail)) {
let { attr } = Calc.calcAttr({ originalAttr, buffs, meta }) let { attr } = DmgAttr.calcAttr({ originalAttr, buffs, meta })
let ds = lodash.merge({ talent }, Calc.getDs(attr, meta)) let ds = lodash.merge({ talent }, DmgAttr.getDs(attr, meta))
detail = detail({ ...ds, attr, profile }) detail = detail({ ...ds, attr, profile })
} }
let params = lodash.merge({}, defParams, detail.params || {}) let params = lodash.merge({}, defParams, detail.params || {})
let { attr } = Calc.calcAttr({ originalAttr, buffs, meta, params, talent: detail.talent || '' }) let { attr } = DmgAttr.calcAttr({ originalAttr, buffs, meta, params, talent: detail.talent || '' })
if (detail.check && !detail.check(Calc.getDs(attr, meta, params))) { if (detail.check && !detail.check(DmgAttr.getDs(attr, meta, params))) {
return return
} }
if (detail.cons && meta.cons < detail.cons * 1) { if (detail.cons && meta.cons < detail.cons * 1) {
return 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 let basicDmgRet
if (detail.dmg) { if (detail.dmg) {
@ -123,7 +176,7 @@ export default class ProfileDmg extends Base {
rowData.push({ type: 'na' }) rowData.push({ type: 'na' })
return return
} }
let { attr } = Calc.calcAttr({ let { attr } = DmgAttr.calcAttr({
originalAttr, originalAttr,
buffs, buffs,
meta, meta,
@ -132,8 +185,8 @@ export default class ProfileDmg extends Base {
reduceAttr, reduceAttr,
talent: detail.talent || '' talent: detail.talent || ''
}) })
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 }) let dmg = DmgCalc.getDmgFn({ ds, attr, level: profile.level, enemyLv })
if (detail.dmg) { if (detail.dmg) {
let dmgCalcRet = detail.dmg(ds, dmg) let dmgCalcRet = detail.dmg(ds, dmg)
rowData.push({ rowData.push({

View File

@ -63,8 +63,14 @@ export default class ProfileReq extends Base {
this.msg('开始获取数据,可能会需要一定时间~') this.msg('开始获取数据,可能会需要一定时间~')
await sleep(100) await sleep(100)
// 发起请求 // 发起请求
let data = {}
try {
let req = await fetch(reqParam.url, reqParam.params || {}) let req = await fetch(reqParam.url, reqParam.params || {})
let data = await req.json() data = await req.json()
} catch (e) {
console.log('面板请求错误', e)
data = {}
}
data = await Serv.response(data, this) data = await Serv.response(data, this)
// 设置CD // 设置CD
cdTime = Serv.getCdTime(data) cdTime = Serv.getCdTime(data)

View File

@ -1,3 +1,4 @@
import Base from './Base.js'
import Character from './Character.js' import Character from './Character.js'
import Artifact from './Artifact.js' import Artifact from './Artifact.js'
import Avatars from './Avatars.js' import Avatars from './Avatars.js'
@ -8,4 +9,4 @@ import ProfileData from './ProfileData.js'
import ProfileArtis from './ProfileArtis.js' import ProfileArtis from './ProfileArtis.js'
import ProfileDmg from './ProfileDmg.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 }

View File

@ -1,321 +1,9 @@
import fs from 'fs'
import lodash from 'lodash' import lodash from 'lodash'
import { Format } from '../../components/index.js'
import { Character } from '../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' import Mastery from './Mastery.js'
let Calc = { 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 }) { getDmgFn ({ ds, attr, profile, enemyLv, showDetail = false }) {
let { calc } = ds let { calc } = ds
@ -449,132 +137,6 @@ let Calc = {
} }
return dmgFn 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
}
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,10 +1,10 @@
import Calc from '../models/profile-lib/Calc.js' import { Data } from '../components/index.js'
import { Character } from '../models/index.js' import { ProfileDmg } from '../models/index.js'
import Miao from '../components/profile-data/miao.js'
export async function calcDmg (inputData, enemyLv = 86) { export async function calcDmg (inputData, enemyLv = 86) {
let profile = Miao.getAvatarDetail(inputData) let dmg = new ProfileDmg(inputData)
console.log(profile) let ret = await dmg.calcData({ enemyLv })
let char = Character.get(profile) ret = Data.getData(ret, 'ret,msg,enemyName')
return await Calc.calcData({ profile, char, enemyLv }) ret.enemyLevel = enemyLv
return ret
} }