mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-16 13:17:32 +00:00
新增柏妮思伤害计算等
This commit is contained in:
parent
6573f1d600
commit
b51acca73e
19 changed files with 1053 additions and 164 deletions
|
|
@ -22,26 +22,27 @@ export var buffTypeEnum;
|
|||
(function (buffTypeEnum) {
|
||||
// 通用乘区
|
||||
buffTypeEnum[buffTypeEnum["\u653B\u51FB\u529B"] = 0] = "\u653B\u51FB\u529B";
|
||||
buffTypeEnum[buffTypeEnum["\u589E\u4F24"] = 1] = "\u589E\u4F24";
|
||||
buffTypeEnum[buffTypeEnum["\u6613\u4F24"] = 2] = "\u6613\u4F24";
|
||||
buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u6297\u6027"] = 3] = "\u65E0\u89C6\u6297\u6027";
|
||||
buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u9632\u5FA1"] = 4] = "\u65E0\u89C6\u9632\u5FA1";
|
||||
buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u503C"] = 5] = "\u7A7F\u900F\u503C";
|
||||
buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u7387"] = 6] = "\u7A7F\u900F\u7387";
|
||||
buffTypeEnum[buffTypeEnum["\u500D\u7387"] = 1] = "\u500D\u7387";
|
||||
buffTypeEnum[buffTypeEnum["\u589E\u4F24"] = 2] = "\u589E\u4F24";
|
||||
buffTypeEnum[buffTypeEnum["\u6613\u4F24"] = 3] = "\u6613\u4F24";
|
||||
buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u6297\u6027"] = 4] = "\u65E0\u89C6\u6297\u6027";
|
||||
buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u9632\u5FA1"] = 5] = "\u65E0\u89C6\u9632\u5FA1";
|
||||
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\u4F24\u5BB3"] = 8] = "\u66B4\u51FB\u4F24\u5BB3";
|
||||
buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u7387"] = 8] = "\u66B4\u51FB\u7387";
|
||||
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\u589E\u4F24"] = 10] = "\u5F02\u5E38\u589E\u4F24";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u7387"] = 11] = "\u5F02\u5E38\u66B4\u51FB\u7387";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"] = 12] = "\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\u7CBE\u901A"] = 10] = "\u5F02\u5E38\u7CBE\u901A";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u589E\u4F24"] = 11] = "\u5F02\u5E38\u589E\u4F24";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u7387"] = 12] = "\u5F02\u5E38\u66B4\u51FB\u7387";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"] = 13] = "\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"] = 14] = "\u5F02\u5E38\u6301\u7EED\u65F6\u95F4";
|
||||
// 其他属性,一般不直接影响伤害,但可能用于buff是否生效判断/转模
|
||||
buffTypeEnum[buffTypeEnum["\u751F\u547D\u503C"] = 14] = "\u751F\u547D\u503C";
|
||||
buffTypeEnum[buffTypeEnum["\u9632\u5FA1\u529B"] = 15] = "\u9632\u5FA1\u529B";
|
||||
buffTypeEnum[buffTypeEnum["\u51B2\u51FB\u529B"] = 16] = "\u51B2\u51FB\u529B";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u638C\u63A7"] = 17] = "\u5F02\u5E38\u638C\u63A7";
|
||||
buffTypeEnum[buffTypeEnum["\u751F\u547D\u503C"] = 15] = "\u751F\u547D\u503C";
|
||||
buffTypeEnum[buffTypeEnum["\u9632\u5FA1\u529B"] = 16] = "\u9632\u5FA1\u529B";
|
||||
buffTypeEnum[buffTypeEnum["\u51B2\u51FB\u529B"] = 17] = "\u51B2\u51FB\u529B";
|
||||
buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u638C\u63A7"] = 18] = "\u5F02\u5E38\u638C\u63A7";
|
||||
})(buffTypeEnum || (buffTypeEnum = {}));
|
||||
let depth = 0, weakMapCheck = new WeakMap();
|
||||
/**
|
||||
|
|
@ -116,8 +117,8 @@ export class BuffManager {
|
|||
param.range = param.range.filter(r => typeof r === 'string');
|
||||
if (!param.range.length)
|
||||
continue;
|
||||
// buff作用范围向后覆盖
|
||||
else if (!param.range.every(ST => buffRange.some(BT => ST.startsWith(BT))))
|
||||
// buff作用范围向后覆盖,满足伤害类型range中任意一个即可
|
||||
else if (!param.range.some(ST => buffRange.some(BT => ST.startsWith(BT))))
|
||||
return false;
|
||||
else
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export type buffSource = 'Weapon' | 'Set' | 'Rank' | 'Talent' | 'Addition' | 'Sk
|
|||
|
||||
export enum buffTypeEnum {
|
||||
// 通用乘区
|
||||
攻击力, 增伤, 易伤, 无视抗性, 无视防御, 穿透值, 穿透率,
|
||||
攻击力, 倍率, 增伤, 易伤, 无视抗性, 无视防御, 穿透值, 穿透率,
|
||||
// 直伤乘区
|
||||
暴击率, 暴击伤害,
|
||||
// 异常乘区
|
||||
|
|
@ -172,8 +172,8 @@ export class BuffManager {
|
|||
if (!buffRange || !param.range) continue // 对任意类型生效
|
||||
param.range = param.range.filter(r => typeof r === 'string')
|
||||
if (!param.range.length) continue
|
||||
// buff作用范围向后覆盖
|
||||
else if (!param.range.every(ST => buffRange.some(BT => ST.startsWith(BT)))) return false
|
||||
// buff作用范围向后覆盖,满足伤害类型range中任意一个即可
|
||||
else if (!param.range.some(ST => buffRange.some(BT => ST.startsWith(BT)))) return false
|
||||
else continue
|
||||
} else if (key === 'element') {
|
||||
if (!buff.element || !param.element) continue // 对任意属性生效
|
||||
|
|
@ -235,6 +235,7 @@ export class BuffManager {
|
|||
filter<T extends keyof buff>(type: T, value: buff[T]): buff[]
|
||||
/**
|
||||
* 根据多个指定属性筛选 **启用状态** 的buff
|
||||
* - 对伤害类型range数组的筛选,只要其中有一个符合即认为满足
|
||||
*/
|
||||
filter(obj: { [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: buff[key] } & { element: element }, calc?: Calculator): buff[]
|
||||
/**
|
||||
|
|
|
|||
591
model/damage/Calculator copy.ts
Normal file
591
model/damage/Calculator copy.ts
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -54,27 +54,30 @@ export class Calculator {
|
|||
return;
|
||||
return this.calc_skill(MySkill);
|
||||
}
|
||||
if (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 = {};
|
||||
if (skill.dmg)
|
||||
return skill.dmg(this);
|
||||
/** 缩小筛选范围 */
|
||||
const usefulBuffs = this.buffM.filter({
|
||||
element: skill.element,
|
||||
range: [skill.type]
|
||||
range: skill.redirect ? [skill.type, skill.redirect] : [skill.type]
|
||||
}, this);
|
||||
if (skill.before)
|
||||
skill.before({ avatar: this.avatar, usefulBuffs, calc: this });
|
||||
skill.before({ skill, avatar: this.avatar, usefulBuffs, calc: this });
|
||||
let Multiplier = 0;
|
||||
const isAnomaly = typeof anomalyEnum[skill.type] === 'number';
|
||||
if (skill.fixedMultiplier)
|
||||
Multiplier = skill.fixedMultiplier;
|
||||
else if (isAnomaly) {
|
||||
Multiplier = (skill.type === '紊乱' ?
|
||||
this.get_DiscoverMultiplier(skill, usefulBuffs) :
|
||||
this.get_AnomalyMultiplier(skill, usefulBuffs)) || 0;
|
||||
this.get_DiscoverMultiplier(skill) :
|
||||
this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0)) || 0;
|
||||
}
|
||||
else {
|
||||
if (skill.skillMultiplier)
|
||||
|
|
@ -82,8 +85,12 @@ export class Calculator {
|
|||
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;
|
||||
|
|
@ -139,14 +146,31 @@ export class Calculator {
|
|||
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 => 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) {
|
||||
if (typeof param === 'object') {
|
||||
|
|
@ -192,25 +216,29 @@ export class Calculator {
|
|||
return a[0];
|
||||
}
|
||||
/** 获取属性异常倍率 */
|
||||
get_AnomalyMultiplier(skill, usefulBuffs, anomalyData) {
|
||||
anomalyData ||= this.get_AnomalyData(skill);
|
||||
get_AnomalyMultiplier(skill, usefulBuffs, 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);
|
||||
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10));
|
||||
times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10));
|
||||
Multiplier = anomalyData.multiplier * times;
|
||||
}
|
||||
logger.debug(`倍率:${Multiplier}`);
|
||||
return Multiplier;
|
||||
}
|
||||
/** 获取紊乱倍率 */
|
||||
get_DiscoverMultiplier(skill, usefulBuffs, anomalyData) {
|
||||
anomalyData ||= this.get_AnomalyData(skill);
|
||||
get_DiscoverMultiplier(skill) {
|
||||
const anomalyData = this.get_AnomalyData(skill);
|
||||
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 discover = anomalyData.discover;
|
||||
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) {
|
||||
return this.cache[type] ??= this.buffM._filter(usefulBuffs, {
|
||||
element: skill?.element,
|
||||
range: [skill?.type],
|
||||
range: skill?.redirect ? [skill.type, skill.redirect] : [skill?.type],
|
||||
type
|
||||
}, this).reduce((previousValue, buff) => {
|
||||
const { value } = buff;
|
||||
|
|
@ -265,6 +293,12 @@ export class Calculator {
|
|||
logger.debug(`攻击力:${ATK}`);
|
||||
return ATK;
|
||||
}
|
||||
/** 额外倍率 */
|
||||
get_ExtraMultiplier(skill, usefulBuffs) {
|
||||
const ExtraMultiplier = this.get('倍率', 0, skill, usefulBuffs);
|
||||
ExtraMultiplier && logger.debug(`额外倍率:${ExtraMultiplier}`);
|
||||
return ExtraMultiplier;
|
||||
}
|
||||
/** 暴击率 */
|
||||
get_CRITRate(skill, usefulBuffs) {
|
||||
let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs);
|
||||
|
|
|
|||
|
|
@ -19,10 +19,21 @@ export interface skill {
|
|||
fixedMultiplier?: number
|
||||
/** 角色面板伤害统计中是否隐藏显示 */
|
||||
isHide?: boolean
|
||||
/**
|
||||
* 重定向技能伤害类型
|
||||
*
|
||||
* 当出现“X"造成的伤害被视为“Y”伤害时,可使用该参数指定Y的类型
|
||||
* 参考技能类型命名标准
|
||||
*/
|
||||
redirect?: string
|
||||
/** 禁用伤害计算cache */
|
||||
banCache?: boolean
|
||||
/** 自定义计算逻辑 */
|
||||
dmg?: (calc: Calculator) => damage
|
||||
/** 额外处理 */
|
||||
before?: ({ avatar, usefulBuffs, calc }: {
|
||||
before?: ({ skill, avatar, usefulBuffs, calc }: {
|
||||
/** 技能自身 */
|
||||
skill: skill
|
||||
avatar: ZZZAvatarInfo
|
||||
usefulBuffs: buff[]
|
||||
calc: Calculator
|
||||
|
|
@ -77,6 +88,8 @@ export interface damage {
|
|||
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
|
||||
|
|
@ -172,30 +185,37 @@ export class Calculator {
|
|||
if (!MySkill) return
|
||||
return this.calc_skill(MySkill)
|
||||
}
|
||||
if (this.damageCache[skill.type]) return this.damageCache[skill.type]
|
||||
logger.debug(`${logger.green(skill.type)}伤害计算:`)
|
||||
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 = {}
|
||||
if (skill.dmg) return skill.dmg(this)
|
||||
/** 缩小筛选范围 */
|
||||
const usefulBuffs = this.buffM.filter({
|
||||
element: skill.element,
|
||||
range: [skill.type]
|
||||
range: skill.redirect ? [skill.type, skill.redirect] : [skill.type]
|
||||
}, 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
|
||||
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, usefulBuffs) :
|
||||
this.get_AnomalyMultiplier(skill, usefulBuffs)
|
||||
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
|
||||
|
|
@ -249,15 +269,30 @@ export class Calculator {
|
|||
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)
|
||||
this.damageCache[skill.type] = damage
|
||||
if (!skill.banCache) this.damageCache[skill.type] = damage
|
||||
return damage
|
||||
}
|
||||
|
||||
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]) {
|
||||
anomalyData ||= this.get_AnomalyData(skill)
|
||||
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)
|
||||
const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10))
|
||||
times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10))
|
||||
Multiplier = anomalyData.multiplier * times
|
||||
}
|
||||
logger.debug(`倍率:${Multiplier}`)
|
||||
|
|
@ -325,10 +360,14 @@ export class Calculator {
|
|||
}
|
||||
|
||||
/** 获取紊乱倍率 */
|
||||
get_DiscoverMultiplier(skill: skill, usefulBuffs: buff[], anomalyData?: typeof AnomalyData[number]) {
|
||||
anomalyData ||= this.get_AnomalyData(skill)
|
||||
get_DiscoverMultiplier(skill: skill) {
|
||||
const anomalyData = this.get_AnomalyData(skill)
|
||||
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 discover = anomalyData.discover!
|
||||
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 {
|
||||
return this.cache[type] ??= this.buffM._filter(usefulBuffs, {
|
||||
element: skill?.element,
|
||||
range: [skill?.type],
|
||||
range: skill?.redirect ? [skill.type, skill.redirect] : [skill?.type],
|
||||
type
|
||||
}, this).reduce((previousValue, buff) => {
|
||||
const { value } = buff
|
||||
|
|
@ -385,6 +424,13 @@ export class Calculator {
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -211,6 +211,9 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动
|
|||
> - T 核心技
|
||||
> - 核心技中的技能各不相同,自行定义即可
|
||||
|
||||
> - Y 影画(如柏妮思6影额外伤害)
|
||||
> - 影画中的技能各不相同,自行定义即可
|
||||
|
||||
> - 属性异常(特殊)
|
||||
> - 强击
|
||||
> - 灼烧
|
||||
|
|
@ -221,11 +224,11 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动
|
|||
|
||||
#### 技能类型命名解释说明
|
||||
|
||||
1. 首字母为技能所属基类,不可更改、不可单独作为技能名,后跟字母表示技能分支
|
||||
1. 首字母为技能所属**基类**,不可更改、不可单独作为技能名,后跟字母表示技能分支
|
||||
|
||||
2. 树状命名,后一位字母代表基于其前一位字母的分支,取技能名发音(倒着读);属性异常较特殊,直接以异常名作为技能类型名
|
||||
2. **树状命名**,后一位字母代表基于其前一位字母的分支,取技能名发音(倒着读);属性异常较特殊,直接以异常名作为技能类型名
|
||||
|
||||
3. 后跟数字可表示段数,如AP1表示第一段普攻;为避免混淆,数字仅表示同一技能不同段数,不用于区分不同技能
|
||||
3. 后跟数字可表示**段数**,如AP1表示第一段普攻;为避免混淆,数字仅表示同一技能不同段数,不用于区分不同技能
|
||||
|
||||
4. 当不需要进一步细分分支时,必须遵守此标准命名,否则可能导致Buff计算错误
|
||||
|
||||
|
|
@ -259,7 +262,7 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲
|
|||
- 如果只包括**CCX**(巡游冲刺攻击),则代表对“冲刺攻击:冰渊潜袭”生效(无论普通或蓄力)
|
||||
- 如果只包括**CCXX**(蓄力巡游冲刺攻击),则代表只对“冲刺攻击:冰渊潜袭”的蓄力巡游冲刺攻击生效
|
||||
|
||||
[点此查看](./character/艾莲/calc.js#L12)艾莲实际伤害计算文件
|
||||
[点此查看](./character/艾莲/calc.js#L24)艾莲实际伤害计算文件
|
||||
|
||||
### 技能倍率
|
||||
|
||||
|
|
@ -271,6 +274,50 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲
|
|||
|
||||
需要自定义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**进行注册
|
||||
|
|
@ -285,7 +332,9 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲
|
|||
|
||||
- 若某技能所造成伤害的属性与角色属性不符,应指定该技能的属性**element**
|
||||
|
||||
- 技能的参数有较多可选的拓展,用于处理更复杂的情况,如有需要请自行查看[Calculator源码](./Calculator.ts)和已有角色的计算案例
|
||||
- 技能的参数有较多可选的拓展,用于处理更复杂的情况,如有需要请自行查看[Calculator源码](./Calculator.ts)和已有角色的计算案例(较为复杂的计算案例可参考[柏妮思的伤害计算](./character/柏妮思/calc.js))
|
||||
|
||||
- 后续会根据需要,新增/调整拓展参数,对于已有的拓展会尽量保持兼容
|
||||
|
||||
- 目前只可注册角色的技能,部分武器有独立的造成额外伤害的机制,暂不考虑
|
||||
|
||||
|
|
@ -352,6 +401,10 @@ export function calc(buffM, calc, avatar) {
|
|||
|
||||
建议开启[在线调试](#管理buff)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
感谢[紫罗兰打烊啦](https://www.miyoushe.com/zzz/accountCenter/postList?id=279259320)对伤害计算细则的评测指正
|
||||
|
||||
---
|
||||
|
||||
[伤害计算]:https://www.miyoushe.com/zzz/article/55265618
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { aliasToID } from '../../lib/convert/char.js';
|
||||
import config from '../../../../lib/config/config.js';
|
||||
import { BuffManager } from './BuffManager.js';
|
||||
import { pluginPath } from '../../lib/path.js';
|
||||
import { elementEnum } from './BuffManager.js';
|
||||
|
|
@ -15,7 +14,15 @@ const calcFnc = {
|
|||
set: {}
|
||||
};
|
||||
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)));
|
||||
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'))
|
||||
|
|
@ -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 dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json'));
|
||||
try {
|
||||
const calcFilePath = path.join(dir, calcFile);
|
||||
if (isWatch) {
|
||||
watchFile(path.join(dir, calcFile), () => importChar(charName));
|
||||
watchFile(calcFilePath, () => importChar(charName));
|
||||
watchFile(dataPath, () => 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()}`);
|
||||
if (!m.calc && (!m.buffs || !m.skills))
|
||||
throw new Error('伤害计算文件格式错误');
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { skill } from './Calculator.js'
|
||||
import type { ZZZAvatarInfo } from '../avatar.js'
|
||||
import { aliasToID } from '../../lib/convert/char.js'
|
||||
import config from '../../../../lib/config/config.js'
|
||||
import { buff, BuffManager } from './BuffManager.js'
|
||||
import { pluginPath } from '../../lib/path.js'
|
||||
import { elementEnum } from './BuffManager.js'
|
||||
|
|
@ -46,7 +45,14 @@ const calcFnc: {
|
|||
}
|
||||
|
||||
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)))
|
||||
for (const type of ['weapon', 'set']) {
|
||||
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 dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json'))
|
||||
try {
|
||||
const calcFilePath = path.join(dir, calcFile)
|
||||
if (isWatch) {
|
||||
watchFile(path.join(dir, calcFile), () => importChar(charName))
|
||||
watchFile(calcFilePath, () => importChar(charName))
|
||||
watchFile(dataPath, () => 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()}`)
|
||||
if (!m.calc && (!m.buffs || !m.skills)) throw new Error('伤害计算文件格式错误')
|
||||
calcFnc.character[id] = m
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '2影',
|
||||
type: '增伤',
|
||||
value: 0.03 * 12,
|
||||
range: ['AP', 'CC', 'CF']
|
||||
},
|
||||
{
|
||||
name: '6影',
|
||||
type: '无视抗性',
|
||||
|
|
@ -7,12 +13,6 @@ export const buffs = [
|
|||
element: 'Fire',
|
||||
range: ['AQ']
|
||||
},
|
||||
{
|
||||
name: '2影',
|
||||
type: '增伤',
|
||||
value: 0.03 * 12,
|
||||
range: ['AP', 'CC', 'CF']
|
||||
},
|
||||
{
|
||||
name: '核心被动:热浪',
|
||||
type: '增伤',
|
||||
|
|
@ -29,6 +29,7 @@ export const buffs = [
|
|||
|
||||
/** @type {import('../../Calculator.ts').Calculator['skills']} */
|
||||
export const skills = [
|
||||
{ name: '灼烧', type: '灼烧' },
|
||||
{ name: '普攻:火力镇压四段', type: 'AQ4' },
|
||||
{ name: '闪避反击:逆火', type: 'CF' },
|
||||
{ name: '强化特殊技:盛燃烈火', type: 'EQ' },
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '4影',
|
||||
type: '暴击率',
|
||||
value: 0.1
|
||||
},
|
||||
{
|
||||
name: '6影',
|
||||
type: '增伤',
|
||||
value: 0.04 * 6,
|
||||
range: ['AQ', 'CFQ']
|
||||
},
|
||||
{
|
||||
name: '4影',
|
||||
type: '暴击率',
|
||||
value: 0.1
|
||||
},
|
||||
{
|
||||
name: '核心被动:兄弟齐心',
|
||||
type: '增伤',
|
||||
|
|
@ -27,6 +27,7 @@ export const buffs = [
|
|||
|
||||
/** @type {import('../../Calculator.ts').Calculator['skills']} */
|
||||
export const skills = [
|
||||
{ name: '感电每次', type: '感电' },
|
||||
{ name: '普攻二段(爆发)', type: 'AQ2' },
|
||||
{ name: '闪避反击:过载钻击(爆发)', type: 'CFQ' },
|
||||
{ name: '特殊技:爆发钻击(爆发)', type: 'EPQ' },
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '6影',
|
||||
type: '无视抗性',
|
||||
value: 0.15
|
||||
},
|
||||
{
|
||||
name: '2影',
|
||||
type: '增伤',
|
||||
value: 0.5,
|
||||
range: ['CCQ']
|
||||
},
|
||||
{
|
||||
name: '6影',
|
||||
type: '无视抗性',
|
||||
value: 0.15
|
||||
},
|
||||
{
|
||||
name: '核心被动:破晓',
|
||||
type: '暴击率',
|
||||
|
|
@ -33,6 +33,7 @@ export const buffs = [
|
|||
|
||||
/** @type {import('../../Calculator.ts').Calculator['skills']} */
|
||||
export const skills = [
|
||||
{ name: '感电每次', type: '感电' },
|
||||
{ name: '普攻:穿云五段', type: 'AP5' },
|
||||
{ name: '普攻:落羽', type: 'AX' },
|
||||
{ name: '冲刺攻击:飞弦·斩', type: 'CCQ3' },
|
||||
|
|
|
|||
|
|
@ -9,20 +9,12 @@
|
|||
// /** 注册buff */
|
||||
// // 影画加成
|
||||
// buffM.new({
|
||||
// name: '6影',
|
||||
// type: '增伤',
|
||||
// isForever: true,
|
||||
// value: 0.30,
|
||||
// name: '1影',
|
||||
// type: '无视防御',
|
||||
// value: 0.36,
|
||||
// range: ['AX']
|
||||
// })
|
||||
// buffM.new({
|
||||
// name: '4影',
|
||||
// type: '增伤',
|
||||
// isForever: true,
|
||||
// value: 0.30,
|
||||
// range: ['TP']
|
||||
// })
|
||||
// buffM.new({
|
||||
// name: '2影',
|
||||
// type: '增伤',
|
||||
// isForever: true,
|
||||
|
|
@ -36,9 +28,17 @@
|
|||
// value: 0.15
|
||||
// })
|
||||
// buffM.new({
|
||||
// name: '1影',
|
||||
// type: '无视防御',
|
||||
// value: 0.36,
|
||||
// name: '4影',
|
||||
// type: '增伤',
|
||||
// isForever: true,
|
||||
// value: 0.30,
|
||||
// range: ['TP']
|
||||
// })
|
||||
// buffM.new({
|
||||
// name: '6影',
|
||||
// type: '增伤',
|
||||
// isForever: true,
|
||||
// value: 0.30,
|
||||
// range: ['AX']
|
||||
// })
|
||||
// // 额外能力加成
|
||||
|
|
@ -93,19 +93,11 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '6影',
|
||||
type: '增伤',
|
||||
isForever: true,
|
||||
value: 0.30,
|
||||
name: '1影',
|
||||
type: '无视防御',
|
||||
value: 0.36,
|
||||
range: ['AX']
|
||||
},
|
||||
{
|
||||
name: '4影',
|
||||
type: '增伤',
|
||||
isForever: true,
|
||||
value: 0.30,
|
||||
range: ['TP']
|
||||
},
|
||||
{
|
||||
name: '2影',
|
||||
type: '暴击率',
|
||||
|
|
@ -120,9 +112,17 @@ export const buffs = [
|
|||
range: ['AP', 'CF']
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
type: '无视防御',
|
||||
value: 0.36,
|
||||
name: '4影',
|
||||
type: '增伤',
|
||||
isForever: true,
|
||||
value: 0.30,
|
||||
range: ['TP']
|
||||
},
|
||||
{
|
||||
name: '6影',
|
||||
type: '增伤',
|
||||
isForever: true,
|
||||
value: 0.30,
|
||||
range: ['AX']
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '4影',
|
||||
type: '无视抗性',
|
||||
value: 0.25,
|
||||
range: ['AQ', 'CCQ']
|
||||
},
|
||||
{
|
||||
name: '2影',
|
||||
type: '增伤',
|
||||
|
|
@ -13,6 +7,12 @@ export const buffs = [
|
|||
element: 'Ether',
|
||||
range: ['AQ', 'CCQ']
|
||||
},
|
||||
{
|
||||
name: '4影',
|
||||
type: '无视抗性',
|
||||
value: 0.25,
|
||||
range: ['AQ', 'CCQ']
|
||||
},
|
||||
{
|
||||
name: '核心被动:特种弹药',
|
||||
type: '增伤',
|
||||
|
|
@ -28,6 +28,7 @@ export const buffs = [
|
|||
|
||||
/** @type {import('../../Calculator.ts').Calculator['skills']} */
|
||||
export const skills = [
|
||||
{ name: '侵蚀每次', type: '侵蚀' },
|
||||
{ name: '普攻三段(以太)', type: 'AQY3' },
|
||||
{ name: '冲刺攻击:火力压制', type: 'CCQ' },
|
||||
{
|
||||
|
|
|
|||
102
model/damage/character/柏妮思/calc.js
Normal file
102
model/damage/character/柏妮思/calc.js
Normal 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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
37
model/damage/character/柏妮思/data.json
Normal file
37
model/damage/character/柏妮思/data.json
Normal 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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,7 @@ export function calc(buffM, calc, avatar) {
|
|||
/** 注册buff */
|
||||
// 影画加成
|
||||
buffM.new({
|
||||
name: '6影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
})
|
||||
buffM.new({
|
||||
name: '4影',
|
||||
name: '1影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
|
|
@ -25,7 +19,13 @@ export function calc(buffM, calc, avatar) {
|
|||
range: ['']
|
||||
})
|
||||
buffM.new({
|
||||
name: '1影',
|
||||
name: '4影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
})
|
||||
buffM.new({
|
||||
name: '6影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
|
|
@ -65,13 +65,7 @@ export function calc(buffM, calc, avatar) {
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '6影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
},
|
||||
{
|
||||
name: '4影',
|
||||
name: '1影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
|
|
@ -83,7 +77,13 @@ export const buffs = [
|
|||
range: ['']
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
name: '4影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
},
|
||||
{
|
||||
name: '6影',
|
||||
type: ,
|
||||
value: 0,
|
||||
range: ['']
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '6影',
|
||||
type: '暴击伤害',
|
||||
value: 0.18 * 3
|
||||
name: '1影',
|
||||
type: '无视抗性',
|
||||
value: 0.16,
|
||||
element: 'Physical'
|
||||
},
|
||||
{
|
||||
name: '4影',
|
||||
|
|
@ -11,10 +12,9 @@ export const buffs = [
|
|||
value: 0.07 * 2
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
type: '无视抗性',
|
||||
value: 0.16,
|
||||
element: 'Physical'
|
||||
name: '6影',
|
||||
type: '暴击伤害',
|
||||
value: 0.18 * 3
|
||||
},
|
||||
{
|
||||
name: '核心被动:猫步诡影',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '1影',
|
||||
type: '暴击率',
|
||||
value: 0.2 * 6
|
||||
},
|
||||
{
|
||||
name: '2影',
|
||||
type: '暴击伤害',
|
||||
value: 0.6,
|
||||
range: ['EQ']
|
||||
},
|
||||
|
||||
{
|
||||
name: '6影',
|
||||
type: '穿透率',
|
||||
|
|
@ -11,17 +23,6 @@ export const buffs = [
|
|||
value: 2.5,
|
||||
range: ['CCXX']
|
||||
},
|
||||
{
|
||||
name: '2影',
|
||||
type: '暴击伤害',
|
||||
value: 0.6,
|
||||
range: ['EQ']
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
type: '暴击率',
|
||||
value: 0.2 * 6
|
||||
},
|
||||
{
|
||||
name: '核心被动:凌牙厉齿',
|
||||
type: '暴击伤害',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
name: '1影',
|
||||
type: '无视防御',
|
||||
value: 0.15
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
type: '暴击率',
|
||||
value: 0.2
|
||||
},
|
||||
{
|
||||
name: '6影',
|
||||
type: '暴击伤害',
|
||||
|
|
@ -12,16 +22,6 @@ export const buffs = [
|
|||
type: '无视抗性',
|
||||
value: 0.2
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
type: '无视防御',
|
||||
value: 0.15
|
||||
},
|
||||
{
|
||||
name: '1影',
|
||||
type: '暴击率',
|
||||
value: 0.2
|
||||
},
|
||||
{
|
||||
name: '额外能力:阳关三叠',
|
||||
type: '攻击力',
|
||||
|
|
@ -45,6 +45,7 @@ export const buffs = [
|
|||
|
||||
/** @type {import('../../Calculator.ts').Calculator['skills']} */
|
||||
export const skills = [
|
||||
{ name: '感电每次', type: '感电' },
|
||||
{ name: '普攻:醉花月云转', type: 'AQ' },
|
||||
{ name: '闪避反击:意不尽', type: 'CF' },
|
||||
{ name: '强化特殊技:月上海棠', type: 'EQ' },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue