新增柏妮思伤害计算等

This commit is contained in:
UCPr 2025-01-14 21:42:23 +08:00
parent 6573f1d600
commit b51acca73e
19 changed files with 1053 additions and 164 deletions

View file

@ -22,26 +22,27 @@ export var buffTypeEnum;
(function (buffTypeEnum) { (function (buffTypeEnum) {
// 通用乘区 // 通用乘区
buffTypeEnum[buffTypeEnum["\u653B\u51FB\u529B"] = 0] = "\u653B\u51FB\u529B"; buffTypeEnum[buffTypeEnum["\u653B\u51FB\u529B"] = 0] = "\u653B\u51FB\u529B";
buffTypeEnum[buffTypeEnum["\u589E\u4F24"] = 1] = "\u589E\u4F24"; buffTypeEnum[buffTypeEnum["\u500D\u7387"] = 1] = "\u500D\u7387";
buffTypeEnum[buffTypeEnum["\u6613\u4F24"] = 2] = "\u6613\u4F24"; buffTypeEnum[buffTypeEnum["\u589E\u4F24"] = 2] = "\u589E\u4F24";
buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u6297\u6027"] = 3] = "\u65E0\u89C6\u6297\u6027"; buffTypeEnum[buffTypeEnum["\u6613\u4F24"] = 3] = "\u6613\u4F24";
buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u9632\u5FA1"] = 4] = "\u65E0\u89C6\u9632\u5FA1"; buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u6297\u6027"] = 4] = "\u65E0\u89C6\u6297\u6027";
buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u503C"] = 5] = "\u7A7F\u900F\u503C"; buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u9632\u5FA1"] = 5] = "\u65E0\u89C6\u9632\u5FA1";
buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u7387"] = 6] = "\u7A7F\u900F\u7387"; buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u503C"] = 6] = "\u7A7F\u900F\u503C";
buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u7387"] = 7] = "\u7A7F\u900F\u7387";
// 直伤乘区 // 直伤乘区
buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u7387"] = 7] = "\u66B4\u51FB\u7387"; buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u7387"] = 8] = "\u66B4\u51FB\u7387";
buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u4F24\u5BB3"] = 8] = "\u66B4\u51FB\u4F24\u5BB3"; buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u4F24\u5BB3"] = 9] = "\u66B4\u51FB\u4F24\u5BB3";
// 异常乘区 // 异常乘区
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u7CBE\u901A"] = 9] = "\u5F02\u5E38\u7CBE\u901A"; buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u7CBE\u901A"] = 10] = "\u5F02\u5E38\u7CBE\u901A";
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u589E\u4F24"] = 10] = "\u5F02\u5E38\u589E\u4F24"; buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u589E\u4F24"] = 11] = "\u5F02\u5E38\u589E\u4F24";
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u7387"] = 11] = "\u5F02\u5E38\u66B4\u51FB\u7387"; buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u7387"] = 12] = "\u5F02\u5E38\u66B4\u51FB\u7387";
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"] = 12] = "\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"; buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"] = 13] = "\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3";
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"] = 13] = "\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"; buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"] = 14] = "\u5F02\u5E38\u6301\u7EED\u65F6\u95F4";
// 其他属性一般不直接影响伤害但可能用于buff是否生效判断/转模 // 其他属性一般不直接影响伤害但可能用于buff是否生效判断/转模
buffTypeEnum[buffTypeEnum["\u751F\u547D\u503C"] = 14] = "\u751F\u547D\u503C"; buffTypeEnum[buffTypeEnum["\u751F\u547D\u503C"] = 15] = "\u751F\u547D\u503C";
buffTypeEnum[buffTypeEnum["\u9632\u5FA1\u529B"] = 15] = "\u9632\u5FA1\u529B"; buffTypeEnum[buffTypeEnum["\u9632\u5FA1\u529B"] = 16] = "\u9632\u5FA1\u529B";
buffTypeEnum[buffTypeEnum["\u51B2\u51FB\u529B"] = 16] = "\u51B2\u51FB\u529B"; buffTypeEnum[buffTypeEnum["\u51B2\u51FB\u529B"] = 17] = "\u51B2\u51FB\u529B";
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u638C\u63A7"] = 17] = "\u5F02\u5E38\u638C\u63A7"; buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u638C\u63A7"] = 18] = "\u5F02\u5E38\u638C\u63A7";
})(buffTypeEnum || (buffTypeEnum = {})); })(buffTypeEnum || (buffTypeEnum = {}));
let depth = 0, weakMapCheck = new WeakMap(); let depth = 0, weakMapCheck = new WeakMap();
/** /**
@ -116,8 +117,8 @@ export class BuffManager {
param.range = param.range.filter(r => typeof r === 'string'); param.range = param.range.filter(r => typeof r === 'string');
if (!param.range.length) if (!param.range.length)
continue; continue;
// buff作用范围向后覆盖 // buff作用范围向后覆盖满足伤害类型range中任意一个即可
else if (!param.range.every(ST => buffRange.some(BT => ST.startsWith(BT)))) else if (!param.range.some(ST => buffRange.some(BT => ST.startsWith(BT))))
return false; return false;
else else
continue; continue;

View file

@ -31,7 +31,7 @@ export type buffSource = 'Weapon' | 'Set' | 'Rank' | 'Talent' | 'Addition' | 'Sk
export enum buffTypeEnum { export enum buffTypeEnum {
// 通用乘区 // 通用乘区
, , , , , 穿, 穿, , , , , , , 穿, 穿,
// 直伤乘区 // 直伤乘区
, , , ,
// 异常乘区 // 异常乘区
@ -172,8 +172,8 @@ export class BuffManager {
if (!buffRange || !param.range) continue // 对任意类型生效 if (!buffRange || !param.range) continue // 对任意类型生效
param.range = param.range.filter(r => typeof r === 'string') param.range = param.range.filter(r => typeof r === 'string')
if (!param.range.length) continue if (!param.range.length) continue
// buff作用范围向后覆盖 // buff作用范围向后覆盖满足伤害类型range中任意一个即可
else if (!param.range.every(ST => buffRange.some(BT => ST.startsWith(BT)))) return false else if (!param.range.some(ST => buffRange.some(BT => ST.startsWith(BT)))) return false
else continue else continue
} else if (key === 'element') { } else if (key === 'element') {
if (!buff.element || !param.element) continue // 对任意属性生效 if (!buff.element || !param.element) continue // 对任意属性生效
@ -235,6 +235,7 @@ export class BuffManager {
filter<T extends keyof buff>(type: T, value: buff[T]): buff[] filter<T extends keyof buff>(type: T, value: buff[T]): buff[]
/** /**
* **** buff * **** buff
* - range数组的筛选
*/ */
filter(obj: { [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: buff[key] } & { element: element }, calc?: Calculator): buff[] filter(obj: { [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: buff[key] } & { element: element }, calc?: Calculator): buff[]
/** /**

View file

@ -0,0 +1,591 @@
import type { BuffManager, anomaly, buff, element } from './BuffManager.ts'
import type { ZZZAvatarInfo } from '../avatar.js'
import { getMapData } from '../../utils/file.js'
import { elementEnum, anomalyEnum } from './BuffManager.js'
import { charData } from './avatar.js'
import _ from 'lodash'
/** 技能类型 */
export interface skill {
/** 技能名,唯一 */
name: string
/** 技能类型,唯一,参考技能类型命名标准 */
type: string
/** 属性类型,不指定时,默认取角色属性 */
element: element
/** 技能倍率数组于character/角色名/data.json中自动获取该json中不存在相应数据时可填写该值以填写值为准 */
skillMultiplier?: number[]
/** 指定固定技能倍率 */
fixedMultiplier?: number
/** 角色面板伤害统计中是否隐藏显示 */
isHide?: boolean
/**
*
*
* X"Y使Y的类型
*
*/
redirect?: string
/** 禁用伤害计算cache */
banCache?: boolean
/** 自定义计算逻辑 */
dmg?: (calc: Calculator) => damage
/** 额外处理 */
before?: ({ skill, avatar, usefulBuffs, calc }: {
/** 技能自身 */
skill: skill
avatar: ZZZAvatarInfo
usefulBuffs: buff[]
calc: Calculator
}) => void
after?: ({ avatar, damage, calc, usefulBuffs }: {
avatar: ZZZAvatarInfo
damage: damage
calc: Calculator
usefulBuffs: buff[]
}) => void
}
export interface damage {
/** 技能类型 */
skill: skill
/** 各乘区详细数据 */
detail: {
/** 伤害倍率 */
Multiplier: number
/** 攻击力 */
ATK: number
/** 暴击率 */
CRITRate: number
/** 暴伤伤害 */
CRITDMG: number
/** 暴击区期望 */
CriticalArea: number
/** 增伤区 */
BoostArea: number
/** 易伤区 */
VulnerabilityArea: number
/** 抗性区 */
ResistanceArea: number
/** 防御区 */
DefenceArea: number
/** 异常暴击率 */
AnomalyCRITRate: number
/** 异常暴击伤害 */
AnomalyCRITDMG: number
/** 异常精通区 */
AnomalyProficiencyArea: number
/** 异常增伤区 */
AnomalyBoostArea: number
/** 等级区 */
LevelArea: number
}
/** 伤害结果 */
result: {
/** 暴击伤害 */
critDMG: number
/** 期望伤害 */
expectDMG: number
}
add?: (damage: string | damage) => void
fnc?: (fnc: (n: number) => number) => void
x?: (n: number) => void
}
const elementType2element = (elementType: number) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]] as element
const AnomalyData = getMapData('AnomalyData') as {
name: string,
element: element,
element_type: number,
sub_element_type: number,
duration: number,
interval: number,
multiplier: number,
discover?: {
multiplier: number,
fixed_multiplier: number
}
}[]
interface enemy {
/** 等级 */
level: number
/** 1级基础防御力 */
basicDEF: number
/** 抗性 */
resistance: number
}
export class Calculator {
readonly buffM: BuffManager
readonly avatar: ZZZAvatarInfo
readonly skills: skill[] = []
private cache: { [key in buff['type']]?: number } = {}
private damageCache: { [type: string]: damage } = {}
defaultSkill: { [key in keyof skill]?: skill[key] } = {}
enemy: enemy
constructor(buffM: BuffManager) {
this.buffM = buffM
this.avatar = this.buffM.avatar
this.enemy = {
level: this.avatar.level,
basicDEF: 50,
resistance: 0.2
}
}
get initial_properties() {
return this.avatar.initial_properties
}
/** 定义敌方属性 */
defEnemy<T extends keyof enemy>(key: T, value: enemy[T]): void
defEnemy(enemy: enemy): void
defEnemy<T extends keyof enemy>(param: T | enemy, value?: enemy[T]) {
if (typeof param === 'string' && value !== undefined) {
this.enemy[param] = value
} else if (typeof param === 'object') {
_.merge(this.enemy, param)
}
}
/** 注册skill */
new(skill: skill): skill[]
/** 注册skills */
new(skills: skill[]): skill[]
new(skill: skill | skill[]) {
if (Array.isArray(skill)) {
skill.forEach(s => this.new(s))
return this.skills
}
skill = _.merge({
...this.defaultSkill
}, skill)
if (!skill.element) skill.element = elementType2element(this.avatar.element_type)
if (!skill.name || !skill.type) return logger.warn('无效skill', skill)
this.skills.push(skill)
return this.skills
}
/**
*
* @param type
*/
calc_skill(type: skill['type']): damage
/**
*
* @param skill
*/
calc_skill(skill: skill): damage
calc_skill(skill: skill['type'] | skill) {
if (typeof skill === 'string') {
const MySkill = this.skills.find(s => s.type === skill)
if (!MySkill) return
return this.calc_skill(MySkill)
}
if (!skill.banCache && this.damageCache[skill.type]) return this.damageCache[skill.type]
logger.debug(`${logger.green(skill.type)}${skill.name}伤害计算:`)
if (skill.dmg) {
const dmg = skill.dmg(this)
logger.debug('自定义计算最终伤害:', dmg.result)
return dmg
}
this.cache = {}
/** 缩小筛选范围 */
const usefulBuffs = this.buffM.filter({
element: skill.element,
range: skill.redirect ? [skill.type, skill.redirect] : [skill.type]
}, this)
if (skill.before) skill.before({ skill, avatar: this.avatar, usefulBuffs, calc: this })
let Multiplier = 0
const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number'
if (skill.fixedMultiplier) Multiplier = skill.fixedMultiplier
else if (isAnomaly) {
Multiplier = (
skill.type === '紊乱' ?
this.get_DiscoverMultiplier(skill) :
this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0)
) || 0
} else {
if (skill.skillMultiplier) Multiplier = skill.skillMultiplier[this.get_SkillLevel(skill.type[0]) - 1]
else Multiplier = this.get_Multiplier(skill.type)
}
const ExtraMultiplier = this.get_ExtraMultiplier(skill, usefulBuffs)
Multiplier += ExtraMultiplier
if (!Multiplier) return logger.warn('技能倍率缺失:', skill)
if (ExtraMultiplier) logger.debug(`最终倍率:${Multiplier}`)
const ATK = this.get_ATK(skill, usefulBuffs)
let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0
let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0
let CriticalArea = 0
if (isAnomaly) {
AnomalyProficiencyArea = this.get_AnomalyProficiencyArea(skill, usefulBuffs)
AnomalyBoostArea = this.get_AnomalyBoostArea(skill, usefulBuffs)
LevelArea = this.get_LevelArea()
AnomalyCRITRate = this.get_AnomalyCRITRate(skill, usefulBuffs)
AnomalyCRITDMG = this.get_AnomalyCRITDMG(skill, usefulBuffs)
CriticalArea = 1 + AnomalyCRITRate * (AnomalyCRITDMG - 1)
} else {
CRITRate = this.get_CRITRate(skill, usefulBuffs)
CRITDMG = this.get_CRITDMG(skill, usefulBuffs)
CriticalArea = 1 + CRITRate * (CRITDMG - 1)
}
logger.debug(`暴击期望:${CriticalArea}`)
const BoostArea = this.get_BoostArea(skill, usefulBuffs)
const VulnerabilityArea = this.get_VulnerabilityArea(skill, usefulBuffs)
const ResistanceArea = this.get_ResistanceArea(skill, usefulBuffs)
const DefenceArea = this.get_DefenceArea(skill, usefulBuffs)
const result: damage['result'] = isAnomaly ?
{
critDMG: AnomalyCRITRate ? ATK * Multiplier * AnomalyCRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0,
expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea
} : {
critDMG: ATK * Multiplier * CRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea,
expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea
}
const detail: damage['detail'] = {
Multiplier,
ATK,
CRITRate,
CRITDMG,
CriticalArea,
BoostArea,
VulnerabilityArea,
ResistanceArea,
DefenceArea,
AnomalyCRITRate,
AnomalyCRITDMG,
AnomalyProficiencyArea,
AnomalyBoostArea,
LevelArea
}
const damage: damage = { skill, detail, result }
if (skill.after) {
damage.add = (d) => {
if (typeof d === 'string') d = this.calc_skill(d)
logger.debug('追加伤害:' + d.skill.name, d.result)
damage.result.expectDMG += d.result.expectDMG
damage.result.critDMG += d.result.critDMG
}
damage.fnc = (fnc) => {
damage.result.critDMG = fnc(damage.result.critDMG)
damage.result.expectDMG = fnc(damage.result.expectDMG)
}
damage.x = (n) => {
logger.debug('伤害系数:' + n)
damage.fnc!(v => v * n)
}
skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs })
}
logger.debug('最终伤害:', result)
if (!skill.banCache) this.damageCache[skill.type] = damage
return damage
}
calc() {
return this.skills.map(skill => {
try {
return this.calc_skill(skill)
} catch (e) {
logger.error('伤害计算错误:', e)
return
}
}).filter(v => v && !v.skill?.isHide)
}
/**
* buff参数的默认值
* @param obj
*/
default(obj: { [key in keyof skill]?: skill[key] }): void
/**
*
* @param value undefined时删除默认值
*/
default<T extends keyof skill>(type: T, value?: skill[T]): void
default<T extends keyof skill>(param: T | { [key in keyof skill]?: skill[key] }, value?: skill[T]): void {
if (typeof param === 'object') {
this.defaultSkill = param
} else {
if (value === undefined) delete this.defaultSkill[param]
else this.defaultSkill[param] = value
}
}
/**
*
* @param baseType 'A', 'E', 'C', 'R', 'T', 'L'
*/
get_SkillLevel(baseType: string) {
const id = ['A', 'E', 'C', 'R', , 'T', 'L'].indexOf(baseType)
if (id === -1) return 1
return Number(this.avatar.skills.find(({ skill_type }) => skill_type === id)?.level || 1)
}
/**
*
* @param type
*/
get_Multiplier(type: string) {
const skillLevel = this.get_SkillLevel(type[0])
logger.debug(`等级:${skillLevel}`)
const Multiplier = charData[this.avatar.id].skill[type]?.[skillLevel - 1]
logger.debug(`倍率:${Multiplier}`)
return Multiplier
}
get_AnomalyData(skill: skill) {
let a = AnomalyData.filter(({ element_type }) => element_type === this.avatar.element_type)
if (skill.type === '紊乱') a = a.filter(({ discover }) => discover)
else a = a.filter(({ name, multiplier }) => name === skill.type && multiplier)
if (a.length === 1) return a[0]
a = a.filter(({ sub_element_type }) => sub_element_type === this.avatar.sub_element_type)
return a[0]
}
/** 获取属性异常倍率 */
get_AnomalyMultiplier(skill: skill, usefulBuffs: buff[], times = 0) {
const anomalyData = this.get_AnomalyData(skill)
if (!anomalyData) return
let Multiplier = anomalyData.multiplier
if (anomalyData.duration && anomalyData.interval) {
const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration)
times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10))
Multiplier = anomalyData.multiplier * times
}
logger.debug(`倍率:${Multiplier}`)
return Multiplier
}
/** 获取紊乱倍率 */
get_DiscoverMultiplier(skill: skill) {
const anomalyData = this.get_AnomalyData(skill)
if (!anomalyData) return
const AnomalyDuration = this.get_AnomalyDuration({
...skill,
name: anomalyData.name,
type: anomalyData.name
}, this.buffM.buffs, anomalyData.duration)
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10))
const discover = anomalyData.discover!
const Multiplier = discover.fixed_multiplier + times * discover.multiplier
logger.debug(`${anomalyData.name}紊乱 倍率:${Multiplier}`)
return Multiplier
}
calc_value(value: buff['value'], buff?: buff) {
switch (typeof value) {
case 'number': return value
case 'function': return +value({ avatar: this.avatar, buffM: this.buffM, calc: this }) || 0
case 'string': return charData[this.avatar.id].buff?.[value]?.[this.get_SkillLevel(value[0]) - 1] || 0
case 'object': {
if (!Array.isArray(value) || !buff) return 0
switch (buff.source) {
case 'Weapon': return value[this.avatar.weapon.star - 1] || 0
case 'Talent':
case 'Addition': return value[this.get_SkillLevel('T') - 1] || 0
}
}
default: return 0
}
}
/**
*
* @param isRatio buff.value为数值类型且<1时按初始数值百分比提高处理
*/
get(type: buff['type'], initial: number, skill: skill, usefulBuffs: buff[] = this.buffM.buffs, isRatio = false): number {
return this.cache[type] ??= this.buffM._filter(usefulBuffs, {
element: skill?.element,
range: skill?.redirect ? [skill.type, skill.redirect] : [skill?.type],
type
}, this).reduce((previousValue, buff) => {
const { value } = buff
let add = 0
if (isRatio && typeof value === 'number' && value < 1) { // 值小于1时认为是百分比
add = value * initial
} else {
add = this.calc_value(value, buff)
if (add < 1 && isRatio && Array.isArray(value))
add *= initial
}
logger.debug(`\tBuff${buff.name}${buff.range || '全类型'}增加${add}${buff.element || ''}${type}`)
return previousValue + add
}, initial)
}
/** 攻击力 */
get_ATK(skill: skill, usefulBuffs: buff[]) {
let ATK = this.get('攻击力', this.initial_properties.ATK, skill, usefulBuffs, true)
ATK = Math.max(0, Math.min(ATK, 10000))
logger.debug(`攻击力:${ATK}`)
return ATK
}
/** 额外倍率 */
get_ExtraMultiplier(skill: skill, usefulBuffs: buff[]) {
const ExtraMultiplier = this.get('倍率', 0, skill, usefulBuffs)
ExtraMultiplier && logger.debug(`额外倍率:${ExtraMultiplier}`)
return ExtraMultiplier
}
/** 暴击率 */
get_CRITRate(skill: skill, usefulBuffs: buff[]) {
let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs)
CRITRate = Math.max(0, Math.min(CRITRate, 1))
logger.debug(`暴击率:${CRITRate}`)
return CRITRate
}
/** 暴击伤害 */
get_CRITDMG(skill: skill, usefulBuffs: buff[]) {
let CRITDMG = this.get('暴击伤害', this.initial_properties.CRITDMG + 1, skill, usefulBuffs)
CRITDMG = Math.max(0, Math.min(CRITDMG, 5))
logger.debug(`暴击伤害:${CRITDMG}`)
return CRITDMG
}
/** 增伤区 */
get_BoostArea(skill: skill, usefulBuffs: buff[]) {
const BoostArea = this.get('增伤', 1, skill, usefulBuffs)
logger.debug(`增伤区:${BoostArea}`)
return BoostArea
}
/** 易伤区 */
get_VulnerabilityArea(skill: skill, usefulBuffs: buff[]) {
const VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs)
logger.debug(`易伤区:${VulnerabilityArea}`)
return VulnerabilityArea
}
/** 抗性区 */
get_ResistanceArea(skill: skill, usefulBuffs: buff[]) {
const ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs)
logger.debug(`抗性区:${ResistanceArea}`)
return ResistanceArea
}
/** 穿透值 */
get_Pen(skill: skill, usefulBuffs: buff[]) {
let Pen = this.get('穿透值', this.initial_properties.Pen, skill, usefulBuffs)
Pen = Math.max(0, Math.min(Pen, 1000))
logger.debug(`穿透值:${Pen}`)
return Pen
}
/** 穿透率 */
get_PenRatio(skill: skill, usefulBuffs: buff[]) {
let PenRatio = this.get('穿透率', this.initial_properties.PenRatio, skill, usefulBuffs)
PenRatio = Math.max(0, Math.min(PenRatio, 2))
logger.debug(`穿透率:${PenRatio}`)
return PenRatio
}
/** 防御区 */
get_DefenceArea(skill: skill, usefulBuffs: buff[]) {
const get_base = (level: number) => Math.floor(0.1551 * Math.min(60, level) ** 2 + 3.141 * Math.min(60, level) + 47.2039)
/** 等级基数 */
const base = get_base(this.avatar.level)
/** 基础防御 */
const DEF = this.enemy.basicDEF / 50 * get_base(this.enemy.level)
const ignore_defence = this.get('无视防御', 0, skill, usefulBuffs)
const Pen = this.get_Pen(skill, usefulBuffs)
const PenRatio = this.get_PenRatio(skill, usefulBuffs)
/** 防御 */
const defence = DEF * (1 - ignore_defence)
/** 有效防御 */
const effective_defence = Math.max(0, defence * (1 - PenRatio) - Pen)
const DefenceArea = base / (effective_defence + base)
logger.debug(`防御区:${DefenceArea}`)
return DefenceArea
}
/** 等级区 */
get_LevelArea(level = this.avatar.level) {
const LevelArea = +(1 + 1 / 59 * (level - 1)).toFixed(4)
logger.debug(`等级区:${LevelArea}`)
return LevelArea
}
/** 异常精通 */
get_AnomalyProficiency(skill: skill, usefulBuffs: buff[]) {
let AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs)
AnomalyProficiency = Math.max(0, Math.min(AnomalyProficiency, 1000))
logger.debug(`异常精通:${AnomalyProficiency}`)
return AnomalyProficiency
}
/** 异常精通区 */
get_AnomalyProficiencyArea(skill: skill, usefulBuffs: buff[]) {
const AnomalyProficiency = this.get_AnomalyProficiency(skill, usefulBuffs)
const AnomalyProficiencyArea = AnomalyProficiency / 100
logger.debug(`异常精通区:${AnomalyProficiencyArea}`)
return AnomalyProficiencyArea
}
/** 异常增伤区 */
get_AnomalyBoostArea(skill: skill, usefulBuffs: buff[]) {
const AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs)
logger.debug(`异常增伤区:${AnomalyBoostArea}`)
return AnomalyBoostArea
}
/** 异常暴击率 */
get_AnomalyCRITRate(skill: skill, usefulBuffs: buff[]) {
let AnomalyCRITRate = this.get('异常暴击率', 0, skill, usefulBuffs)
AnomalyCRITRate = Math.max(0, Math.min(AnomalyCRITRate, 1))
logger.debug(`异常暴击率:${AnomalyCRITRate}`)
return AnomalyCRITRate
}
/** 异常暴击伤害 */
get_AnomalyCRITDMG(skill: skill, usefulBuffs: buff[]) {
let AnomalyCRITDMG = this.get('异常暴击伤害', 1, skill, usefulBuffs)
AnomalyCRITDMG = Math.max(0, Math.min(AnomalyCRITDMG, 5))
logger.debug(`异常暴击伤害:${AnomalyCRITDMG}`)
return AnomalyCRITDMG
}
/** 异常持续时间 */
get_AnomalyDuration(skill: skill, usefulBuffs: buff[], duration: number = 0) {
const AnomalyDuration = +this.get('异常持续时间', duration, skill, usefulBuffs).toFixed(1)
logger.debug(`异常持续时间:${AnomalyDuration}`)
return AnomalyDuration
}
/** 生命值 */
get_HP(skill: skill, usefulBuffs: buff[]) {
let HP = this.get('生命值', this.initial_properties.HP, skill, usefulBuffs, true)
HP = Math.max(0, Math.min(HP, 100000))
logger.debug(`生命值:${HP}`)
return HP
}
/** 防御力 */
get_DEF(skill: skill, usefulBuffs: buff[]) {
let DEF = this.get('防御力', this.initial_properties.DEF, skill, usefulBuffs, true)
DEF = Math.max(0, Math.min(DEF, 1000))
logger.debug(`防御力:${DEF}`)
return DEF
}
/** 冲击力 */
get_Impact(skill: skill, usefulBuffs: buff[]) {
let Impact = this.get('冲击力', this.initial_properties.Impact, skill, usefulBuffs, true)
Impact = Math.max(0, Math.min(Impact, 1000))
logger.debug(`冲击力:${Impact}`)
return Impact
}
/** 异常掌控 */
get_AnomalyMastery(skill: skill, usefulBuffs: buff[]) {
let AnomalyMastery = this.get('异常掌控', this.initial_properties.AnomalyMastery, skill, usefulBuffs, true)
AnomalyMastery = Math.max(0, Math.min(AnomalyMastery, 1000))
logger.debug(`异常掌控:${AnomalyMastery}`)
return AnomalyMastery
}
}

View file

@ -54,27 +54,30 @@ export class Calculator {
return; return;
return this.calc_skill(MySkill); return this.calc_skill(MySkill);
} }
if (this.damageCache[skill.type]) if (!skill.banCache && this.damageCache[skill.type])
return this.damageCache[skill.type]; return this.damageCache[skill.type];
logger.debug(`${logger.green(skill.type)}伤害计算:`); logger.debug(`${logger.green(skill.type)}${skill.name}伤害计算:`);
if (skill.dmg) {
const dmg = skill.dmg(this);
logger.debug('自定义计算最终伤害:', dmg.result);
return dmg;
}
this.cache = {}; this.cache = {};
if (skill.dmg)
return skill.dmg(this);
/** 缩小筛选范围 */ /** 缩小筛选范围 */
const usefulBuffs = this.buffM.filter({ const usefulBuffs = this.buffM.filter({
element: skill.element, element: skill.element,
range: [skill.type] range: skill.redirect ? [skill.type, skill.redirect] : [skill.type]
}, this); }, this);
if (skill.before) if (skill.before)
skill.before({ avatar: this.avatar, usefulBuffs, calc: this }); skill.before({ skill, avatar: this.avatar, usefulBuffs, calc: this });
let Multiplier = 0; let Multiplier = 0;
const isAnomaly = typeof anomalyEnum[skill.type] === 'number'; const isAnomaly = typeof anomalyEnum[skill.type] === 'number';
if (skill.fixedMultiplier) if (skill.fixedMultiplier)
Multiplier = skill.fixedMultiplier; Multiplier = skill.fixedMultiplier;
else if (isAnomaly) { else if (isAnomaly) {
Multiplier = (skill.type === '紊乱' ? Multiplier = (skill.type === '紊乱' ?
this.get_DiscoverMultiplier(skill, usefulBuffs) : this.get_DiscoverMultiplier(skill) :
this.get_AnomalyMultiplier(skill, usefulBuffs)) || 0; this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0)) || 0;
} }
else { else {
if (skill.skillMultiplier) if (skill.skillMultiplier)
@ -82,8 +85,12 @@ export class Calculator {
else else
Multiplier = this.get_Multiplier(skill.type); Multiplier = this.get_Multiplier(skill.type);
} }
const ExtraMultiplier = this.get_ExtraMultiplier(skill, usefulBuffs);
Multiplier += ExtraMultiplier;
if (!Multiplier) if (!Multiplier)
return logger.warn('技能倍率缺失:', skill); return logger.warn('技能倍率缺失:', skill);
if (ExtraMultiplier)
logger.debug(`最终倍率:${Multiplier}`);
const ATK = this.get_ATK(skill, usefulBuffs); const ATK = this.get_ATK(skill, usefulBuffs);
let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0; let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0;
let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0; let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0;
@ -111,9 +118,9 @@ export class Calculator {
critDMG: AnomalyCRITRate ? ATK * Multiplier * AnomalyCRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, critDMG: AnomalyCRITRate ? ATK * Multiplier * AnomalyCRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0,
expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea
} : { } : {
critDMG: ATK * Multiplier * CRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, critDMG: ATK * Multiplier * CRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea,
expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea
}; };
const detail = { const detail = {
Multiplier, Multiplier,
ATK, ATK,
@ -139,14 +146,31 @@ export class Calculator {
damage.result.expectDMG += d.result.expectDMG; damage.result.expectDMG += d.result.expectDMG;
damage.result.critDMG += d.result.critDMG; damage.result.critDMG += d.result.critDMG;
}; };
damage.fnc = (fnc) => {
damage.result.critDMG = fnc(damage.result.critDMG);
damage.result.expectDMG = fnc(damage.result.expectDMG);
};
damage.x = (n) => {
logger.debug('伤害系数:' + n);
damage.fnc(v => v * n);
};
skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs }); skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs });
} }
logger.debug('最终伤害:', result); logger.debug('最终伤害:', result);
this.damageCache[skill.type] = damage; if (!skill.banCache)
this.damageCache[skill.type] = damage;
return damage; return damage;
} }
calc() { calc() {
return this.skills.map(skill => this.calc_skill(skill)).filter(v => v && !v.skill?.isHide); return this.skills.map(skill => {
try {
return this.calc_skill(skill);
}
catch (e) {
logger.error('伤害计算错误:', e);
return;
}
}).filter(v => v && !v.skill?.isHide);
} }
default(param, value) { default(param, value) {
if (typeof param === 'object') { if (typeof param === 'object') {
@ -192,25 +216,29 @@ export class Calculator {
return a[0]; return a[0];
} }
/** 获取属性异常倍率 */ /** 获取属性异常倍率 */
get_AnomalyMultiplier(skill, usefulBuffs, anomalyData) { get_AnomalyMultiplier(skill, usefulBuffs, times = 0) {
anomalyData ||= this.get_AnomalyData(skill); const anomalyData = this.get_AnomalyData(skill);
if (!anomalyData) if (!anomalyData)
return; return;
let Multiplier = anomalyData.multiplier; let Multiplier = anomalyData.multiplier;
if (anomalyData.duration && anomalyData.interval) { if (anomalyData.duration && anomalyData.interval) {
const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration); const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration);
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)); times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10));
Multiplier = anomalyData.multiplier * times; Multiplier = anomalyData.multiplier * times;
} }
logger.debug(`倍率:${Multiplier}`); logger.debug(`倍率:${Multiplier}`);
return Multiplier; return Multiplier;
} }
/** 获取紊乱倍率 */ /** 获取紊乱倍率 */
get_DiscoverMultiplier(skill, usefulBuffs, anomalyData) { get_DiscoverMultiplier(skill) {
anomalyData ||= this.get_AnomalyData(skill); const anomalyData = this.get_AnomalyData(skill);
if (!anomalyData) if (!anomalyData)
return; return;
const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration); const AnomalyDuration = this.get_AnomalyDuration({
...skill,
name: anomalyData.name,
type: anomalyData.name
}, this.buffM.buffs, anomalyData.duration);
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)); const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10));
const discover = anomalyData.discover; const discover = anomalyData.discover;
const Multiplier = discover.fixed_multiplier + times * discover.multiplier; const Multiplier = discover.fixed_multiplier + times * discover.multiplier;
@ -241,7 +269,7 @@ export class Calculator {
get(type, initial, skill, usefulBuffs = this.buffM.buffs, isRatio = false) { get(type, initial, skill, usefulBuffs = this.buffM.buffs, isRatio = false) {
return this.cache[type] ??= this.buffM._filter(usefulBuffs, { return this.cache[type] ??= this.buffM._filter(usefulBuffs, {
element: skill?.element, element: skill?.element,
range: [skill?.type], range: skill?.redirect ? [skill.type, skill.redirect] : [skill?.type],
type type
}, this).reduce((previousValue, buff) => { }, this).reduce((previousValue, buff) => {
const { value } = buff; const { value } = buff;
@ -265,6 +293,12 @@ export class Calculator {
logger.debug(`攻击力:${ATK}`); logger.debug(`攻击力:${ATK}`);
return ATK; return ATK;
} }
/** 额外倍率 */
get_ExtraMultiplier(skill, usefulBuffs) {
const ExtraMultiplier = this.get('倍率', 0, skill, usefulBuffs);
ExtraMultiplier && logger.debug(`额外倍率:${ExtraMultiplier}`);
return ExtraMultiplier;
}
/** 暴击率 */ /** 暴击率 */
get_CRITRate(skill, usefulBuffs) { get_CRITRate(skill, usefulBuffs) {
let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs); let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs);

View file

@ -19,10 +19,21 @@ export interface skill {
fixedMultiplier?: number fixedMultiplier?: number
/** 角色面板伤害统计中是否隐藏显示 */ /** 角色面板伤害统计中是否隐藏显示 */
isHide?: boolean isHide?: boolean
/**
*
*
* X"Y使Y的类型
*
*/
redirect?: string
/** 禁用伤害计算cache */
banCache?: boolean
/** 自定义计算逻辑 */ /** 自定义计算逻辑 */
dmg?: (calc: Calculator) => damage dmg?: (calc: Calculator) => damage
/** 额外处理 */ /** 额外处理 */
before?: ({ avatar, usefulBuffs, calc }: { before?: ({ skill, avatar, usefulBuffs, calc }: {
/** 技能自身 */
skill: skill
avatar: ZZZAvatarInfo avatar: ZZZAvatarInfo
usefulBuffs: buff[] usefulBuffs: buff[]
calc: Calculator calc: Calculator
@ -77,6 +88,8 @@ export interface damage {
expectDMG: number expectDMG: number
} }
add?: (damage: string | damage) => void add?: (damage: string | damage) => void
fnc?: (fnc: (n: number) => number) => void
x?: (n: number) => void
} }
const elementType2element = (elementType: number) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]] as element const elementType2element = (elementType: number) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]] as element
@ -172,30 +185,37 @@ export class Calculator {
if (!MySkill) return if (!MySkill) return
return this.calc_skill(MySkill) return this.calc_skill(MySkill)
} }
if (this.damageCache[skill.type]) return this.damageCache[skill.type] if (!skill.banCache && this.damageCache[skill.type]) return this.damageCache[skill.type]
logger.debug(`${logger.green(skill.type)}伤害计算:`) logger.debug(`${logger.green(skill.type)}${skill.name}伤害计算:`)
if (skill.dmg) {
const dmg = skill.dmg(this)
logger.debug('自定义计算最终伤害:', dmg.result)
return dmg
}
this.cache = {} this.cache = {}
if (skill.dmg) return skill.dmg(this)
/** 缩小筛选范围 */ /** 缩小筛选范围 */
const usefulBuffs = this.buffM.filter({ const usefulBuffs = this.buffM.filter({
element: skill.element, element: skill.element,
range: [skill.type] range: skill.redirect ? [skill.type, skill.redirect] : [skill.type]
}, this) }, this)
if (skill.before) skill.before({ avatar: this.avatar, usefulBuffs, calc: this }) if (skill.before) skill.before({ skill, avatar: this.avatar, usefulBuffs, calc: this })
let Multiplier = 0 let Multiplier = 0
const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number' const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number'
if (skill.fixedMultiplier) Multiplier = skill.fixedMultiplier if (skill.fixedMultiplier) Multiplier = skill.fixedMultiplier
else if (isAnomaly) { else if (isAnomaly) {
Multiplier = ( Multiplier = (
skill.type === '紊乱' ? skill.type === '紊乱' ?
this.get_DiscoverMultiplier(skill, usefulBuffs) : this.get_DiscoverMultiplier(skill) :
this.get_AnomalyMultiplier(skill, usefulBuffs) this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0)
) || 0 ) || 0
} else { } else {
if (skill.skillMultiplier) Multiplier = skill.skillMultiplier[this.get_SkillLevel(skill.type[0]) - 1] if (skill.skillMultiplier) Multiplier = skill.skillMultiplier[this.get_SkillLevel(skill.type[0]) - 1]
else Multiplier = this.get_Multiplier(skill.type) else Multiplier = this.get_Multiplier(skill.type)
} }
const ExtraMultiplier = this.get_ExtraMultiplier(skill, usefulBuffs)
Multiplier += ExtraMultiplier
if (!Multiplier) return logger.warn('技能倍率缺失:', skill) if (!Multiplier) return logger.warn('技能倍率缺失:', skill)
if (ExtraMultiplier) logger.debug(`最终倍率:${Multiplier}`)
const ATK = this.get_ATK(skill, usefulBuffs) const ATK = this.get_ATK(skill, usefulBuffs)
let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0 let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0
let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0 let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0
@ -249,15 +269,30 @@ export class Calculator {
damage.result.expectDMG += d.result.expectDMG damage.result.expectDMG += d.result.expectDMG
damage.result.critDMG += d.result.critDMG damage.result.critDMG += d.result.critDMG
} }
damage.fnc = (fnc) => {
damage.result.critDMG = fnc(damage.result.critDMG)
damage.result.expectDMG = fnc(damage.result.expectDMG)
}
damage.x = (n) => {
logger.debug('伤害系数:' + n)
damage.fnc!(v => v * n)
}
skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs }) skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs })
} }
logger.debug('最终伤害:', result) logger.debug('最终伤害:', result)
this.damageCache[skill.type] = damage if (!skill.banCache) this.damageCache[skill.type] = damage
return damage return damage
} }
calc() { calc() {
return this.skills.map(skill => this.calc_skill(skill)).filter(v => v && !v.skill?.isHide) return this.skills.map(skill => {
try {
return this.calc_skill(skill)
} catch (e) {
logger.error('伤害计算错误:', e)
return
}
}).filter(v => v && !v.skill?.isHide)
} }
/** /**
@ -311,13 +346,13 @@ export class Calculator {
} }
/** 获取属性异常倍率 */ /** 获取属性异常倍率 */
get_AnomalyMultiplier(skill: skill, usefulBuffs: buff[], anomalyData?: typeof AnomalyData[number]) { get_AnomalyMultiplier(skill: skill, usefulBuffs: buff[], times = 0) {
anomalyData ||= this.get_AnomalyData(skill) const anomalyData = this.get_AnomalyData(skill)
if (!anomalyData) return if (!anomalyData) return
let Multiplier = anomalyData.multiplier let Multiplier = anomalyData.multiplier
if (anomalyData.duration && anomalyData.interval) { if (anomalyData.duration && anomalyData.interval) {
const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration) const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration)
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)) times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10))
Multiplier = anomalyData.multiplier * times Multiplier = anomalyData.multiplier * times
} }
logger.debug(`倍率:${Multiplier}`) logger.debug(`倍率:${Multiplier}`)
@ -325,10 +360,14 @@ export class Calculator {
} }
/** 获取紊乱倍率 */ /** 获取紊乱倍率 */
get_DiscoverMultiplier(skill: skill, usefulBuffs: buff[], anomalyData?: typeof AnomalyData[number]) { get_DiscoverMultiplier(skill: skill) {
anomalyData ||= this.get_AnomalyData(skill) const anomalyData = this.get_AnomalyData(skill)
if (!anomalyData) return if (!anomalyData) return
const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration) const AnomalyDuration = this.get_AnomalyDuration({
...skill,
name: anomalyData.name,
type: anomalyData.name
}, this.buffM.buffs, anomalyData.duration)
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)) const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10))
const discover = anomalyData.discover! const discover = anomalyData.discover!
const Multiplier = discover.fixed_multiplier + times * discover.multiplier const Multiplier = discover.fixed_multiplier + times * discover.multiplier
@ -360,7 +399,7 @@ export class Calculator {
get(type: buff['type'], initial: number, skill: skill, usefulBuffs: buff[] = this.buffM.buffs, isRatio = false): number { get(type: buff['type'], initial: number, skill: skill, usefulBuffs: buff[] = this.buffM.buffs, isRatio = false): number {
return this.cache[type] ??= this.buffM._filter(usefulBuffs, { return this.cache[type] ??= this.buffM._filter(usefulBuffs, {
element: skill?.element, element: skill?.element,
range: [skill?.type], range: skill?.redirect ? [skill.type, skill.redirect] : [skill?.type],
type type
}, this).reduce((previousValue, buff) => { }, this).reduce((previousValue, buff) => {
const { value } = buff const { value } = buff
@ -385,6 +424,13 @@ export class Calculator {
return ATK return ATK
} }
/** 额外倍率 */
get_ExtraMultiplier(skill: skill, usefulBuffs: buff[]) {
const ExtraMultiplier = this.get('倍率', 0, skill, usefulBuffs)
ExtraMultiplier && logger.debug(`额外倍率:${ExtraMultiplier}`)
return ExtraMultiplier
}
/** 暴击率 */ /** 暴击率 */
get_CRITRate(skill: skill, usefulBuffs: buff[]) { get_CRITRate(skill: skill, usefulBuffs: buff[]) {
let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs) let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs)

View file

@ -211,6 +211,9 @@ Buff来源可分为三大类武器、套装、角色影画、核心被动
> - T 核心技 > - T 核心技
> - 核心技中的技能各不相同,自行定义即可 > - 核心技中的技能各不相同,自行定义即可
> - Y 影画如柏妮思6影额外伤害
> - 影画中的技能各不相同,自行定义即可
> - 属性异常(特殊) > - 属性异常(特殊)
> - 强击 > - 强击
> - 灼烧 > - 灼烧
@ -221,11 +224,11 @@ Buff来源可分为三大类武器、套装、角色影画、核心被动
#### 技能类型命名解释说明 #### 技能类型命名解释说明
1. 首字母为技能所属基类,不可更改、不可单独作为技能名,后跟字母表示技能分支 1. 首字母为技能所属**基类**,不可更改、不可单独作为技能名,后跟字母表示技能分支
2. 树状命名,后一位字母代表基于其前一位字母的分支,取技能名发音(倒着读);属性异常较特殊,直接以异常名作为技能类型名 2. **树状命名**,后一位字母代表基于其前一位字母的分支,取技能名发音(倒着读);属性异常较特殊,直接以异常名作为技能类型名
3. 后跟数字可表示段数如AP1表示第一段普攻为避免混淆数字仅表示同一技能不同段数不用于区分不同技能 3. 后跟数字可表示**段数**如AP1表示第一段普攻为避免混淆数字仅表示同一技能不同段数不用于区分不同技能
4. 当不需要进一步细分分支时必须遵守此标准命名否则可能导致Buff计算错误 4. 当不需要进一步细分分支时必须遵守此标准命名否则可能导致Buff计算错误
@ -259,7 +262,7 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲
- 如果只包括**CCX**(巡游冲刺攻击),则代表对“冲刺攻击:冰渊潜袭”生效(无论普通或蓄力) - 如果只包括**CCX**(巡游冲刺攻击),则代表对“冲刺攻击:冰渊潜袭”生效(无论普通或蓄力)
- 如果只包括**CCXX**(蓄力巡游冲刺攻击),则代表只对“冲刺攻击:冰渊潜袭”的蓄力巡游冲刺攻击生效 - 如果只包括**CCXX**(蓄力巡游冲刺攻击),则代表只对“冲刺攻击:冰渊潜袭”的蓄力巡游冲刺攻击生效
[点此查看](./character/艾莲/calc.js#L12)艾莲实际伤害计算文件 [点此查看](./character/艾莲/calc.js#L24)艾莲实际伤害计算文件
### 技能倍率 ### 技能倍率
@ -271,6 +274,50 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲
需要自定义data.json时同样复制一份重命名为**data_user.json**即可 需要自定义data.json时同样复制一份重命名为**data_user.json**即可
角色每个技能各等级对应的倍率建议在[米游社官网图鉴](https://baike.mihoyo.com/zzz/wiki/channel/map/2/43)中查询。不建议使用第三方图鉴工具如B站的绝区零wiki其技能倍率可能存在错误
技能倍率大部分情况下为**等差数列**,少数情况下增量**存在变化**,请注意甄别。对于等差数列的技能倍率,我写了一个简易的生成函数,你可复制粘贴直接使用:
<details>
<summary>点击展开</summary>
```js
import { exec } from 'child_process'
const copyToClipboard = (text) => {
exec('clip').stdin.end(text)
}
function counter(first, second, num = 16) {
if (first > 100 && second > 100) {
first = Math.round(first * 100)
second = Math.round(second * 100)
} else {
first = Math.round(first * 10000)
second = Math.round(second * 10000)
}
const step = second - first
const arr = [first / 10000]
let txt = first / 10000 + ''
for (let i = 1; i < num; i++) {
const next = (first + step * i) / 10000
txt += ',' + +next.toFixed(4)
arr.push(next)
}
if (process.platform === 'win32') {
copyToClipboard(txt)
}
console.log(txt)
return arr
}
// 可按照实际数值填写当倍率大于100%时可忽略百分号填写
// 参数依次为1级倍率 2级倍率 生成长度
counter(145.7, 159, 16)
```
</details>
### 注册技能 ### 注册技能
伤害计算模块提供了注册各技能的接口[Calculator](./Calculator.ts),所有技能都需要通过此类的实例**calc**进行注册 伤害计算模块提供了注册各技能的接口[Calculator](./Calculator.ts),所有技能都需要通过此类的实例**calc**进行注册
@ -285,7 +332,9 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲
- 若某技能所造成伤害的属性与角色属性不符,应指定该技能的属性**element** - 若某技能所造成伤害的属性与角色属性不符,应指定该技能的属性**element**
- 技能的参数有较多可选的拓展,用于处理更复杂的情况,如有需要请自行查看[Calculator源码](./Calculator.ts)和已有角色的计算案例 - 技能的参数有较多可选的拓展,用于处理更复杂的情况,如有需要请自行查看[Calculator源码](./Calculator.ts)和已有角色的计算案例(较为复杂的计算案例可参考[柏妮思的伤害计算](./character/柏妮思/calc.js)
- 后续会根据需要,新增/调整拓展参数,对于已有的拓展会尽量保持兼容
- 目前只可注册角色的技能,部分武器有独立的造成额外伤害的机制,暂不考虑 - 目前只可注册角色的技能,部分武器有独立的造成额外伤害的机制,暂不考虑
@ -352,6 +401,10 @@ export function calc(buffM, calc, avatar) {
建议开启[在线调试](#管理buff) 建议开启[在线调试](#管理buff)
## 鸣谢
感谢[紫罗兰打烊啦](https://www.miyoushe.com/zzz/accountCenter/postList?id=279259320)对伤害计算细则的评测指正
--- ---
[伤害计算]:https://www.miyoushe.com/zzz/article/55265618 [伤害计算]:https://www.miyoushe.com/zzz/article/55265618

View file

@ -1,5 +1,4 @@
import { aliasToID } from '../../lib/convert/char.js'; import { aliasToID } from '../../lib/convert/char.js';
import config from '../../../../lib/config/config.js';
import { BuffManager } from './BuffManager.js'; import { BuffManager } from './BuffManager.js';
import { pluginPath } from '../../lib/path.js'; import { pluginPath } from '../../lib/path.js';
import { elementEnum } from './BuffManager.js'; import { elementEnum } from './BuffManager.js';
@ -15,7 +14,15 @@ const calcFnc = {
set: {} set: {}
}; };
async function init() { async function init() {
const isWatch = config.bot.log_level === 'debug'; // debug模式下监听文件变化 // debug模式下监听文件变化
const isWatch = await (async () => {
try {
return (await import('../../../../lib/config/config.js')).default.bot.log_level === 'debug';
}
catch {
return false;
}
})();
await Promise.all(fs.readdirSync(path.join(damagePath, 'character')).filter(v => v !== '模板').map(v => importChar(v, isWatch))); await Promise.all(fs.readdirSync(path.join(damagePath, 'character')).filter(v => v !== '模板').map(v => importChar(v, isWatch)));
for (const type of ['weapon', 'set']) { for (const type of ['weapon', 'set']) {
await Promise.all(fs.readdirSync(path.join(damagePath, type)).filter(v => v !== '模板.js' && !v.endsWith('_user.js') && v.endsWith('.js')) await Promise.all(fs.readdirSync(path.join(damagePath, type)).filter(v => v !== '模板.js' && !v.endsWith('_user.js') && v.endsWith('.js'))
@ -43,11 +50,14 @@ async function importChar(charName, isWatch = false) {
const calcFile = fs.existsSync(path.join(dir, 'calc_user.js')) ? 'calc_user.js' : 'calc.js'; const calcFile = fs.existsSync(path.join(dir, 'calc_user.js')) ? 'calc_user.js' : 'calc.js';
const dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json')); const dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json'));
try { try {
const calcFilePath = path.join(dir, calcFile);
if (isWatch) { if (isWatch) {
watchFile(path.join(dir, calcFile), () => importChar(charName)); watchFile(calcFilePath, () => importChar(charName));
watchFile(dataPath, () => charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8'))); watchFile(dataPath, () => charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8')));
} }
charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8')); charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8'));
if (!fs.existsSync(calcFilePath))
return;
const m = await import(`./character/${charName}/${calcFile}?${Date.now()}`); const m = await import(`./character/${charName}/${calcFile}?${Date.now()}`);
if (!m.calc && (!m.buffs || !m.skills)) if (!m.calc && (!m.buffs || !m.skills))
throw new Error('伤害计算文件格式错误'); throw new Error('伤害计算文件格式错误');

View file

@ -1,7 +1,6 @@
import type { skill } from './Calculator.js' import type { skill } from './Calculator.js'
import type { ZZZAvatarInfo } from '../avatar.js' import type { ZZZAvatarInfo } from '../avatar.js'
import { aliasToID } from '../../lib/convert/char.js' import { aliasToID } from '../../lib/convert/char.js'
import config from '../../../../lib/config/config.js'
import { buff, BuffManager } from './BuffManager.js' import { buff, BuffManager } from './BuffManager.js'
import { pluginPath } from '../../lib/path.js' import { pluginPath } from '../../lib/path.js'
import { elementEnum } from './BuffManager.js' import { elementEnum } from './BuffManager.js'
@ -46,7 +45,14 @@ const calcFnc: {
} }
async function init() { async function init() {
const isWatch = config.bot.log_level === 'debug' // debug模式下监听文件变化 // debug模式下监听文件变化
const isWatch = await (async () => {
try {
return (await import('../../../../lib/config/config.js')).default.bot.log_level === 'debug'
} catch {
return false
}
})()
await Promise.all(fs.readdirSync(path.join(damagePath, 'character')).filter(v => v !== '模板').map(v => importChar(v, isWatch))) await Promise.all(fs.readdirSync(path.join(damagePath, 'character')).filter(v => v !== '模板').map(v => importChar(v, isWatch)))
for (const type of ['weapon', 'set']) { for (const type of ['weapon', 'set']) {
await Promise.all( await Promise.all(
@ -76,11 +82,13 @@ async function importChar(charName: string, isWatch = false) {
const calcFile = fs.existsSync(path.join(dir, 'calc_user.js')) ? 'calc_user.js' : 'calc.js' const calcFile = fs.existsSync(path.join(dir, 'calc_user.js')) ? 'calc_user.js' : 'calc.js'
const dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json')) const dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json'))
try { try {
const calcFilePath = path.join(dir, calcFile)
if (isWatch) { if (isWatch) {
watchFile(path.join(dir, calcFile), () => importChar(charName)) watchFile(calcFilePath, () => importChar(charName))
watchFile(dataPath, () => charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8'))) watchFile(dataPath, () => charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8')))
} }
charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8')) charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8'))
if (!fs.existsSync(calcFilePath)) return
const m = await import(`./character/${charName}/${calcFile}?${Date.now()}`) const m = await import(`./character/${charName}/${calcFile}?${Date.now()}`)
if (!m.calc && (!m.buffs || !m.skills)) throw new Error('伤害计算文件格式错误') if (!m.calc && (!m.buffs || !m.skills)) throw new Error('伤害计算文件格式错误')
calcFnc.character[id] = m calcFnc.character[id] = m

View file

@ -1,5 +1,11 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{
name: '2影',
type: '增伤',
value: 0.03 * 12,
range: ['AP', 'CC', 'CF']
},
{ {
name: '6影', name: '6影',
type: '无视抗性', type: '无视抗性',
@ -7,12 +13,6 @@ export const buffs = [
element: 'Fire', element: 'Fire',
range: ['AQ'] range: ['AQ']
}, },
{
name: '2影',
type: '增伤',
value: 0.03 * 12,
range: ['AP', 'CC', 'CF']
},
{ {
name: '核心被动:热浪', name: '核心被动:热浪',
type: '增伤', type: '增伤',
@ -29,6 +29,7 @@ export const buffs = [
/** @type {import('../../Calculator.ts').Calculator['skills']} */ /** @type {import('../../Calculator.ts').Calculator['skills']} */
export const skills = [ export const skills = [
{ name: '灼烧', type: '灼烧' },
{ name: '普攻:火力镇压四段', type: 'AQ4' }, { name: '普攻:火力镇压四段', type: 'AQ4' },
{ name: '闪避反击:逆火', type: 'CF' }, { name: '闪避反击:逆火', type: 'CF' },
{ name: '强化特殊技:盛燃烈火', type: 'EQ' }, { name: '强化特殊技:盛燃烈火', type: 'EQ' },

View file

@ -1,16 +1,16 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{
name: '4影',
type: '暴击率',
value: 0.1
},
{ {
name: '6影', name: '6影',
type: '增伤', type: '增伤',
value: 0.04 * 6, value: 0.04 * 6,
range: ['AQ', 'CFQ'] range: ['AQ', 'CFQ']
}, },
{
name: '4影',
type: '暴击率',
value: 0.1
},
{ {
name: '核心被动:兄弟齐心', name: '核心被动:兄弟齐心',
type: '增伤', type: '增伤',
@ -27,6 +27,7 @@ export const buffs = [
/** @type {import('../../Calculator.ts').Calculator['skills']} */ /** @type {import('../../Calculator.ts').Calculator['skills']} */
export const skills = [ export const skills = [
{ name: '感电每次', type: '感电' },
{ name: '普攻二段(爆发)', type: 'AQ2' }, { name: '普攻二段(爆发)', type: 'AQ2' },
{ name: '闪避反击:过载钻击(爆发)', type: 'CFQ' }, { name: '闪避反击:过载钻击(爆发)', type: 'CFQ' },
{ name: '特殊技:爆发钻击(爆发)', type: 'EPQ' }, { name: '特殊技:爆发钻击(爆发)', type: 'EPQ' },

View file

@ -1,16 +1,16 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{
name: '6影',
type: '无视抗性',
value: 0.15
},
{ {
name: '2影', name: '2影',
type: '增伤', type: '增伤',
value: 0.5, value: 0.5,
range: ['CCQ'] range: ['CCQ']
}, },
{
name: '6影',
type: '无视抗性',
value: 0.15
},
{ {
name: '核心被动:破晓', name: '核心被动:破晓',
type: '暴击率', type: '暴击率',
@ -33,6 +33,7 @@ export const buffs = [
/** @type {import('../../Calculator.ts').Calculator['skills']} */ /** @type {import('../../Calculator.ts').Calculator['skills']} */
export const skills = [ export const skills = [
{ name: '感电每次', type: '感电' },
{ name: '普攻:穿云五段', type: 'AP5' }, { name: '普攻:穿云五段', type: 'AP5' },
{ name: '普攻:落羽', type: 'AX' }, { name: '普攻:落羽', type: 'AX' },
{ name: '冲刺攻击:飞弦·斩', type: 'CCQ3' }, { name: '冲刺攻击:飞弦·斩', type: 'CCQ3' },

View file

@ -9,20 +9,12 @@
// /** 注册buff */ // /** 注册buff */
// // 影画加成 // // 影画加成
// buffM.new({ // buffM.new({
// name: '6影', // name: '1影',
// type: '增伤', // type: '无视防御',
// isForever: true, // value: 0.36,
// value: 0.30,
// range: ['AX'] // range: ['AX']
// }) // })
// buffM.new({ // buffM.new({
// name: '4影',
// type: '增伤',
// isForever: true,
// value: 0.30,
// range: ['TP']
// })
// buffM.new({
// name: '2影', // name: '2影',
// type: '增伤', // type: '增伤',
// isForever: true, // isForever: true,
@ -36,9 +28,17 @@
// value: 0.15 // value: 0.15
// }) // })
// buffM.new({ // buffM.new({
// name: '1影', // name: '4影',
// type: '无视防御', // type: '增伤',
// value: 0.36, // isForever: true,
// value: 0.30,
// range: ['TP']
// })
// buffM.new({
// name: '6影',
// type: '增伤',
// isForever: true,
// value: 0.30,
// range: ['AX'] // range: ['AX']
// }) // })
// // 额外能力加成 // // 额外能力加成
@ -93,19 +93,11 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{ {
name: '6影', name: '1影',
type: '增伤', type: '无视防御',
isForever: true, value: 0.36,
value: 0.30,
range: ['AX'] range: ['AX']
}, },
{
name: '4影',
type: '增伤',
isForever: true,
value: 0.30,
range: ['TP']
},
{ {
name: '2影', name: '2影',
type: '暴击率', type: '暴击率',
@ -120,9 +112,17 @@ export const buffs = [
range: ['AP', 'CF'] range: ['AP', 'CF']
}, },
{ {
name: '1影', name: '4影',
type: '无视防御', type: '增伤',
value: 0.36, isForever: true,
value: 0.30,
range: ['TP']
},
{
name: '6影',
type: '增伤',
isForever: true,
value: 0.30,
range: ['AX'] range: ['AX']
}, },
{ {

View file

@ -1,11 +1,5 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{
name: '4影',
type: '无视抗性',
value: 0.25,
range: ['AQ', 'CCQ']
},
{ {
name: '2影', name: '2影',
type: '增伤', type: '增伤',
@ -13,6 +7,12 @@ export const buffs = [
element: 'Ether', element: 'Ether',
range: ['AQ', 'CCQ'] range: ['AQ', 'CCQ']
}, },
{
name: '4影',
type: '无视抗性',
value: 0.25,
range: ['AQ', 'CCQ']
},
{ {
name: '核心被动:特种弹药', name: '核心被动:特种弹药',
type: '增伤', type: '增伤',
@ -28,6 +28,7 @@ export const buffs = [
/** @type {import('../../Calculator.ts').Calculator['skills']} */ /** @type {import('../../Calculator.ts').Calculator['skills']} */
export const skills = [ export const skills = [
{ name: '侵蚀每次', type: '侵蚀' },
{ name: '普攻三段(以太)', type: 'AQY3' }, { name: '普攻三段(以太)', type: 'AQY3' },
{ name: '冲刺攻击:火力压制', type: 'CCQ' }, { name: '冲刺攻击:火力压制', type: 'CCQ' },
{ {

View file

@ -0,0 +1,102 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [
{
name: '1影',
type: '倍率',
value: 1,
range: ['TY', 'YY']
},
{
name: '2影',
type: '穿透率',
value: 0.2
},
{
name: '4影',
type: '暴击率',
value: 0.3,
range: ['EQ', 'L']
},
{
name: '6影',
type: '无视抗性',
value: 0.25,
element: 'Fire',
range: ['TY', 'YY', '灼烧']
},
{
name: '核心被动:燃油特调',
type: '增伤',
value: ({ calc }) => Math.min(30, Math.floor(calc.get_AnomalyProficiency() / 10)) * 0.01,
isForever: true,
range: ['TY', 'YY']
},
{
name: '额外能力:星火燎原',
type: '异常持续时间',
value: 3,
range: ['灼烧']
}
]
/** @type {import('../../Calculator.ts').Calculator['skills']} */
export const skills = [
{ name: '灼烧', type: '灼烧' },
{ name: '紊乱', type: '紊乱' },
{
name: '核心被动:余烬',
type: 'TY',
redirect: 'L'
},
// { name: '普攻:炽焰直调式五段', type: 'AP5' },
{ name: '长按普攻0', type: 'AX0', isHide: true },
{
name: '长按普攻',
type: 'AX',
after: ({ damage }) => damage.add('AX0')
},
{ name: '闪避反击:摇荡闪', type: 'CF' },
{ name: '强化E火焰冲击', type: 'EQP0' },
{
name: '强化E持续喷射秒伤',
type: 'EQP',
after: ({ damage }) => damage.x(0.5)
},
{ name: '强化E双份火焰冲击', type: 'EQS0' },
{
name: '强化E双份持续喷射秒伤',
type: 'EQS',
after: ({ damage }) => damage.x(0.5)
},
{ name: '连携技:燃油熔焰', type: 'RL' },
{ name: '终结技:纵享盛焰', type: 'RZ' }
]
/**
* @param {import('../../BuffManager.ts').BuffManager} buffM
* @param {import('../../Calculator.ts').Calculator} calc
* @param {import('../../../avatar.js').ZZZAvatarInfo} avatar
*/
export function calc(buffM, calc, avatar) {
if (avatar.rank >= 6) {
calc.new({
name: '6影强化E双份额外余烬秒伤',
type: 'YY',
fixedMultiplier: 1.2
})
calc.new({
name: '6影强化E双份额外灼烧',
type: 'Y灼烧',
dmg: (calc) => {
const dmg = calc.calc_skill({
name: '灼烧每段',
element: 'Fire',
type: '灼烧',
after: ({ damage }) => damage.x(18)
})
dmg.skill.name = '6影强化E双份额外灼烧'
return dmg
}
})
}
}

View file

@ -0,0 +1,37 @@
{
"skill": {
"AP5": [
0.963,1.051,1.139,1.227,1.315,1.403,1.491,1.579,1.667,1.755,1.843,1.931,2.019,2.107,2.195,2.283
],
"AX0": [
1.254,1.368,1.482,1.596,1.71,1.824,1.938,2.052,2.166,2.28,2.394,2.508,2.622,2.736,2.85,2.964
],
"AX": [
2.328,2.54,2.752,2.964,3.176,3.388,3.6,3.812,4.024,4.236,4.448,4.66,4.872,5.084,5.296,5.508
],
"CF": [
2.197,2.397,2.597,2.797,2.997,3.197,3.397,3.597,3.797,3.997,4.197,4.397,4.597,4.797,4.997,5.197
],
"EQP0": [
0.967,1.055,1.143,1.231,1.319,1.407,1.495,1.583,1.671,1.759,1.847,1.935,2.023,2.111,2.199,2.287
],
"EQP": [
5.438,5.933,6.428,6.923,7.418,7.913,8.408,8.903,9.398,9.893,10.388,10.883,11.378,11.873,12.368,12.863
],
"EQS0": [
2.871,3.132,3.393,3.654,3.915,4.176,4.437,4.698,4.959,5.22,5.481,5.742,6.003,6.264,6.525,6.786
],
"EQS": [
9.581,10.452,11.323,12.194,13.065,13.936,14.807,15.678,16.549,17.42,18.291,19.162,20.033,20.904,21.775,22.646
],
"RL": [
6.809,7.428,8.047,8.666,9.285,9.904,10.523,11.142,11.761,12.38,12.999,13.618,14.237,14.856,15.475,16.094
],
"RZ": [
20.122,21.952,23.782,25.612,27.442,29.272,31.102,32.932,34.762,36.592,38.422,40.252,42.082,43.912,45.742,47.572
],
"TY": [
1.75,2.05,2.35,2.62,2.9,3.2,3.5
]
}
}

View file

@ -7,13 +7,7 @@ export function calc(buffM, calc, avatar) {
/** 注册buff */ /** 注册buff */
// 影画加成 // 影画加成
buffM.new({ buffM.new({
name: '6影', name: '1影',
type: ,
value: 0,
range: ['']
})
buffM.new({
name: '4影',
type: , type: ,
value: 0, value: 0,
range: [''] range: ['']
@ -25,7 +19,13 @@ export function calc(buffM, calc, avatar) {
range: [''] range: ['']
}) })
buffM.new({ buffM.new({
name: '1影', name: '4影',
type: ,
value: 0,
range: ['']
})
buffM.new({
name: '6影',
type: , type: ,
value: 0, value: 0,
range: [''] range: ['']
@ -65,13 +65,7 @@ export function calc(buffM, calc, avatar) {
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{ {
name: '6影', name: '1影',
type: ,
value: 0,
range: ['']
},
{
name: '4影',
type: , type: ,
value: 0, value: 0,
range: [''] range: ['']
@ -83,7 +77,13 @@ export const buffs = [
range: [''] range: ['']
}, },
{ {
name: '1影', name: '4影',
type: ,
value: 0,
range: ['']
},
{
name: '6影',
type: , type: ,
value: 0, value: 0,
range: [''] range: ['']

View file

@ -1,9 +1,10 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{ {
name: '6影', name: '1影',
type: '暴击伤害', type: '无视抗性',
value: 0.18 * 3 value: 0.16,
element: 'Physical'
}, },
{ {
name: '4影', name: '4影',
@ -11,10 +12,9 @@ export const buffs = [
value: 0.07 * 2 value: 0.07 * 2
}, },
{ {
name: '1影', name: '6影',
type: '无视抗性', type: '暴击伤害',
value: 0.16, value: 0.18 * 3
element: 'Physical'
}, },
{ {
name: '核心被动:猫步诡影', name: '核心被动:猫步诡影',

View file

@ -1,5 +1,17 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{
name: '1影',
type: '暴击率',
value: 0.2 * 6
},
{
name: '2影',
type: '暴击伤害',
value: 0.6,
range: ['EQ']
},
{ {
name: '6影', name: '6影',
type: '穿透率', type: '穿透率',
@ -11,17 +23,6 @@ export const buffs = [
value: 2.5, value: 2.5,
range: ['CCXX'] range: ['CCXX']
}, },
{
name: '2影',
type: '暴击伤害',
value: 0.6,
range: ['EQ']
},
{
name: '1影',
type: '暴击率',
value: 0.2 * 6
},
{ {
name: '核心被动:凌牙厉齿', name: '核心被动:凌牙厉齿',
type: '暴击伤害', type: '暴击伤害',

View file

@ -1,5 +1,15 @@
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ /** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
export const buffs = [ export const buffs = [
{
name: '1影',
type: '无视防御',
value: 0.15
},
{
name: '1影',
type: '暴击率',
value: 0.2
},
{ {
name: '6影', name: '6影',
type: '暴击伤害', type: '暴击伤害',
@ -12,16 +22,6 @@ export const buffs = [
type: '无视抗性', type: '无视抗性',
value: 0.2 value: 0.2
}, },
{
name: '1影',
type: '无视防御',
value: 0.15
},
{
name: '1影',
type: '暴击率',
value: 0.2
},
{ {
name: '额外能力:阳关三叠', name: '额外能力:阳关三叠',
type: '攻击力', type: '攻击力',
@ -45,6 +45,7 @@ export const buffs = [
/** @type {import('../../Calculator.ts').Calculator['skills']} */ /** @type {import('../../Calculator.ts').Calculator['skills']} */
export const skills = [ export const skills = [
{ name: '感电每次', type: '感电' },
{ name: '普攻:醉花月云转', type: 'AQ' }, { name: '普攻:醉花月云转', type: 'AQ' },
{ name: '闪避反击:意不尽', type: 'CF' }, { name: '闪避反击:意不尽', type: 'CF' },
{ name: '强化特殊技:月上海棠', type: 'EQ' }, { name: '强化特殊技:月上海棠', type: 'EQ' },