支持贯穿伤害计算等;新增仪玄伤害计算

This commit is contained in:
UCPr 2025-08-15 23:51:01 +08:00
parent 2c6e20d846
commit a1a3b2b171
21 changed files with 524 additions and 184 deletions

View file

@ -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
</p>
</details>
## 自定义评分权重、伤害计算
**自定义评分权重**和**自定义伤害计算**请查看[此教程](./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格式一致皆可
## 角色图缺失

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

@ -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<T extends keyof 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)
}

View file

@ -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
[伤害计算]:https://www.miyoushe.com/zzz/article/66274506

View file

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

View file

@ -136,7 +136,7 @@ async function importFile(type: 'weapon' | 'set', name: string, isWatch = false)
}
}
await init()
init()
/** 角色计算实例 */
export function avatar_calc(avatar: ZZZAvatarInfo) {

View file

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

View file

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

View file

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

View file

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

View file

@ -22,8 +22,7 @@ export const buffs = [
{
name: '4影',
type: '异常增伤',
value: 0.18,
exclude: ['紊乱']
value: 0.18
},
{
name: '6影',

View file

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

View file

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

View file

@ -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']
}
]

View file

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

View file

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

View file

@ -187,7 +187,11 @@
<div class="td area-td">增伤区</div>
<div class="td area-td">易伤区</div>
<div class="td area-td">抗性区</div>
{{if damage.skill.isSheerDMG}}
<div class="td area-td">贯穿增伤区</div>
{{else}}
<div class="td area-td">防御区</div>
{{/if}}
</div>
<div class="tr area-tr titlebar">
<div class="td area-td">{{(areas.BasicArea || 1).toFixed(0)}}</div>
@ -195,9 +199,13 @@
<div class="td area-td">{{(areas.BoostArea || 1).toFixed(2)}}</div>
<div class="td area-td">{{(areas.VulnerabilityArea || 1).toFixed(2)}}</div>
<div class="td area-td">{{(areas.ResistanceArea || 1).toFixed(2)}}</div>
{{if damage.skill.isSheerDMG}}
<div class="td area-td">{{(areas.SheerBoostArea || 1).toFixed(2)}}</div>
{{else}}
<div class="td area-td">{{(areas.DefenceArea || 1).toFixed(4)}}</div>
{{/if}}
</div>
{{if areas.AnomalyProficiencyArea}}
{{if damage.skill.isAnomalyDMG}}
<div class="tr area-tr anomaly titlebar no-zzz-font">
<div class="td area-td">异常精通区</div>
<div class="td area-td">异常增伤区</div>
@ -231,7 +239,7 @@
<div class="td buff-td no-zzz-font">{{buff.name}}</div>
<div class="td buff-td no-zzz-font">{{buff.source}}</div>
<div class="td buff-td no-zzz-font">{{buff.type}}</div>
<div class="td buff-td">{{buff.value % 1 == 0 ? buff.value < 2 ? buff.value.toFixed(2) : buff.value : buff.value.toFixed(2)}}</div>
<div class="td buff-td">{{buff.value < 2 ? (buff.value.toFixed(2) * 100 + '%') : (buff.value % 1 === 0 ? buff.value : buff.value.toFixed(2))}}</div>
</div>
{{/each}}
</div>

View file

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