From 7ace8908ff712458c192fd3298b2ed04449c4581 Mon Sep 17 00:00:00 2001 From: UCPr <2032385471@qq.com> Date: Fri, 30 May 2025 00:28:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89=E4=BC=A4=E5=AE=B3?= =?UTF-8?q?=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/avatar.js | 2 +- model/damage/BuffManager.js | 25 +++--- model/damage/BuffManager.ts | 47 ++++++----- model/damage/Calculator.js | 23 +++-- model/damage/Calculator.ts | 30 +++++-- model/damage/README.md | 10 ++- model/damage/character/薇薇安/calc.js | 106 ++++++++++++++++++++++++ model/damage/character/薇薇安/data.json | 43 ++++++++++ model/damage/weapon/飞鸟星梦.js | 7 ++ 9 files changed, 240 insertions(+), 53 deletions(-) create mode 100644 model/damage/character/薇薇安/calc.js create mode 100644 model/damage/character/薇薇安/data.json create mode 100644 model/damage/weapon/飞鸟星梦.js diff --git a/model/avatar.js b/model/avatar.js index a33b675..58acb3e 100644 --- a/model/avatar.js +++ b/model/avatar.js @@ -264,7 +264,7 @@ export class ZZZAvatarInfo { this.level_rank = Math.floor(this.level / 10); const weight = scoreFnc[this.id] && scoreFnc[this.id](this); this.weightRule = weight?.[0] || '默认'; - this.scoreWeight = formatScoreWeight(weight?.[1]) || scoreWeight[this.id]; + this.scoreWeight = Object.assign(formatScoreWeight(weight?.[1]) || {}, scoreWeight[this.id]); for (const equip of this.equip) { equip.get_score(this.scoreWeight); } diff --git a/model/damage/BuffManager.js b/model/damage/BuffManager.js index 297f897..8c1c7a7 100644 --- a/model/damage/BuffManager.js +++ b/model/damage/BuffManager.js @@ -200,18 +200,23 @@ export class BuffManager { find(type, value) { return this.buffs.find(buff => buff[type] === value); } - operator(type, value, fnc) { - this.forEach(buff => { - if (buff[type] === value) { - fnc(buff); - } - }); + operator(key, value, fnc) { + const isMatch = typeof key === 'object' ? + (targetBuff) => Object.entries(key).every(([k, v]) => targetBuff[k] === v) : + (targetBuff) => targetBuff[key] === value; + this.forEach(buff => isMatch(buff) && (fnc || value)(buff)); } - close(type, value) { - this.operator(type, value, buff => buff.status = false); + close(key, value) { + if (typeof key === 'object') + this.operator(key, buff => buff.status = false); + else + this.operator(key, value, buff => buff.status = false); } - open(type, value) { - this.operator(type, value, buff => buff.status = true); + open(key, value) { + if (typeof key === 'object') + this.operator(key, buff => buff.status = true); + else + this.operator(key, value, buff => buff.status = true); } default(param, value) { if (typeof param === 'object') { diff --git a/model/damage/BuffManager.ts b/model/damage/BuffManager.ts index 22f3cf2..8c73a00 100644 --- a/model/damage/BuffManager.ts +++ b/model/damage/BuffManager.ts @@ -132,7 +132,7 @@ export class BuffManager { readonly buffs: buff[] = [] /** 套装计数 */ setCount: { [name: string]: number } = {} - defaultBuff: { [key in keyof buff]?: buff[key] } = {} + defaultBuff: Partial = {} constructor(avatar: ZZZAvatarInfo) { this.avatar = avatar @@ -319,39 +319,46 @@ export class BuffManager { return this.buffs.find(buff => buff[type] === value) } - operator(type: T, value: buff[T], fnc: (buff: buff) => void) { - this.forEach(buff => { - if (buff[type] === value) { - fnc(buff) - } - }) + operator(buff: Partial, fnc: (buff: buff) => void): void + operator(key: T, value: buff[T], fnc: (buff: buff) => void): void + operator(key: T | Partial, value: buff[T] | ((buff: buff) => void), fnc?: (buff: buff) => void) { + const isMatch = typeof key === 'object' ? + (targetBuff: buff) => Object.entries(key).every(([k, v]) => targetBuff[k as keyof buff] === v) : + (targetBuff: buff) => targetBuff[key] === value + this.forEach(buff => isMatch(buff) && (fnc || value as (buff: buff) => void)(buff)) } - /** - * 关闭符合条件的所有buff - */ - close(type: T, value: buff[T]) { - this.operator(type, value, buff => buff.status = false) + /** 关闭符合条件的所有buff */ + close(buff: Partial): void + close(key: T, value: buff[T]): void + close(key: T | Partial, value?: buff[T] | Partial) { + if (typeof key === 'object') + this.operator(key, buff => buff.status = false) + else + this.operator(key, value as buff[T], buff => buff.status = false) } - /** - * 开启符合条件的所有buff - */ - open(type: T, value: buff[T]) { - this.operator(type, value, buff => buff.status = true) + /** 开启符合条件的所有buff */ + open(buff: Partial): void + open(key: T, value: buff[T]): void + open(key: T, value?: buff[T]) { + if (typeof key === 'object') + this.operator(key, buff => buff.status = true) + else + this.operator(key, value as buff[T], buff => buff.status = true) } /** * 设置后续新增buff参数的默认值 * @param obj 直接覆盖默认值 */ - default(obj: { [key in keyof buff]?: buff[key] }): void + default(obj: Partial): void /** * 设置后续新增buff参数的默认值 * @param value 为undefined时删除默认值 */ - default(type: T, value?: buff[T]): void - default(param: T | { [key in keyof buff]?: buff[key] }, value?: buff[T]): void { + default(key: T, value?: buff[T]): void + default(param: T | Partial, value?: buff[T]): void { if (typeof param === 'object') { this.defaultBuff = param } else { diff --git a/model/damage/Calculator.js b/model/damage/Calculator.js index b4667f1..df8963a 100644 --- a/model/damage/Calculator.js +++ b/model/damage/Calculator.js @@ -112,7 +112,7 @@ export class Calculator { const areas = {}; if (skill.before) skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas }); - const isAnomaly = typeof anomalyEnum[skill.type] === 'number'; + const isAnomaly = typeof anomalyEnum[skill.type.slice(0, 2)] === 'number'; if (!areas.BasicArea) { let Multiplier = props.倍率; if (!Multiplier) { @@ -120,7 +120,7 @@ export class Calculator { Multiplier = skill.fixedMultiplier; } else if (isAnomaly) { - Multiplier = (skill.type === '紊乱' ? + Multiplier = (skill.type.startsWith('紊乱') ? this.get_DiscoverMultiplier(skill) : this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0)) || 0; } @@ -146,7 +146,7 @@ export class Calculator { areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs); areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs); areas.LevelArea ??= this.get_LevelArea(); - if (skill.type !== '紊乱') { + if (!skill.type.startsWith('紊乱')) { props.异常暴击率 ??= this.get_AnomalyCRITRate(skill, usefulBuffs); props.异常暴击伤害 ??= this.get_AnomalyCRITDMG(skill, usefulBuffs); areas.CriticalArea ??= 1 + props.异常暴击率 * (props.异常暴击伤害 - 1); @@ -372,23 +372,28 @@ export class Calculator { logger.debug(`技能倍率:${Multiplier}`); return Multiplier; } - get_AnomalyData(type) { + get_AnomalyData(anomaly) { + if (!anomaly) { + return AnomalyData.find(({ element_type, sub_element_type, multiplier }) => multiplier && + element_type === this.avatar.element_type && + sub_element_type === this.avatar.sub_element_type); + } let a = AnomalyData.filter(({ element_type }) => element_type === this.avatar.element_type); - if (type === '紊乱') + if (anomaly === '紊乱') a = a.filter(({ discover }) => discover); else - a = a.filter(({ name, multiplier }) => name === type && multiplier); + a = a.filter(({ name, multiplier }) => name === anomaly && multiplier); if (a.length === 1) return a[0]; a = a.filter(({ sub_element_type }) => sub_element_type === this.avatar.sub_element_type); return a[0]; } get_AnomalyMultiplier(skill, usefulBuffs, times = 0) { - const anomalyData = this.get_AnomalyData(skill.type); + const anomalyData = this.get_AnomalyData(skill?.type.slice(0, 2)); if (!anomalyData) return; let Multiplier = anomalyData.multiplier; - if (anomalyData.duration && anomalyData.interval) { + if (times !== 1 && anomalyData.duration && anomalyData.interval) { const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration); times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)); Multiplier = anomalyData.multiplier * times; @@ -397,7 +402,7 @@ export class Calculator { return Multiplier; } get_DiscoverMultiplier(skill) { - const anomalyData = this.get_AnomalyData(skill.type); + const anomalyData = this.get_AnomalyData(skill?.type.slice(0, 2)); if (!anomalyData) return; const AnomalyDuration = this.get_AnomalyDuration({ diff --git a/model/damage/Calculator.ts b/model/damage/Calculator.ts index 871a67c..204e218 100644 --- a/model/damage/Calculator.ts +++ b/model/damage/Calculator.ts @@ -110,6 +110,7 @@ export interface damage { x?: (n: number) => void } +/** ID 2 EN */ const elementType2element = (elementType: number) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]] as element const subBaseValueData = { @@ -265,7 +266,7 @@ export class Calculator { }, this) const areas = {} as damage['areas'] if (skill.before) skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas }) - const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number' + const isAnomaly = typeof anomalyEnum[skill.type.slice(0, 2) as anomaly] === 'number' if (!areas.BasicArea) { let Multiplier = props.倍率 if (!Multiplier) { @@ -273,7 +274,7 @@ export class Calculator { Multiplier = skill.fixedMultiplier } else if (isAnomaly) { Multiplier = ( - skill.type === '紊乱' ? + skill.type.startsWith('紊乱') ? this.get_DiscoverMultiplier(skill) : this.get_AnomalyMultiplier(skill, usefulBuffs, skill.name.includes('每') ? 1 : 0) ) || 0 @@ -295,7 +296,7 @@ export class Calculator { areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs) areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs) areas.LevelArea ??= this.get_LevelArea() - if (skill.type !== '紊乱') { // 紊乱暂无异常暴击区 + if (!skill.type.startsWith('紊乱')) { // 紊乱暂无异常暴击区 props.异常暴击率 ??= this.get_AnomalyCRITRate(skill, usefulBuffs) props.异常暴击伤害 ??= this.get_AnomalyCRITDMG(skill, usefulBuffs) areas.CriticalArea ??= 1 + props.异常暴击率! * (props.异常暴击伤害! - 1) @@ -573,10 +574,21 @@ export class Calculator { return Multiplier } - get_AnomalyData(type: skill['type']) { + /** 获取角色自身**出伤**异常的异常数据 */ + get_AnomalyData(): typeof AnomalyData[number] + /** 获取角色自身**指定**异常的异常数据 */ + get_AnomalyData(anomaly: anomaly): typeof AnomalyData[number] + get_AnomalyData(anomaly?: anomaly) { + if (!anomaly) { + return AnomalyData.find(({ element_type, sub_element_type, multiplier }) => + multiplier && + element_type === this.avatar.element_type && + sub_element_type === this.avatar.sub_element_type + ) + } let a = AnomalyData.filter(({ element_type }) => element_type === this.avatar.element_type) - if (type === '紊乱') a = a.filter(({ discover }) => discover) - else a = a.filter(({ name, multiplier }) => name === type && multiplier) + if (anomaly === '紊乱') a = a.filter(({ discover }) => discover) + else a = a.filter(({ name, multiplier }) => name === anomaly && multiplier) if (a.length === 1) return a[0] a = a.filter(({ sub_element_type }) => sub_element_type === this.avatar.sub_element_type) return a[0] @@ -584,10 +596,10 @@ export class Calculator { /** 获取属性异常倍率 */ get_AnomalyMultiplier(skill: skill, usefulBuffs: buff[], times = 0) { - const anomalyData = this.get_AnomalyData(skill.type) + const anomalyData = this.get_AnomalyData(skill?.type.slice(0, 2) as anomaly) if (!anomalyData) return let Multiplier = anomalyData.multiplier - if (anomalyData.duration && anomalyData.interval) { + if (times !== 1 && anomalyData.duration && anomalyData.interval) { const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration) times ||= Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)) Multiplier = anomalyData.multiplier * times @@ -598,7 +610,7 @@ export class Calculator { /** 获取紊乱倍率 */ get_DiscoverMultiplier(skill: skill) { - const anomalyData = this.get_AnomalyData(skill.type) + const anomalyData = this.get_AnomalyData(skill?.type.slice(0, 2) as anomaly) if (!anomalyData) return const AnomalyDuration = this.get_AnomalyDuration({ ...skill, diff --git a/model/damage/README.md b/model/damage/README.md index 086a848..6c3761b 100644 --- a/model/damage/README.md +++ b/model/damage/README.md @@ -32,6 +32,7 @@ - 元组:[评分规则名, 权重数据] - 类型:**[string, { [词条名: string]: number }]** - 若返回其他类型,会自动选择默认评分规则 + - 自定义权重会以默认权重为基础值,即**自定义权重中未指定的词条会取默认权重中的数据** ## 方法二:直接修改默认权重 @@ -207,8 +208,7 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动 - 游戏中的buff生效情况错综复杂,但通过[自定义敌方属性](#自定义敌方属性)和对buff的精确管控,插件的计算结果将与游戏实机十分吻合

- - +

## 技能属性 @@ -276,6 +276,8 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动 > - 感电 > - 侵蚀 > - 紊乱 +> +> 当需要对属性异常进行变体时(如薇薇安的异放),使命名以属性异常名开头即可(如 侵蚀·异放),注意buff作用范围需保持同步 > - 追加攻击 > - 直接以“追加攻击”命名 @@ -327,7 +329,7 @@ buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲 - 属性异常中**强击**和**碎冰**没有持续时间的概念,总倍率不受持续时间的影响也无法结算紊乱。因此对于作用于**异常持续时间**的buff,其buff.range应填写异常对应的**状态异常**(**畏缩**和**霜寒**),灼烧等既是伤害异常也是状态异常则无需区分 -- 对于`“X"(造成的伤害)被视为“Y”(伤害)`此类特殊技能,需要指定技能**重定向参数**,同时上述buff覆盖规则会发生变化,具体请参考[源码内描述](./Calculator.ts#L22) +- 对于`“X"(造成的伤害)被视为“Y”(伤害)`此类特殊技能,需要指定技能**重定向参数**,同时上述buff覆盖规则会发生变化,具体请参考[源码内描述](./Calculator.ts#L23) > 需要注意的是:即使出现`“X"(造成的伤害)被视为“Y”(伤害)`,对**Y**类型的增益**X**不一定能吃到,视具体情况变化 @@ -428,7 +430,7 @@ counter(145.7, 159, 16) 敌方基础属性可查看[此表](https://img.nga.178.com/attachments/mon_202407/16/axvkQq44x-2xpiZyT3cSwm-1hf.png) -例如将敌人的1级基础防御力设置为36: +例如将敌人的1级基础防御力设置为36(如提尔锋): ```JS export function calc(buffM, calc, avatar) { diff --git a/model/damage/character/薇薇安/calc.js b/model/damage/character/薇薇安/calc.js new file mode 100644 index 0000000..08c4421 --- /dev/null +++ b/model/damage/character/薇薇安/calc.js @@ -0,0 +1,106 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '1影', + type: '异常增伤', // 异常易伤? + value: 0.16, + is: { + team: true + } + }, + { + name: '2影', + type: '无视抗性', + value: 0.15, + range: ['侵蚀·异放'] + }, + { + name: '4影', + type: '暴击率', + value: 1, + range: ['AX', 'AH'] + }, + { + name: '4影', + type: '攻击力', + value: 0.12 + }, + { + name: '6影', + type: '增伤', + value: 0.4, + element: 'Ether', + isForever: true + }, + { + name: '额外能力:预言之泪', + type: '异常增伤', + value: 0.12, + // 侵蚀、侵蚀结算的紊乱生效 + check: ({ calc }) => calc.skill.element === 'Ether', + range: ['侵蚀', '紊乱'] + } +] + +/** @param {import('../../Calculator.ts').Calculator} vva */ +const generateBefore = (vva) => { + /** @type {import('../../Calculator.ts').skill['before']} */ + const before = ({ avatar, calc, areas }) => { + const AnomalyMultiplier = calc.get_AnomalyMultiplier(undefined, undefined, 1) + const ATK = calc.get_ATK(calc.skill) + const AnomalyProficiency = vva.get_AnomalyProficiency(calc.skill) + const skillMultiplier = vva.get_SkillMultiplier(`TY${avatar.element_type}`) + const n = AnomalyProficiency / 10 * skillMultiplier * (vva.avatar.rank >= 2 ? 1.3 : 1) + areas.BasicArea = ATK * AnomalyMultiplier * n + } + return before +} + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '侵蚀每段', type: '侵蚀' }, + { name: '紊乱', type: '紊乱' }, + { + name: '异放·结算薇薇安', + type: '侵蚀·异放', + isMain: true, + banCache: true, + before: (data) => generateBefore(data.calc)(data) + }, + { + name: '6影特殊异放·5护羽', + type: '侵蚀·异放', + check: 6, + banCache: true, + dmg: (calc) => { + const dmg = calc.calc_skill({ + ...calc.skills.find(skill => skill.type === '侵蚀·异放'), + after: ({ damage }) => damage.x(5) + }) + dmg.skill.name = '6影特殊异放·5护羽' + return dmg + } + }, + { name: '普攻:翎羽拂击四段', type: 'AP4' }, + { name: '普攻:淑女礼仪·舞步', type: 'AT' }, + { name: '普攻:裙裾浮游·悬落', type: 'AX' }, + { name: '普攻:落羽生花', type: 'AH' }, + { + name: '薇薇安的预言', + type: 'TW', + fixedMultiplier: 0.55 + }, + { name: '闪避反击:羽刃反振', type: 'CF' }, + { name: '强化特殊技:堇花悼亡', type: 'EQ' }, + { name: '连携技:星羽和声', type: 'RL' }, + { name: '终结技:飞鸟鸣颂', type: 'RZ' } +] + +/** + * @param {import('../../BuffManager.ts').BuffManager} buffM + * @param {import('../../Calculator.ts').Calculator} calc + * @param {import('../../../avatar.js').ZZZAvatarInfo} avatar + */ +// export function calc(buffM, calc, avatar) { + +// } \ 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..5c59d28 --- /dev/null +++ b/model/damage/character/薇薇安/data.json @@ -0,0 +1,43 @@ +{ + "skill": { + "AP4": [ + 1.284,1.401,1.518,1.635,1.752,1.869,1.986,2.103,2.22,2.337,2.454,2.571,2.688,2.805,2.922,3.039 + ], + "AT": [ + 0.478,0.522,0.566,0.61,0.654,0.698,0.742,0.786,0.83,0.874,0.918,0.962,1.006,1.05,1.094,1.138 + ], + "AX": [ + 0.802,0.875,0.948,1.021,1.094,1.167,1.24,1.313,1.386,1.459,1.532,1.605,1.678,1.751,1.824,1.897 + ], + "AH": [ + 2.2,2.4,2.6,2.8,3,3.2,3.4,3.6,3.8,4,4.2,4.4,4.6,4.8,5,5.2 + ], + "CF": [ + 2.364,2.579,2.794,3.009,3.224,3.439,3.654,3.869,4.084,4.299,4.514,4.729,4.944,5.159,5.374,5.589 + ], + "EQ": [ + 5.926,6.465,7.004,7.543,8.082,8.621,9.16,9.699,10.238,10.777,11.316,11.855,12.394,12.933,13.472,14.011 + ], + "RL": [ + 6.589,7.188,7.787,8.386,8.985,9.584,10.183,10.782,11.381,11.98,12.579,13.178,13.777,14.376,14.975,15.574 + ], + "RZ": [ + 16.868,18.402,19.936,21.47,23.004,24.538,26.072,27.606,29.14,30.674,32.208,33.742,35.276,36.81,38.344,39.878 + ], + "TY200": [ + 0.0037,0.0044,0.0051,0.0058,0.0065,0.007,0.0075 + ], + "TY201": [ + 0.04,0.0466,0.0532,0.0598,0.0664,0.073,0.08 + ], + "TY202": [ + 0.0054,0.0063,0.0072,0.0081,0.009,0.0099,0.0108 + ], + "TY203": [ + 0.016,0.0186,0.0212,0.0238,0.0264,0.029,0.032 + ], + "TY205": [ + 0.0307,0.0359,0.0411,0.0463,0.0515,0.0565,0.0615 + ] + } +} \ No newline at end of file diff --git a/model/damage/weapon/飞鸟星梦.js b/model/damage/weapon/飞鸟星梦.js new file mode 100644 index 0000000..b805ce8 --- /dev/null +++ b/model/damage/weapon/飞鸟星梦.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '异常精通', + value: [20, 23, 26, 29, 32].map(v => v * 6) + } +] \ No newline at end of file