diff --git a/README.md b/README.md index 46d0e64..f24766f 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ > 插件依靠社区维护,发起者随缘更新,但是ZZZure组织成员会对PR进行合并,你可以在PR页面@协助者进行合并。 > > 在你使用之前请**务必**完整阅读 `README` 内容,如因无视 `README` 遇到的问题,在提问时难免遭受嘲笑。 -> -> **自定义词条权重**和**自定义伤害计算**请查看[此教程](./model/damage/README.md) # 安装 @@ -45,6 +43,10 @@ git clone --depth=1 https://gitee.com/bietiaop/ZZZ-Plugin.git ./plugins/ZZZ-Plug

+## 自定义评分权重、伤害计算 + +**自定义评分权重**和**自定义伤害计算**请查看[此教程](./model/damage/README.md) + ## 攻略、图鉴 **攻略、图鉴建议使用第三方插件**,本插件的攻略功能是在没有专业插件的情况下的保底功能。 @@ -86,7 +88,11 @@ git clone --depth=1 https://gitee.com/bietiaop/ZZZ-Plugin.git ./plugins/ZZZ-Plug 绑定设备**无法100%解决**账号异常问题。 -若更新面板遇见账号异常问题,可尝试 **%更新展柜面板**,这将调用[Enka](https://enka.network/?zzz)接口更新游戏内展示的角色的数据。如若通过此方法更新的角色数据与实际不一致,请[提出issue](https://github.com/ZZZure/ZZZ-Plugin/issues/new) +## 更新展柜面板 + +若通过默认的米游社更新面板遇见账号异常问题,可尝试 **%更新展柜面板**,这将调用[Enka](https://enka.network/?zzz)接口更新游戏内展示的角色的数据。如若通过此方法更新的角色数据与实际不一致,请[提出issue](https://github.com/ZZZure/ZZZ-Plugin/issues/new) + +该服务可能偶尔无法使用,如更新展柜面板一直请求失败,可通过锅巴修改`自定义enkaApi地址`项来自定义请求链接,请求和返回与Enka格式一致皆可 ## 角色图缺失 diff --git a/model/avatar.js b/model/avatar.js index 8136cfd..fe76e89 100644 --- a/model/avatar.js +++ b/model/avatar.js @@ -313,7 +313,7 @@ export class ZZZAvatarInfo { */ const get = (name) => { const data = basic_properties[name] - return Number(data.base || data.final) + return Number(data?.base || data?.final || 0) } return this._base_properties = { HP: get('hpmax'), @@ -334,6 +334,7 @@ export class ZZZAvatarInfo { * @param {keyof ZZZAvatarInfo['basic_properties']} name */ const get = (name) => { + if (!basic_properties[name]) return 0 const data = basic_properties[name].final return Number(data.includes('%') ? data.replace('%', '') / 100 : data) } diff --git a/model/damage/BuffManager.js b/model/damage/BuffManager.js index faec682..85cc988 100644 --- a/model/damage/BuffManager.js +++ b/model/damage/BuffManager.js @@ -28,17 +28,20 @@ export var buffTypeEnum; 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"] = 8] = "\u66B4\u51FB\u7387"; - buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u4F24\u5BB3"] = 9] = "\u66B4\u51FB\u4F24\u5BB3"; - 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"; - 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["\u5931\u8861\u6613\u4F24"] = 8] = "\u5931\u8861\u6613\u4F24"; + buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u7387"] = 9] = "\u66B4\u51FB\u7387"; + buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u4F24\u5BB3"] = 10] = "\u66B4\u51FB\u4F24\u5BB3"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u7CBE\u901A"] = 11] = "\u5F02\u5E38\u7CBE\u901A"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u589E\u4F24"] = 12] = "\u5F02\u5E38\u589E\u4F24"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u7387"] = 13] = "\u5F02\u5E38\u66B4\u51FB\u7387"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"] = 14] = "\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"] = 15] = "\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"; + buffTypeEnum[buffTypeEnum["\u8D2F\u7A7F\u529B"] = 16] = "\u8D2F\u7A7F\u529B"; + buffTypeEnum[buffTypeEnum["\u8D2F\u7A7F\u589E\u4F24"] = 17] = "\u8D2F\u7A7F\u589E\u4F24"; + buffTypeEnum[buffTypeEnum["\u751F\u547D\u503C"] = 18] = "\u751F\u547D\u503C"; + buffTypeEnum[buffTypeEnum["\u9632\u5FA1\u529B"] = 19] = "\u9632\u5FA1\u529B"; + buffTypeEnum[buffTypeEnum["\u51B2\u51FB\u529B"] = 20] = "\u51B2\u51FB\u529B"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u638C\u63A7"] = 21] = "\u5F02\u5E38\u638C\u63A7"; })(buffTypeEnum || (buffTypeEnum = {})); let depth = 0, weakMapCheck = new WeakMap(); export class BuffManager { @@ -59,7 +62,6 @@ export class BuffManager { const oriBuff = buff; buff = _.merge({ status: true, - is: {}, ...this.defaultBuff }, buff); if (buff.range && !Array.isArray(buff.range)) diff --git a/model/damage/BuffManager.ts b/model/damage/BuffManager.ts index 2b0c5f6..d75d9c8 100644 --- a/model/damage/BuffManager.ts +++ b/model/damage/BuffManager.ts @@ -25,11 +25,13 @@ export type buffSource = '音擎' | '套装' | '技能' | '影画' | '核心被 export enum buffTypeEnum { // 通用乘区 - 攻击力, 倍率, 增伤, 易伤, 无视抗性, 无视防御, 穿透值, 穿透率, + 攻击力, 倍率, 增伤, 易伤, 无视抗性, 无视防御, 穿透值, 穿透率, 失衡易伤, // 直伤乘区 暴击率, 暴击伤害, // 异常乘区 异常精通, 异常增伤, 异常暴击率, 异常暴击伤害, 异常持续时间, + // 贯穿乘区 + 贯穿力, 贯穿增伤, // 其他属性,一般不直接影响伤害,但可能用于buff是否生效判断/转模 生命值, 防御力, 冲击力, 异常掌控 } @@ -52,7 +54,7 @@ export interface buff { * - 一般情况下此值即为提高值 * - 当buff增益类型为**攻击力/冲击力/异常精通/异常掌控/防御力/生命值**时,若此值 **<1**,则将此值理解为**初始属性**的**百分比提高** * @string - * 角色自身的buff提高值可能随技能/天赋等级提高而提高,此时可以于data.json的"buff"中添加对应的倍率信息(同上支持百分比提高),此时value即为键名,其首字母必须为对应技能的基类(参考技能类型命名标准) + * 角色自身的buff提高值可能随技能/天赋等级提高而提高,此时可以于data.json的"buff"中添加倍率数组(同上支持百分比提高),此时value即为键名,其首字母必须为对应技能的基类(参考技能类型命名标准) * @array * 根据buff.source自动选择对应等级/星级的值(同上支持百分比提高),支持的source: * - 音擎:音擎进阶星级 @@ -74,7 +76,7 @@ export interface buff { range?: string[] | anomaly[] | "追加攻击"[] /** * Buff增益技能类型**生效技能** - * - 不同于**range**,仅全匹配时该值生效,不会向后覆盖生效 + * - 不同于**range**,仅全匹配type时该值生效,不会向后覆盖生效 * - 无**range**且无**include**则该buff对**exclude**以外的全部技能生效 * - **range**与**include**符合其一则认为buff生效 * - 当技能参数存在**redirect**时,**range**与**include**的区别在于**include**不会尝试匹配**redirect** @@ -82,7 +84,7 @@ export interface buff { include?: string[] /** * Buff增益技能类型**排除技能** - * - 与**include**相同,仅全匹配时该值生效,不会向后覆盖生效 + * - 与**include**相同,仅全匹配type时该值生效,不会向后覆盖生效 * - 优先级高于**range**与**include** */ exclude?: string[] @@ -142,7 +144,7 @@ export class BuffManager { const oriBuff = buff buff = _.merge({ status: true, - is: {}, + // is: {}, ...this.defaultBuff }, buff) if (buff.range && !Array.isArray(buff.range)) diff --git a/model/damage/Calculator.js b/model/damage/Calculator.js index be58d3d..4f70337 100644 --- a/model/damage/Calculator.js +++ b/model/damage/Calculator.js @@ -50,7 +50,7 @@ export class Calculator { this.enemy = { level: this.avatar.level, basicDEF: 50, - resistance: 0.2 + resistance: -0.2 }; } get initial_properties() { @@ -81,12 +81,17 @@ export class Calculator { const num = skill.check; skill.check = oriSkill.check = ({ avatar }) => avatar.rank >= num; } + skill.isAnomalyDMG ??= oriSkill.isAnomalyDMG = typeof anomalyEnum[skill.type.slice(0, 2)] === 'number'; + skill.isSheerDMG ??= oriSkill.isSheerDMG = this.avatar.avatar_profession === 6 && elementType2element(this.avatar.element_type) === skill.element && !skill.isAnomalyDMG; this.skills.push(skill); return this.skills; } + find_skill(key, value) { + return this.skills.find(skill => skill[key] === value); + } calc_skill(skill) { if (typeof skill === 'string') { - const MySkill = this.skills.find(s => s.type === skill); + const MySkill = this.find_skill('type', skill); if (!MySkill) return; return this.calc_skill(MySkill); @@ -115,7 +120,7 @@ export class Calculator { if (skill.before) skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas }); logger.debug(`有效buff*${usefulBuffs.length}/${this.buffM.buffs.length}`); - const isAnomaly = typeof anomalyEnum[skill.type.slice(0, 2)] === 'number'; + const { isAnomalyDMG = false, isSheerDMG = false } = skill; if (!areas.BasicArea) { let Multiplier = props.倍率; if (!Multiplier) { @@ -138,7 +143,7 @@ export class Calculator { logger.warn('无效的技能倍率:', skill); } } - else if (isAnomaly) { + else if (isAnomalyDMG) { Multiplier = (skill.type.startsWith('紊乱') ? this.get_DiscoverMultiplier(skill) : this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0)) || 0; @@ -154,40 +159,51 @@ export class Calculator { logger.debug(`最终倍率:${Multiplier}`); } props.倍率 = Multiplier; - this.get_ATK(skill, usefulBuffs); - areas.BasicArea = props.攻击力 * props.倍率; - } - logger.debug(`基础伤害区:${areas.BasicArea}`); - if (isAnomaly) { - areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs); - areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs); - areas.LevelArea ??= this.get_LevelArea(); - if (!skill.type.startsWith('紊乱')) { - props.异常暴击率 ??= this.get_AnomalyCRITRate(skill, usefulBuffs); - props.异常暴击伤害 ??= this.get_AnomalyCRITDMG(skill, usefulBuffs); - areas.CriticalArea ??= 1 + props.异常暴击率 * (props.异常暴击伤害 - 1); + if (isSheerDMG) { + areas.BasicArea = this.get_SheerForce(skill, usefulBuffs) * Multiplier; + } + else { + areas.BasicArea = this.get_ATK(skill, usefulBuffs) * Multiplier; } } - else { - props.暴击率 ??= this.get_CRITRate(skill, usefulBuffs); - props.暴击伤害 ??= this.get_CRITDMG(skill, usefulBuffs); - areas.CriticalArea ??= 1 + props.暴击率 * (props.暴击伤害 - 1); + logger.debug(`基础伤害区:${areas.BasicArea}`); + let CRITRate = 0, CRITDMG = 0; + if (!areas.CriticalArea) { + if (isAnomalyDMG) { + if (!skill.type.startsWith('紊乱')) { + CRITRate = this.get_AnomalyCRITRate(skill, usefulBuffs); + CRITDMG = this.get_AnomalyCRITDMG(skill, usefulBuffs); + } + } + else { + CRITRate = this.get_CRITRate(skill, usefulBuffs); + CRITDMG = this.get_CRITDMG(skill, usefulBuffs); + } + areas.CriticalArea = 1 + CRITRate * CRITDMG; } - areas.CriticalArea ??= 1; - logger.debug(`暴击期望:${areas.CriticalArea}`); + areas.CriticalArea !== 1 && logger.debug(`暴击期望:${areas.CriticalArea}`); areas.BoostArea ??= this.get_BoostArea(skill, usefulBuffs); areas.VulnerabilityArea ??= this.get_VulnerabilityArea(skill, usefulBuffs); areas.ResistanceArea ??= this.get_ResistanceArea(skill, usefulBuffs); - areas.DefenceArea ??= this.get_DefenceArea(skill, usefulBuffs); - const { BasicArea, CriticalArea, BoostArea, VulnerabilityArea, ResistanceArea, DefenceArea, AnomalyProficiencyArea, LevelArea, AnomalyBoostArea } = areas; - const { 暴击伤害, 异常暴击伤害 } = props; - const result = isAnomaly ? + areas.DefenceArea ??= isSheerDMG ? 1 : this.get_DefenceArea(skill, usefulBuffs); + areas.StunVulnerabilityArea ??= this.get_StunVulnerabilityArea(skill, usefulBuffs); + if (isAnomalyDMG) { + areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs); + areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs); + areas.LevelArea ??= this.get_LevelArea(); + } + if (isSheerDMG) { + areas.SheerBoostArea ??= this.get_SheerBoostArea(skill, usefulBuffs); + } + const { BasicArea, CriticalArea, BoostArea, VulnerabilityArea, ResistanceArea, DefenceArea, AnomalyProficiencyArea, LevelArea, AnomalyBoostArea, StunVulnerabilityArea, SheerBoostArea = 1 } = areas; + const commonArea = BasicArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * StunVulnerabilityArea; + const result = isAnomalyDMG ? { - critDMG: (CriticalArea !== 1) ? BasicArea * 异常暴击伤害 * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, - expectDMG: BasicArea * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea + critDMG: (CriticalArea !== 1) ? commonArea * (CRITDMG + 1) * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, + expectDMG: commonArea * CriticalArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea } : { - critDMG: BasicArea * 暴击伤害 * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, - expectDMG: BasicArea * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea + critDMG: commonArea * (CRITDMG + 1) * SheerBoostArea, + expectDMG: commonArea * CriticalArea * SheerBoostArea }; const damage = { skill, usefulBuffs: _.sortBy(Array.from(this.usefulBuffResults.values()), ['type', 'value']).reverse(), props, areas, result }; this.usefulBuffResults.clear(); @@ -319,11 +335,11 @@ export class Calculator { } calc_differences(buffs, skill) { if (!skill) { - skill = this.skills.find((skill) => skill.isMain) + skill = this.find_skill('isMain', true) || this.calc().sort((a, b) => b.result.expectDMG - a.result.expectDMG)[0]?.skill; } else if (typeof skill === 'string') { - const MySkill = this.skills.find(s => s.type === skill); + const MySkill = this.find_skill('type', skill); if (!MySkill) return []; return this.calc_differences(buffs, MySkill); @@ -479,7 +495,7 @@ export class Calculator { } get_ATK(skill, usefulBuffs) { let ATK = this.get('攻击力', this.initial_properties.ATK, skill, usefulBuffs, true); - ATK = Math.max(0, Math.min(ATK, 10000)); + ATK = min_max(0, 10000, ATK); logger.debug(`攻击力:${ATK}`); return ATK; } @@ -490,29 +506,37 @@ export class Calculator { } get_CRITRate(skill, usefulBuffs) { let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs); - CRITRate = Math.max(0, Math.min(CRITRate, 1)); + CRITRate = min_max(0, 1, CRITRate); logger.debug(`暴击率:${CRITRate}`); return CRITRate; } get_CRITDMG(skill, usefulBuffs) { - let CRITDMG = this.get('暴击伤害', this.initial_properties.CRITDMG + 1, skill, usefulBuffs); - CRITDMG = Math.max(0, Math.min(CRITDMG, 5)); + let CRITDMG = this.get('暴击伤害', this.initial_properties.CRITDMG, skill, usefulBuffs); + CRITDMG = min_max(0, 5, CRITDMG); logger.debug(`暴击伤害:${CRITDMG}`); return CRITDMG; } get_BoostArea(skill, usefulBuffs) { - const BoostArea = this.get('增伤', 1, skill, usefulBuffs); + let BoostArea = this.get('增伤', 1, skill, usefulBuffs); + BoostArea = min_max(0, 6, BoostArea); logger.debug(`增伤区:${BoostArea}`); return BoostArea; } get_VulnerabilityArea(skill, usefulBuffs) { - const VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs); + let VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs); + VulnerabilityArea = min_max(0.2, 2, VulnerabilityArea); logger.debug(`易伤区:${VulnerabilityArea}`); return VulnerabilityArea; } + get_StunVulnerabilityArea(skill, usefulBuffs) { + let StunVulnerabilityArea = this.get('失衡易伤', 1, skill, usefulBuffs); + StunVulnerabilityArea = min_max(0.2, 5, StunVulnerabilityArea); + StunVulnerabilityArea !== 1 && logger.debug(`失衡易伤区:${StunVulnerabilityArea}`); + return StunVulnerabilityArea; + } get_ResistanceArea(skill, usefulBuffs) { - let ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs); - ResistanceArea = Math.min(2, ResistanceArea); + let ResistanceArea = this.get('无视抗性', 1 - this.enemy.resistance, skill, usefulBuffs); + ResistanceArea = min_max(0, 2, ResistanceArea); logger.debug(`抗性区:${ResistanceArea}`); return ResistanceArea; } @@ -542,7 +566,7 @@ export class Calculator { const PenRatio = this.get_PenRatio(skill, usefulBuffs); const defence = DEF * (1 - IgnoreDEF); const effective_defence = Math.max(0, defence * (1 - PenRatio) - Pen); - const DefenceArea = base / (effective_defence + base); + const DefenceArea = min_max(0, 1, base / (effective_defence + base)); logger.debug(`防御区:${DefenceArea}`); return DefenceArea; } @@ -552,31 +576,37 @@ export class Calculator { return LevelArea; } get_AnomalyProficiency(skill, usefulBuffs) { - let AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs); - AnomalyProficiency = Math.max(0, Math.min(AnomalyProficiency, 1000)); + const AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs); logger.debug(`异常精通:${AnomalyProficiency}`); return AnomalyProficiency; } + get_AnomalyMastery(skill, usefulBuffs) { + let AnomalyMastery = this.get('异常掌控', this.initial_properties.AnomalyMastery, skill, usefulBuffs, true); + AnomalyMastery = min_max(0, 1000, AnomalyMastery); + logger.debug(`异常掌控:${AnomalyMastery}`); + return AnomalyMastery; + } get_AnomalyProficiencyArea(skill, usefulBuffs) { const AnomalyProficiency = this.get_AnomalyProficiency(skill, usefulBuffs); - const AnomalyProficiencyArea = AnomalyProficiency / 100; + const AnomalyProficiencyArea = min_max(0, 10, AnomalyProficiency / 100); logger.debug(`异常精通区:${AnomalyProficiencyArea}`); return AnomalyProficiencyArea; } get_AnomalyBoostArea(skill, usefulBuffs) { - const AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs); - AnomalyBoostArea && logger.debug(`异常增伤区:${AnomalyBoostArea}`); + let AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs); + AnomalyBoostArea = min_max(0, 3, AnomalyBoostArea); + AnomalyBoostArea !== 1 && logger.debug(`异常增伤区:${AnomalyBoostArea}`); return AnomalyBoostArea; } get_AnomalyCRITRate(skill, usefulBuffs) { let AnomalyCRITRate = this.get('异常暴击率', 0, skill, usefulBuffs); - AnomalyCRITRate = Math.max(0, Math.min(AnomalyCRITRate, 1)); + AnomalyCRITRate = min_max(0, 1, AnomalyCRITRate); AnomalyCRITRate && logger.debug(`异常暴击率:${AnomalyCRITRate}`); return AnomalyCRITRate; } get_AnomalyCRITDMG(skill, usefulBuffs) { - let AnomalyCRITDMG = this.get('异常暴击伤害', 1, skill, usefulBuffs); - AnomalyCRITDMG = Math.max(0, Math.min(AnomalyCRITDMG, 5)); + let AnomalyCRITDMG = this.get('异常暴击伤害', 0, skill, usefulBuffs); + AnomalyCRITDMG = min_max(0, 5, AnomalyCRITDMG); AnomalyCRITDMG && logger.debug(`异常暴击伤害:${AnomalyCRITDMG}`); return AnomalyCRITDMG; } @@ -587,26 +617,36 @@ export class Calculator { } get_HP(skill, usefulBuffs) { let HP = this.get('生命值', this.initial_properties.HP, skill, usefulBuffs, true); - HP = Math.max(0, Math.min(HP, 100000)); + HP = min_max(0, 100000, HP); logger.debug(`生命值:${HP}`); return HP; } get_DEF(skill, usefulBuffs) { let DEF = this.get('防御力', this.initial_properties.DEF, skill, usefulBuffs, true); - DEF = Math.max(0, Math.min(DEF, 1000)); + DEF = min_max(0, 1000, DEF); logger.debug(`防御力:${DEF}`); return DEF; } get_Impact(skill, usefulBuffs) { let Impact = this.get('冲击力', this.initial_properties.Impact, skill, usefulBuffs, true); - Impact = Math.max(0, Math.min(Impact, 1000)); + Impact = min_max(0, 1000, Impact); logger.debug(`冲击力:${Impact}`); return Impact; } - get_AnomalyMastery(skill, usefulBuffs) { - let AnomalyMastery = this.get('异常掌控', this.initial_properties.AnomalyMastery, skill, usefulBuffs, true); - AnomalyMastery = Math.max(0, Math.min(AnomalyMastery, 1000)); - logger.debug(`异常掌控:${AnomalyMastery}`); - return AnomalyMastery; + get_SheerForce(skill, usefulBuffs) { + let SheerForce = Math.trunc(this.get_ATK(skill, usefulBuffs) * 0.3); + SheerForce = this.get('贯穿力', SheerForce, skill, usefulBuffs, true); + SheerForce = min_max(0, 10000, SheerForce); + logger.debug(`贯穿力:${SheerForce}`); + return SheerForce; + } + get_SheerBoostArea(skill, usefulBuffs) { + let SheerBoostArea = this.get('贯穿增伤', 1, skill, usefulBuffs); + SheerBoostArea = min_max(0.2, 9, SheerBoostArea); + SheerBoostArea !== 1 && logger.debug(`贯穿增伤区:${SheerBoostArea}`); + return SheerBoostArea; } } +function min_max(min, max, value) { + return Math.min(Math.max(value, min), max); +} diff --git a/model/damage/Calculator.ts b/model/damage/Calculator.ts index f348d0d..b76dd00 100644 --- a/model/damage/Calculator.ts +++ b/model/damage/Calculator.ts @@ -48,6 +48,10 @@ export interface skill { isMain?: boolean /** 角色面板伤害统计中是否隐藏显示 */ isHide?: boolean + /** 是否为异常伤害。自动判断 */ + isAnomalyDMG?: boolean + /** 是否为贯穿伤害。自动判断 */ + isSheerDMG?: boolean /** 禁用伤害计算cache */ banCache?: boolean /** 是否计算该技能 */ @@ -105,12 +109,16 @@ export interface damage { ResistanceArea: number /** 防御区 */ DefenceArea: number + /** 失衡易伤区 */ + StunVulnerabilityArea: number /** 异常精通区 */ AnomalyProficiencyArea: number /** 异常增伤区 */ AnomalyBoostArea: number /** 等级区 */ LevelArea: number + /** 贯穿增伤区 */ + SheerBoostArea: number } /** 伤害结果 */ result: { @@ -204,7 +212,7 @@ export class Calculator { this.enemy = { level: this.avatar.level, basicDEF: 50, - resistance: 0.2 + resistance: -0.2 } } @@ -242,10 +250,17 @@ export class Calculator { const num = skill.check as unknown as number skill.check = oriSkill.check = ({ avatar }) => avatar.rank >= num } + skill.isAnomalyDMG ??= oriSkill.isAnomalyDMG = typeof anomalyEnum[skill.type.slice(0, 2) as anomaly] === 'number' + skill.isSheerDMG ??= oriSkill.isSheerDMG = this.avatar.avatar_profession === 6 && elementType2element(this.avatar.element_type) === skill.element && !skill.isAnomalyDMG this.skills.push(skill) return this.skills } + /** 查找指定已注册技能 */ + find_skill(key: T, value: skill[T]) { + return this.skills.find(skill => skill[key] === value) + } + /** * 计算已注册的技能伤害 * @param type 技能类型名 @@ -258,7 +273,7 @@ export class Calculator { calc_skill(skill: skill): damage calc_skill(skill: skill['type'] | skill) { if (typeof skill === 'string') { - const MySkill = this.skills.find(s => s.type === skill) + const MySkill = this.find_skill('type', skill) if (!MySkill) return return this.calc_skill(MySkill) } @@ -275,7 +290,7 @@ export class Calculator { return dmg } const props = this.props = skill.props || {} - /** 缩小筛选范围 */ + // 缩小筛选范围 const usefulBuffs = this.buffM.filter({ element: skill.element, range: [skill.type], @@ -284,7 +299,9 @@ export class Calculator { const areas = {} as damage['areas'] if (skill.before) skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas }) logger.debug(`有效buff*${usefulBuffs.length}/${this.buffM.buffs.length}`) - const isAnomaly = typeof anomalyEnum[skill.type.slice(0, 2) as anomaly] === 'number' + const { isAnomalyDMG = false, isSheerDMG = false } = skill + + // 基础伤害区 if (!areas.BasicArea) { let Multiplier = props.倍率 if (!Multiplier) { @@ -306,7 +323,7 @@ export class Calculator { Multiplier = this.get_SkillMultiplier(skill.type) logger.warn('无效的技能倍率:', skill) } - } else if (isAnomaly) { + } else if (isAnomalyDMG) { Multiplier = ( skill.type.startsWith('紊乱') ? this.get_DiscoverMultiplier(skill) : @@ -321,42 +338,57 @@ export class Calculator { if (ExtraMultiplier) logger.debug(`最终倍率:${Multiplier}`) } props.倍率 = Multiplier - this.get_ATK(skill, usefulBuffs) - areas.BasicArea = props.攻击力! * props.倍率 + if (isSheerDMG) { + areas.BasicArea = this.get_SheerForce(skill, usefulBuffs) * Multiplier + } else { + areas.BasicArea = this.get_ATK(skill, usefulBuffs) * Multiplier + } } logger.debug(`基础伤害区:${areas.BasicArea}`) - if (isAnomaly) { - areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs) - areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs) - areas.LevelArea ??= this.get_LevelArea() - if (!skill.type.startsWith('紊乱')) { // 紊乱暂无异常暴击区 - props.异常暴击率 ??= this.get_AnomalyCRITRate(skill, usefulBuffs) - props.异常暴击伤害 ??= this.get_AnomalyCRITDMG(skill, usefulBuffs) - areas.CriticalArea ??= 1 + props.异常暴击率! * (props.异常暴击伤害! - 1) + // 暴击区 + let CRITRate = 0, CRITDMG = 0 + if (!areas.CriticalArea) { + if (isAnomalyDMG) { + if (!skill.type.startsWith('紊乱')) { // 紊乱暂无异常暴击区 + CRITRate = this.get_AnomalyCRITRate(skill, usefulBuffs) + CRITDMG = this.get_AnomalyCRITDMG(skill, usefulBuffs) + } + } else { + CRITRate = this.get_CRITRate(skill, usefulBuffs) + CRITDMG = this.get_CRITDMG(skill, usefulBuffs) } - } else { - props.暴击率 ??= this.get_CRITRate(skill, usefulBuffs) - props.暴击伤害 ??= this.get_CRITDMG(skill, usefulBuffs) - areas.CriticalArea ??= 1 + props.暴击率! * (props.暴击伤害! - 1) + areas.CriticalArea = 1 + CRITRate * CRITDMG } - areas.CriticalArea ??= 1 - logger.debug(`暴击期望:${areas.CriticalArea}`) + areas.CriticalArea !== 1 && logger.debug(`暴击期望:${areas.CriticalArea}`) + // 通用乘区 areas.BoostArea ??= this.get_BoostArea(skill, usefulBuffs) areas.VulnerabilityArea ??= this.get_VulnerabilityArea(skill, usefulBuffs) areas.ResistanceArea ??= this.get_ResistanceArea(skill, usefulBuffs) - areas.DefenceArea ??= this.get_DefenceArea(skill, usefulBuffs) + areas.DefenceArea ??= isSheerDMG ? 1 : this.get_DefenceArea(skill, usefulBuffs) + areas.StunVulnerabilityArea ??= this.get_StunVulnerabilityArea(skill, usefulBuffs) + // 异常乘区 + if (isAnomalyDMG) { + areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs) + areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs) + areas.LevelArea ??= this.get_LevelArea() + } + // 贯穿乘区 + if (isSheerDMG) { + areas.SheerBoostArea ??= this.get_SheerBoostArea(skill, usefulBuffs) + } + const { BasicArea, CriticalArea, BoostArea, VulnerabilityArea, ResistanceArea, DefenceArea, - AnomalyProficiencyArea, LevelArea, AnomalyBoostArea + AnomalyProficiencyArea, LevelArea, AnomalyBoostArea, StunVulnerabilityArea, SheerBoostArea = 1 } = areas - const { 暴击伤害, 异常暴击伤害 } = props - const result: damage['result'] = isAnomaly ? + const commonArea = BasicArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * StunVulnerabilityArea + const result: damage['result'] = isAnomalyDMG ? { - critDMG: (CriticalArea !== 1) ? BasicArea * 异常暴击伤害! * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, - expectDMG: BasicArea * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea + critDMG: (CriticalArea !== 1) ? commonArea * (CRITDMG + 1) * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, + expectDMG: commonArea * CriticalArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea } : { - critDMG: BasicArea * 暴击伤害! * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, - expectDMG: BasicArea * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea + critDMG: commonArea * (CRITDMG + 1) * SheerBoostArea, + expectDMG: commonArea * CriticalArea * SheerBoostArea } const damage: damage = { skill, usefulBuffs: _.sortBy(Array.from(this.usefulBuffResults.values()), ['type', 'value']).reverse(), props, areas, result } this.usefulBuffResults.clear() @@ -539,10 +571,10 @@ export class Calculator { skill?: skill['type'] | skill ): { add: B, del: B, damage: damage, difference: number }[][] { if (!skill) { - skill = this.skills.find((skill) => skill.isMain) // 主技能 + skill = this.find_skill('isMain', true) // 主技能 || this.calc().sort((a, b) => b.result.expectDMG - a.result.expectDMG)[0]?.skill // 伤害最高技能 } else if (typeof skill === 'string') { - const MySkill = this.skills.find(s => s.type === skill) + const MySkill = this.find_skill('type', skill) if (!MySkill) return [] return this.calc_differences(buffs, MySkill) } @@ -724,67 +756,77 @@ export class Calculator { } /** 攻击力 */ - get_ATK(skill: skill, usefulBuffs: buff[]) { + 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)) + ATK = min_max(0, 10000, ATK) logger.debug(`攻击力:${ATK}`) return ATK } /** 额外倍率 */ - get_ExtraMultiplier(skill: skill, usefulBuffs: buff[]) { + 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) - CRITRate = Math.max(0, Math.min(CRITRate, 1)) + CRITRate = min_max(0, 1, CRITRate) 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)) + get_CRITDMG(skill?: skill, usefulBuffs?: buff[]) { + let CRITDMG = this.get('暴击伤害', this.initial_properties.CRITDMG, skill, usefulBuffs) + CRITDMG = min_max(0, 5, CRITDMG) logger.debug(`暴击伤害:${CRITDMG}`) return CRITDMG } /** 增伤区 */ - get_BoostArea(skill: skill, usefulBuffs: buff[]) { - const BoostArea = this.get('增伤', 1, skill, usefulBuffs) + get_BoostArea(skill?: skill, usefulBuffs?: buff[]) { + let BoostArea = this.get('增伤', 1, skill, usefulBuffs) + BoostArea = min_max(0, 6, BoostArea) logger.debug(`增伤区:${BoostArea}`) return BoostArea } - /** 易伤区 */ - get_VulnerabilityArea(skill: skill, usefulBuffs: buff[]) { - const VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs) + /** (减)易伤区 */ + get_VulnerabilityArea(skill?: skill, usefulBuffs?: buff[]) { + let VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs) + VulnerabilityArea = min_max(0.2, 2, VulnerabilityArea) logger.debug(`易伤区:${VulnerabilityArea}`) return VulnerabilityArea } + /** 失衡易伤区 */ + get_StunVulnerabilityArea(skill?: skill, usefulBuffs?: buff[]) { + let StunVulnerabilityArea = this.get('失衡易伤', 1, skill, usefulBuffs) + StunVulnerabilityArea = min_max(0.2, 5, StunVulnerabilityArea) + StunVulnerabilityArea !== 1 && logger.debug(`失衡易伤区:${StunVulnerabilityArea}`) + return StunVulnerabilityArea + } + /** 抗性区 */ - get_ResistanceArea(skill: skill, usefulBuffs: buff[]) { - let ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs) - ResistanceArea = Math.min(2, ResistanceArea) + get_ResistanceArea(skill?: skill, usefulBuffs?: buff[]) { + let ResistanceArea = this.get('无视抗性', 1 - this.enemy.resistance, skill, usefulBuffs) + ResistanceArea = min_max(0, 2, ResistanceArea) logger.debug(`抗性区:${ResistanceArea}`) return ResistanceArea } /** 无视防御 */ - get_IgnoreDEF(skill: skill, usefulBuffs: buff[]) { + get_IgnoreDEF(skill?: skill, usefulBuffs?: buff[]) { const IgnoreDEF = this.get('无视防御', 0, skill, usefulBuffs) IgnoreDEF && logger.debug(`无视防御:${IgnoreDEF}`) return IgnoreDEF } /** 穿透值 */ - get_Pen(skill: skill, usefulBuffs: buff[]) { + get_Pen(skill?: skill, usefulBuffs?: buff[]) { let Pen = this.get('穿透值', this.initial_properties.Pen, skill, usefulBuffs) Pen = Math.min(Pen, 1000) Pen && logger.debug(`穿透值:${Pen}`) @@ -792,7 +834,7 @@ export class Calculator { } /** 穿透率 */ - get_PenRatio(skill: skill, usefulBuffs: buff[]) { + get_PenRatio(skill?: skill, usefulBuffs?: buff[]) { let PenRatio = this.get('穿透率', this.initial_properties.PenRatio, skill, usefulBuffs) PenRatio = Math.min(PenRatio, 2) PenRatio && logger.debug(`穿透率:${PenRatio}`) @@ -800,7 +842,7 @@ export class Calculator { } /** 防御区 */ - get_DefenceArea(skill: skill, usefulBuffs: buff[]) { + 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) @@ -813,7 +855,7 @@ export class Calculator { const defence = DEF * (1 - IgnoreDEF) /** 有效防御 */ const effective_defence = Math.max(0, defence * (1 - PenRatio) - Pen) - const DefenceArea = base / (effective_defence + base) + const DefenceArea = min_max(0, 1, base / (effective_defence + base)) logger.debug(`防御区:${DefenceArea}`) return DefenceArea } @@ -826,81 +868,103 @@ export class Calculator { } /** 异常精通 */ - get_AnomalyProficiency(skill: skill, usefulBuffs: buff[]) { - let AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs) - AnomalyProficiency = Math.max(0, Math.min(AnomalyProficiency, 1000)) + get_AnomalyProficiency(skill?: skill, usefulBuffs?: buff[]) { + const AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs) logger.debug(`异常精通:${AnomalyProficiency}`) return AnomalyProficiency } + /** 异常掌控 */ + get_AnomalyMastery(skill?: skill, usefulBuffs?: buff[]) { + let AnomalyMastery = this.get('异常掌控', this.initial_properties.AnomalyMastery, skill, usefulBuffs, true) + AnomalyMastery = min_max(0, 1000, AnomalyMastery) + logger.debug(`异常掌控:${AnomalyMastery}`) + return AnomalyMastery + } + /** 异常精通区 */ - get_AnomalyProficiencyArea(skill: skill, usefulBuffs: buff[]) { + get_AnomalyProficiencyArea(skill?: skill, usefulBuffs?: buff[]) { const AnomalyProficiency = this.get_AnomalyProficiency(skill, usefulBuffs) - const AnomalyProficiencyArea = AnomalyProficiency / 100 + const AnomalyProficiencyArea = min_max(0, 10, AnomalyProficiency / 100) logger.debug(`异常精通区:${AnomalyProficiencyArea}`) return AnomalyProficiencyArea } /** 异常增伤区 */ - get_AnomalyBoostArea(skill: skill, usefulBuffs: buff[]) { - const AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs) - AnomalyBoostArea && logger.debug(`异常增伤区:${AnomalyBoostArea}`) + get_AnomalyBoostArea(skill?: skill, usefulBuffs?: buff[]) { + let AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs) + AnomalyBoostArea = min_max(0, 3, AnomalyBoostArea) + AnomalyBoostArea !== 1 && logger.debug(`异常增伤区:${AnomalyBoostArea}`) return AnomalyBoostArea } /** 异常暴击率 */ - get_AnomalyCRITRate(skill: skill, usefulBuffs: buff[]) { + get_AnomalyCRITRate(skill?: skill, usefulBuffs?: buff[]) { let AnomalyCRITRate = this.get('异常暴击率', 0, skill, usefulBuffs) - AnomalyCRITRate = Math.max(0, Math.min(AnomalyCRITRate, 1)) + AnomalyCRITRate = min_max(0, 1, AnomalyCRITRate) AnomalyCRITRate && 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)) + get_AnomalyCRITDMG(skill?: skill, usefulBuffs?: buff[]) { + let AnomalyCRITDMG = this.get('异常暴击伤害', 0, skill, usefulBuffs) + AnomalyCRITDMG = min_max(0, 5, AnomalyCRITDMG) AnomalyCRITDMG && logger.debug(`异常暴击伤害:${AnomalyCRITDMG}`) return AnomalyCRITDMG } /** 异常持续时间 */ - get_AnomalyDuration(skill: skill, usefulBuffs: buff[], duration: number = 0) { + 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[]) { + 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)) + HP = min_max(0, 100000, HP) logger.debug(`生命值:${HP}`) return HP } /** 防御力 */ - get_DEF(skill: skill, usefulBuffs: buff[]) { + 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)) + DEF = min_max(0, 1000, DEF) logger.debug(`防御力:${DEF}`) return DEF } /** 冲击力 */ - get_Impact(skill: skill, usefulBuffs: buff[]) { + 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)) + Impact = min_max(0, 1000, Impact) 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 + /** 贯穿力 */ + get_SheerForce(skill?: skill, usefulBuffs?: buff[]) { + // 默认取 攻击力*0.3 + let SheerForce = Math.trunc(this.get_ATK(skill, usefulBuffs) * 0.3) + SheerForce = this.get('贯穿力', SheerForce, skill, usefulBuffs, true) + SheerForce = min_max(0, 10000, SheerForce) + logger.debug(`贯穿力:${SheerForce}`) + return SheerForce } + /** 贯穿增伤区 */ + get_SheerBoostArea(skill?: skill, usefulBuffs?: buff[]) { + let SheerBoostArea = this.get('贯穿增伤', 1, skill, usefulBuffs) + SheerBoostArea = min_max(0.2, 9, SheerBoostArea) + SheerBoostArea !== 1 && logger.debug(`贯穿增伤区:${SheerBoostArea}`) + return SheerBoostArea + } + +} + +function min_max(min: number, max: number, value: number) { + return Math.min(Math.max(value, min), max) } \ No newline at end of file diff --git a/model/damage/README.md b/model/damage/README.md index 5c78976..1ffe862 100644 --- a/model/damage/README.md +++ b/model/damage/README.md @@ -1,4 +1,4 @@ -# 词条权重自定义 +# 评分权重自定义 ## 方法一:预设方法(推荐) @@ -24,7 +24,7 @@ > 将崽底层日志模式切换为**debug**模式,可在控制台查看评分计算详细过程;且会自动监听现有评分计算文件实时热更新。可按需开启 -在函数体中,可根据玩家角色数据**动态选用**不同的权重方案。参考[薇薇安评分规则](./character/薇薇安/score.js) +在函数体中,可根据玩家角色数据**动态选用**不同的权重方案。参考[爱丽丝评分规则](./character/爱丽丝/score.js) - 函数参数:[ZZZAvatarInfo](../avatar.js#L173)(角色数据) @@ -72,7 +72,7 @@ ### 认识buff -每个buff由各项[buff参数](./BuffManager.ts#L40)组成,重要参数: +每个buff由各项[buff参数](./BuffManager.ts#L42)组成,重要参数: ```js { @@ -88,7 +88,7 @@ * - 一般情况下此值即为提高值 * - 当buff增益类型为攻击力/冲击力/异常精通/异常掌控/防御力/生命值时,若此值<1,则将此值理解为初始属性的百分比提高 * @string - * 角色自身的buff提高值可能随技能/天赋等级提高而提高,此时可以于data.json的"buff"中添加对应的倍率信息(同上支持百分比提高),此时value即为键名,其首字母必须为对应技能的基类(参考技能类型命名标准) + * 角色自身的buff提高值可能随技能/天赋等级提高而提高,此时可以于data.json的"buff"中添加倍率数组(同上支持百分比提高),此时value即为键名,其首字母必须为对应技能的基类(参考技能类型命名标准) * @array * 根据buff.source自动选择对应等级/星级的值(同上支持百分比提高),支持的source: * - 音擎:音擎星级(进阶) @@ -99,10 +99,25 @@ value: number | string | number[] | Function /** * Buff增益技能类型生效范围;参考技能类型命名标准 - * - 当技能参数不存在redirect时,range作用范围向后覆盖 + * - 当技能参数不存在redirect时,range作用范围向后覆盖生效 * - 当技能参数存在redirect时,range与type全匹配时生效,redirect向后覆盖生效 + * - 若需全匹配的精细操作,可使用include与exclude参数 */ range?: string[] + /** + * Buff增益技能类型生效技能 + * - 不同于range,仅全匹配type时该值生效,不会向后覆盖生效 + * - 无range且无include则该buff对exclude以外的全部技能生效 + * - range与include符合其一则认为buff生效 + * - 当技能参数存在redirect时,range与include的区别在于include不会尝试匹配redirect + */ + include?: string[] + /** + * Buff增益技能类型排除技能 + * - 与include相同,仅全匹配type时该值生效,不会向后覆盖生效 + * - 优先级高于range与include + */ + exclude?: string[] /** Buff增益属性类型,无则对全部属性生效 */ element?: element | element[] } @@ -118,11 +133,15 @@ - **value**:Buff增益值。具体解释如上述 -- **range**:Buff增益技能类型范围,匹配技能type。该参数用于鉴别不同buff的[生效范围](#技能类型命名对buff作用的影响)(比如只对普攻生效),[填写方法](#技能类型命名标准)会在技能属性中详细说明 +- **range**:Buff增益技能类型范围,模糊匹配技能type。该参数用于鉴别不同buff的[生效范围](#技能类型命名对buff作用的影响),[填写方法](#技能类型命名标准)会在技能属性中详细说明 + +- **include**:Buff增益技能类型生效技能,全匹配技能type。总的来说,对于作用于某一类的buff(比如对所有普攻/强E生效)应使用range;而明确指定了生效技能的buff,应使用include + +- **exclude**:Buff增益技能类型排除技能,全匹配技能type。显式指定使该buff不作用于某些技能 - **element**:Buff增益属性类型,可为字符串或字符串数组。该参数用于鉴别不同buff的生效属性(比如只对冰属性伤害生效)。查看[属性类型](./BuffManager.ts#L5) -- buff存在更多的参数用于处理各种情况,详见[buff参数](./BuffManager.ts#L40) +完整参数详见[buff参数](./BuffManager.ts#L42) ### 注册buff @@ -169,7 +188,7 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动 - 主词条的提升会自动注册,无需处理 -- 二件套效果只有**属性伤害提升**和**局外面板以外的效果**(如[追加攻击]和[冲刺攻击]造成的伤害提升)需要注册,其他已包含于初始属性 +- 二件套效果只有**属性伤害提升**和**局外面板以外的效果**(如驱动盘如影相随[追加攻击]和[冲刺攻击]造成的伤害提升)需要注册,其他已包含于初始属性无需注册 - 四件套效果需要单独注册 @@ -179,7 +198,7 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动 [角色buff两种导出方式实例](./character/星见雅/calc.js) -角色buff分为影画、核心被动、额外能力、技能此四个来源 +角色buff分为**影画、核心被动、额外能力、技能**四个来源 - **影画·Rank** @@ -187,7 +206,7 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动 - **核心被动·Talent** - 核心被动buff参数中的**buff增益值**可能随核心技等级提升而提升,此时**value**的类型对应字符串情况,并且需要于data.json中添加对应的倍率信息,你可参考[安东伤害计算](./character/安东/calc.js#L17)的处理 + 核心被动buff参数中的**buff增益值**可能随核心技等级提升而提升,此时**value**的可选两种类型:1. 直接填写倍率`数组`,参考[仪玄伤害计算](./character/仪玄/calc.js#L33);2. 填写`字符串`,并于data.json中添加倍率数组,参考[安东伤害计算](./character/安东/calc.js#L17) - **额外能力·Addition** @@ -281,7 +300,10 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动 > - 追加攻击 > - 直接以“追加攻击”命名 -> - 追加攻击不被认为是一种新的输出手段(技能),它更类似于原直伤输出的一个特殊属性,使该伤害能同时享受**对其原所属技能类型生效**的增益和**仅对追加攻击生效**的增益,故一般不将其单独作为技能类型而是在技能属性的重定向参数中添加**追加攻击**参数,请参考[技能重定向说明注意事项](#技能类型命名对Buff作用的影响) +> - 追加攻击不被认为是一种新的输出手段(技能),它更类似于原直伤输出的一个额外标签,使该伤害能同时吃到**对其原所属技能类型生效**的增益和**仅对追加攻击生效**的增益,故一般不将其单独作为技能类型而是在技能属性的重定向参数中添加**追加攻击**参数,请参考[技能重定向说明注意事项](#技能类型命名对Buff作用的影响) + +> - 贯穿伤害 +> - 命名等同于直伤规则,无需作特殊处理,底层会自动识别是否为贯穿伤害 #### 技能类型命名解释说明 @@ -333,7 +355,7 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲 > 需要注意的是:即使出现`“X"(造成的伤害)被视为“Y”(伤害)`,对**Y**类型的增益**X**不一定能吃到,视具体情况变化 - > 对于被视为**追加攻击**伤害的技能,其重定向参数一般为包含其**原技能类型**和**追加攻击**的数组,因为它可以享受到分别对两者生效的不同增益(以游戏内具体情况为准),参考[**零号·安比**伤害计算文件](./character/零号·安比/calc.js#L51) + > 对于被视为**追加攻击**伤害的技能,其重定向参数一般为包含其**原技能类型**和**追加攻击**的数组,因为它可以吃到分别对两者生效的不同增益(以游戏内具体情况为准),参考[**零号·安比**伤害计算文件](./character/零号·安比/calc.js#L50) ### 技能倍率 @@ -420,11 +442,11 @@ counter(145.7, 159, 16) ### 自定义敌方属性 -可以用 **等级、1级基础防御力、抗性区常量** 三个参数来替代**防御力、抗性、弱点**: +可以用 **等级、1级基础防御力、抗性** 三个参数来衡量**防御力、抗性、弱点**: - **等级、1级基础防御力**:根据这两个参数可计算出敌方防御力。插件默认敌方等级=角色等级,1级基础防御力=50 -- **抗性区常量**:敌方存在对应的弱点时,此值为 **0.2**;敌方存在对应的抗性时,此值为 **-0.2**;既不抗也不弱,此值为 **0**。插件默认抗性区常量=0.2 +- **抗性**:敌方存在对应的弱点时,此值为 **-0.2**;敌方存在对应的抗性时,此值为 **0.2**;既不抗也不弱,此值为 **0**。插件默认抗性=-0.2 通过在角色伤害计算文件中导出**calc**函数,调用函数参数中calc的[defEnemy](./Calculator.ts)方法,你可以对此三个参数进行自定义 @@ -476,16 +498,21 @@ export function calc(buffM, calc, avatar) { - [x] 伤害计算自定义支持 - [x] 伤害计算说明文档 - [x] 全角色伤害计算 -- [x] 词条收益分析 +- [x] 驱动盘评分计算 +- [x] 驱动盘词条差异计算 +- [ ] 伤害计算buff指令热注入 +- [ ] 伤害计算敌人自定义 +- [ ] 组队伤害计算 - [ ] 失衡值累积计算 - [ ] ~~异常积蓄值计算~~ - [ ] ~~治疗量计算~~ -- [ ] ~~组队伤害计算~~ ## 鸣谢 -感谢[紫罗兰打烊啦](https://www.miyoushe.com/zzz/accountCenter/postList?id=279259320)对伤害计算细则的评测指正 +感谢[紫罗兰打烊啦](https://www.miyoushe.com/zzz/accountCenter/postList?id=279259320)对伤害计算细则的评测探讨指正 + +感谢[666bj](https://www.miyoushe.com/zzz/accountCenter/postList?id=291537017)对绝区零数据机制研究做出的贡献 --- -[伤害计算]:https://www.miyoushe.com/zzz/article/55265618 \ No newline at end of file +[伤害计算]:https://www.miyoushe.com/zzz/article/66274506 \ No newline at end of file diff --git a/model/damage/avatar.js b/model/damage/avatar.js index abb32ff..f3d8153 100644 --- a/model/damage/avatar.js +++ b/model/damage/avatar.js @@ -103,7 +103,7 @@ async function importFile(type, name, isWatch = false) { logger.error(`导入${type}/${name}.js错误:`, e); } } -await init(); +init(); export function avatar_calc(avatar) { const m = calcFnc.character[avatar.id]; if (!m) diff --git a/model/damage/avatar.ts b/model/damage/avatar.ts index c84b88c..12af0bd 100644 --- a/model/damage/avatar.ts +++ b/model/damage/avatar.ts @@ -136,7 +136,7 @@ async function importFile(type: 'weapon' | 'set', name: string, isWatch = false) } } -await init() +init() /** 角色计算实例 */ export function avatar_calc(avatar: ZZZAvatarInfo) { diff --git a/model/damage/character/仪玄/calc.js b/model/damage/character/仪玄/calc.js new file mode 100644 index 0000000..f88249c --- /dev/null +++ b/model/damage/character/仪玄/calc.js @@ -0,0 +1,103 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '1影', + type: '暴击率', + value: 0.1 + }, + { + name: '2影', + type: '无视抗性', + value: 0.15, + range: ['RZ', 'EQ'] + }, + { + name: '4影', + type: '增伤', + value: 0.3 * 2, + include: ['EQX', 'EQM'] + }, + { + name: '6影', + type: '贯穿增伤', + value: 0.2 + }, + { + name: '核心被动:术法宗师', + type: '贯穿力', + value: ({ calc }) => Math.trunc(calc.get_HP() * 0.1) + }, + { + name: '核心被动:术法宗师', + type: '增伤', + value: [0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6], + range: ['EQ', 'LT', 'RL', 'RZ'], + include: ['AXQ', 'AXZ'] + }, + { + name: '额外能力:玄墨暗涌', + type: '暴击伤害', + value: 0.4 + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '侵蚀每段', type: '侵蚀' }, + { name: '普攻:霄云劲五段', type: 'AP5' }, + // { name: '普攻:墨影凝云', type: 'AXP' }, + { + name: '普攻:玄墨极阵(满蓄)', + type: 'AXQ', + after: ({ damage }) => damage.add('AXZ') + }, + { + name: '普攻:青溟震击', + type: 'AXZ', + isHide: true + }, + { + name: '蓄力普攻:凝云术(满蓄)', + type: 'EQX', + after: ({ damage }) => damage.add('EQM'), + }, + { name: '闪避反击:除祟一击', type: 'CF' }, + { name: '强化E1:墨痕化形', type: 'EQP1P' }, + { + name: '强化E1:墨痕化形(满蓄或格挡)追加攻击', + type: 'EQP1Z', + isHide: true + }, + { + name: '强化E1:墨痕化形(满蓄或格挡)', + type: 'EQP1X', + dmg: (calc) => { + const dmg = calc.calc_skill({ + ...calc.find_skill('type', 'EQP1P'), + banCache: true, + after: ({ damage }) => damage.add('EQP1Z') + }) + return dmg + } + }, + { name: '强化E2:霄云迅击-破', type: 'EQP2' }, + { name: '强化E3:青溟震击-破', type: 'EQP3' }, + { + name: '强化E:墨烬影消', + type: 'EQM', + isHide: true + }, + { + name: '2影强化E:符法千重-破', + type: 'EQF', + check: 2, + multiplier: 12 + }, + { name: '连携技:玄墨迅击', type: 'RL' }, + { name: '终结技:青溟云影', type: 'RZQ' }, + { + name: '终结技:符法千重', + type: 'RZF', + isMain: true + } +] \ No newline at end of file diff --git a/model/damage/character/仪玄/data.json b/model/damage/character/仪玄/data.json new file mode 100644 index 0000000..fd130e9 --- /dev/null +++ b/model/damage/character/仪玄/data.json @@ -0,0 +1,43 @@ +{ + "skill": { + "AP5": [ + 1.137,1.241,1.345,1.449,1.553,1.657,1.761,1.865,1.969,2.073,2.177,2.281,2.385,2.489,2.593,2.697 + ], + "AXQ": [ + 3.052,3.33,3.608,3.886,4.164,4.442,4.72,4.998,5.276,5.554,5.832,6.11,6.388,6.666,6.944,7.222 + ], + "AXZ": [ + 1.106,1.207,1.308,1.409,1.51,1.611,1.712,1.813,1.914,2.015,2.116,2.217,2.318,2.419,2.52,2.621 + ], + "CF": [ + 2.196,2.396,2.596,2.796,2.996,3.196,3.396,3.596,3.796,3.996,4.196,4.396,4.596,4.796,4.996,5.196 + ], + "EQX": [ + 6.718,7.329,7.94,8.551,9.162,9.773,10.384,10.995,11.606,12.217,12.828,13.439,14.05,14.661,15.272,15.883 + ], + "EQP1P": [ + 3.003,3.276,3.549,3.822,4.095,4.368,4.641,4.914,5.187,5.46,5.733,6.006,6.279,6.552,6.825,7.098 + ], + "EQP1Z": [ + 1.496,1.632,1.768,1.904,2.04,2.176,2.312,2.448,2.584,2.72,2.856,2.992,3.128,3.264,3.4,3.536 + ], + "EQP2": [ + 3.706,4.043,4.38,4.717,5.054,5.391,5.728,6.065,6.402,6.739,7.076,7.413,7.75,8.087,8.424,8.761 + ], + "EQP3": [ + 4.264,4.652,5.04,5.428,5.816,6.204,6.592,6.98,7.368,7.756,8.144,8.532,8.92,9.308,9.696,10.084 + ], + "EQM": [ + 2.343,2.556,2.769,2.982,3.195,3.408,3.621,3.834,4.047,4.26,4.473,4.686,4.899,5.112,5.325,5.538 + ], + "RL": [ + 5.331,5.816,6.301,6.786,7.271,7.756,8.241,8.726,9.211,9.696,10.181,10.666,11.151,11.636,12.121,12.606 + ], + "RZQ": [ + 18.534,20.219,21.904,23.589,25.274,26.959,28.644,30.329,32.014,33.699,35.384,37.069,38.754,40.439,42.124,43.809 + ], + "RZF": [ + 14.662,15.995,17.328,18.661,19.994,21.327,22.66,23.993,25.326,26.659,27.992,29.325,30.658,31.991,33.324,34.657 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/爱丽丝/calc.js b/model/damage/character/爱丽丝/calc.js index 3602eb6..424d619 100644 --- a/model/damage/character/爱丽丝/calc.js +++ b/model/damage/character/爱丽丝/calc.js @@ -47,7 +47,7 @@ export const skills = [ type: 'TC', dmg: (calc) => { const dmg = calc.calc_skill({ - ...calc.skills.find(skill => skill.type === '强击'), + ...calc.find_skill('type', '强击'), banCache: true, after: ({ damage }) => damage.x(0.025) }) diff --git a/model/damage/character/爱丽丝/score.js b/model/damage/character/爱丽丝/score.js index 2e4ab2d..a4321db 100644 --- a/model/damage/character/爱丽丝/score.js +++ b/model/damage/character/爱丽丝/score.js @@ -1,6 +1,7 @@ /** @type {import('../../avatar.ts')['scoreFnc'][string]} */ export default function (avatar) { const props = avatar.initial_properties + // (暴击率 * 2 + 爆伤 >= 200%) 且 (异常精通 < 300) 时转为直伤流规则 if (props.CRITRate * 2 + props.CRITDMG >= 2 && props.AnomalyProficiency < 300) { return ['直伤流', { "生命值百分比": 0, diff --git a/model/damage/character/简/calc.js b/model/damage/character/简/calc.js index 29687ae..3ee340f 100644 --- a/model/damage/character/简/calc.js +++ b/model/damage/character/简/calc.js @@ -22,8 +22,7 @@ export const buffs = [ { name: '4影', type: '异常增伤', - value: 0.18, - exclude: ['紊乱'] + value: 0.18 }, { name: '6影', diff --git a/model/damage/character/薇薇安/calc.js b/model/damage/character/薇薇安/calc.js index 6a89c40..e10afa1 100644 --- a/model/damage/character/薇薇安/calc.js +++ b/model/damage/character/薇薇安/calc.js @@ -73,7 +73,7 @@ export const skills = [ banCache: true, dmg: (calc) => { const dmg = calc.calc_skill({ - ...calc.skills.find(skill => skill.type === '侵蚀·异放'), + ...calc.find_skill('type', '侵蚀·异放'), after: ({ damage }) => damage.x(5) }) return dmg diff --git a/model/damage/set/云岿如我.js b/model/damage/set/云岿如我.js new file mode 100644 index 0000000..02cc09e --- /dev/null +++ b/model/damage/set/云岿如我.js @@ -0,0 +1,13 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '暴击率', + value: 0.04 * 3, + check: 4 + }, + { + type: '贯穿增伤', + value: 0.1, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/weapon/青溟笼舍.js b/model/damage/weapon/青溟笼舍.js new file mode 100644 index 0000000..d17ae2d --- /dev/null +++ b/model/damage/weapon/青溟笼舍.js @@ -0,0 +1,18 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '暴击率', + value: [0.2, 0.23, 0.26, 0.29, 0.32] + }, + { + type: '增伤', + value: [0.08, 0.092, 0.104, 0.116, 0.128].map(v => v * 2), + element: 'Ether' + }, + { + type: '贯穿增伤', + value: [0.1, 0.115, 0.13, 0.145, 0.16].map(v => v * 2), + element: 'Ether', + range: ['RZ', 'EQ'] + } +] \ No newline at end of file diff --git a/resources/map/AnomalyData.json b/resources/map/AnomalyData.json index 59792e3..f05a293 100644 --- a/resources/map/AnomalyData.json +++ b/resources/map/AnomalyData.json @@ -94,5 +94,18 @@ "multiplier": 0.625, "fixed_multiplier": 4.5 } + }, + { + "name": "侵蚀", + "element": "Ether", + "element_type": 205, + "sub_element_type": 2, + "duration": 10, + "interval": 0.5, + "multiplier": 0.625, + "discover": { + "multiplier": 0.625, + "fixed_multiplier": 4.5 + } } ] \ No newline at end of file diff --git a/resources/panel/damage.css b/resources/panel/damage.css index 9b910e4..621d3b7 100644 --- a/resources/panel/damage.css +++ b/resources/panel/damage.css @@ -450,7 +450,7 @@ background-color: rgba(255, 255, 255, 0.12); } .damage .data-list .td { - padding: 0.5em 0.5em; + padding: 0.5em 0; font-size: 0.9em; border-right: 0.1em solid rgba(255, 255, 255, 0.3); overflow: hidden; diff --git a/resources/panel/damage.html b/resources/panel/damage.html index b29bf6a..71c918a 100644 --- a/resources/panel/damage.html +++ b/resources/panel/damage.html @@ -187,7 +187,11 @@
增伤区
易伤区
抗性区
+ {{if damage.skill.isSheerDMG}} +
贯穿增伤区
+ {{else}}
防御区
+ {{/if}}
{{(areas.BasicArea || 1).toFixed(0)}}
@@ -195,9 +199,13 @@
{{(areas.BoostArea || 1).toFixed(2)}}
{{(areas.VulnerabilityArea || 1).toFixed(2)}}
{{(areas.ResistanceArea || 1).toFixed(2)}}
+ {{if damage.skill.isSheerDMG}} +
{{(areas.SheerBoostArea || 1).toFixed(2)}}
+ {{else}}
{{(areas.DefenceArea || 1).toFixed(4)}}
+ {{/if}}
- {{if areas.AnomalyProficiencyArea}} + {{if damage.skill.isAnomalyDMG}}
异常精通区
异常增伤区
@@ -231,7 +239,7 @@
{{buff.name}}
{{buff.source}}
{{buff.type}}
-
{{buff.value % 1 == 0 ? buff.value < 2 ? buff.value.toFixed(2) : buff.value : buff.value.toFixed(2)}}
+
{{buff.value < 2 ? (buff.value.toFixed(2) * 100 + '%') : (buff.value % 1 === 0 ? buff.value : buff.value.toFixed(2))}}
{{/each}} diff --git a/resources/panel/damage.scss b/resources/panel/damage.scss index c1d7e8a..d9ba811 100644 --- a/resources/panel/damage.scss +++ b/resources/panel/damage.scss @@ -420,7 +420,7 @@ } .td { - padding: 0.5em 0.5em; + padding: 0.5em 0; font-size: 0.9em; border-right: 0.1em solid rgba(255, 255, 255, 0.3); overflow: hidden;