From 3f8e64af66985871fb07d39e17def4827120eba3 Mon Sep 17 00:00:00 2001 From: UCPr <2032385471@qq.com> Date: Tue, 14 Jan 2025 02:18:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=85=A8=E9=87=8D=E6=9E=84=E4=BC=A4?= =?UTF-8?q?=E5=AE=B3=E8=AE=A1=E7=AE=97=EF=BC=9B=E6=94=AF=E6=8C=81=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E4=BC=A4=E5=AE=B3=E8=AE=A1=E7=AE=97=EF=BC=9B=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=89=80=E6=9C=89=E6=AD=A6=E5=99=A8=E3=80=81=E5=A5=97?= =?UTF-8?q?=E8=A3=85=E8=AE=A1=E7=AE=97=EF=BC=9B=E6=96=B0=E5=A2=9E=E6=82=A0?= =?UTF-8?q?=E7=9C=9F=E8=AE=A1=E7=AE=97=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- README.md | 2 + lib/convert/property.js | 4 +- lib/convert/weapon.js | 23 + model/avatar.js | 160 ++-- model/damage/BuffManager.js | 223 ++++++ model/damage/BuffManager.ts | 296 ++++++++ model/damage/Calculator.js | 406 ++++++++++ model/damage/Calculator.ts | 545 ++++++++++++++ model/damage/README.md | 314 ++++++++ model/damage/avatar.js | 953 ++++-------------------- model/damage/avatar.ts | 172 +++++ model/damage/character/11号/calc.js | 37 + model/damage/character/11号/data.json | 22 + model/damage/character/安东/calc.js | 35 + model/damage/character/安东/data.json | 27 + model/damage/character/悠真/calc.js | 42 ++ model/damage/character/悠真/data.json | 30 + model/damage/character/星见雅/calc.js | 173 +++++ model/damage/character/星见雅/data.json | 34 + model/damage/character/朱鸢/calc.js | 50 ++ model/damage/character/朱鸢/data.json | 24 + model/damage/character/模板/calc.js | 123 +++ model/damage/character/模板/data.json | 27 + model/damage/character/猫又/calc.js | 40 + model/damage/character/猫又/data.json | 24 + model/damage/character/艾莲/calc.js | 64 ++ model/damage/character/艾莲/data.json | 39 + model/damage/character/青衣/calc.js | 53 ++ model/damage/character/青衣/data.json | 19 + model/damage/relice.js | 119 --- model/damage/role.js | 304 -------- model/damage/set/原始朋克.js | 8 + model/damage/set/啄木鸟电音.js | 8 + model/damage/set/折枝剑歌.js | 42 ++ model/damage/set/摇摆爵士.js | 8 + model/damage/set/极地重金属.js | 23 + model/damage/set/模板.js | 43 ++ model/damage/set/河豚电音.js | 15 + model/damage/set/混沌爵士.js | 16 + model/damage/set/混沌重金属.js | 21 + model/damage/set/激素朋克.js | 8 + model/damage/set/炎狱重金属.js | 15 + model/damage/set/獠牙重金属.js | 15 + model/damage/set/雷暴重金属.js | 15 + model/damage/set/静听嘉音.js | 8 + model/damage/weapon.js | 84 --- model/damage/weapon/「恒等式」-本格.js | 7 + model/damage/weapon/「月相」-晦.js | 7 + model/damage/weapon/「月相」-望.js | 9 + model/damage/weapon/「残响」-Ⅰ型.js | 7 + model/damage/weapon/「残响」-Ⅱ型.js | 11 + model/damage/weapon/「残响」-Ⅲ型.js | 7 + model/damage/weapon/「湍流」-斧型.js | 7 + model/damage/weapon/「电磁暴」-壹式.js | 7 + model/damage/weapon/「电磁暴」-贰式.js | 7 + model/damage/weapon/人为刀俎.js | 7 + model/damage/weapon/仿制星徽引擎.js | 8 + model/damage/weapon/兔能环.js | 12 + model/damage/weapon/加农转子.js | 8 + model/damage/weapon/双生泣星.js | 7 + model/damage/weapon/含羞恶面.js | 13 + model/damage/weapon/啜泣摇篮.js | 11 + model/damage/weapon/奔袭獠牙.js | 7 + model/damage/weapon/好斗的阿炮.js | 7 + model/damage/weapon/家政员.js | 9 + model/damage/weapon/嵌合编译器.js | 12 + model/damage/weapon/强音热望.js | 7 + model/damage/weapon/德玛拉电池Ⅱ型.js | 9 + model/damage/weapon/拘缚者.js | 8 + model/damage/weapon/旋钻机-赤轴.js | 9 + model/damage/weapon/时流贤者.js | 13 + model/damage/weapon/星徽引擎.js | 7 + model/damage/weapon/模板.js | 28 + model/damage/weapon/正版变身器.js | 12 + model/damage/weapon/残心青囊.js | 19 + model/damage/weapon/淬锋钳刺.js | 8 + model/damage/weapon/深海访客.js | 13 + model/damage/weapon/灼心摇壶.js | 11 + model/damage/weapon/焰心桂冠.js | 12 + model/damage/weapon/燃狱齿轮.js | 7 + model/damage/weapon/玉壶青冰.js | 11 + model/damage/weapon/硫磺石.js | 7 + model/damage/weapon/聚宝箱.js | 8 + model/damage/weapon/街头巨星.js | 8 + model/damage/weapon/触电唇彩.js | 11 + model/damage/weapon/轰鸣座驾.js | 11 + model/damage/weapon/逍遥游球.js | 7 + model/damage/weapon/鎏金花信.js | 13 + model/damage/weapon/钢铁肉垫.js | 13 + model/damage/weapon/防暴者Ⅵ型.js | 14 + model/damage/weapon/雨林饕客.js | 7 + model/damage/weapon/霰落星殿.js | 34 + model/equip.js | 2 +- resources/map/AnomalyData.json | 98 +++ resources/map/Property2Name.json | 38 +- resources/map/WeaponId2Data.json | 338 +++++++++ resources/map/weapon_effect.json | 162 ---- resources/panel/card.html | 27 +- 99 files changed, 4243 insertions(+), 1615 deletions(-) create mode 100644 model/damage/BuffManager.js create mode 100644 model/damage/BuffManager.ts create mode 100644 model/damage/Calculator.js create mode 100644 model/damage/Calculator.ts create mode 100644 model/damage/README.md create mode 100644 model/damage/avatar.ts create mode 100644 model/damage/character/11号/calc.js create mode 100644 model/damage/character/11号/data.json create mode 100644 model/damage/character/安东/calc.js create mode 100644 model/damage/character/安东/data.json create mode 100644 model/damage/character/悠真/calc.js create mode 100644 model/damage/character/悠真/data.json create mode 100644 model/damage/character/星见雅/calc.js create mode 100644 model/damage/character/星见雅/data.json create mode 100644 model/damage/character/朱鸢/calc.js create mode 100644 model/damage/character/朱鸢/data.json create mode 100644 model/damage/character/模板/calc.js create mode 100644 model/damage/character/模板/data.json create mode 100644 model/damage/character/猫又/calc.js create mode 100644 model/damage/character/猫又/data.json create mode 100644 model/damage/character/艾莲/calc.js create mode 100644 model/damage/character/艾莲/data.json create mode 100644 model/damage/character/青衣/calc.js create mode 100644 model/damage/character/青衣/data.json delete mode 100644 model/damage/relice.js delete mode 100644 model/damage/role.js create mode 100644 model/damage/set/原始朋克.js create mode 100644 model/damage/set/啄木鸟电音.js create mode 100644 model/damage/set/折枝剑歌.js create mode 100644 model/damage/set/摇摆爵士.js create mode 100644 model/damage/set/极地重金属.js create mode 100644 model/damage/set/模板.js create mode 100644 model/damage/set/河豚电音.js create mode 100644 model/damage/set/混沌爵士.js create mode 100644 model/damage/set/混沌重金属.js create mode 100644 model/damage/set/激素朋克.js create mode 100644 model/damage/set/炎狱重金属.js create mode 100644 model/damage/set/獠牙重金属.js create mode 100644 model/damage/set/雷暴重金属.js create mode 100644 model/damage/set/静听嘉音.js delete mode 100644 model/damage/weapon.js create mode 100644 model/damage/weapon/「恒等式」-本格.js create mode 100644 model/damage/weapon/「月相」-晦.js create mode 100644 model/damage/weapon/「月相」-望.js create mode 100644 model/damage/weapon/「残响」-Ⅰ型.js create mode 100644 model/damage/weapon/「残响」-Ⅱ型.js create mode 100644 model/damage/weapon/「残响」-Ⅲ型.js create mode 100644 model/damage/weapon/「湍流」-斧型.js create mode 100644 model/damage/weapon/「电磁暴」-壹式.js create mode 100644 model/damage/weapon/「电磁暴」-贰式.js create mode 100644 model/damage/weapon/人为刀俎.js create mode 100644 model/damage/weapon/仿制星徽引擎.js create mode 100644 model/damage/weapon/兔能环.js create mode 100644 model/damage/weapon/加农转子.js create mode 100644 model/damage/weapon/双生泣星.js create mode 100644 model/damage/weapon/含羞恶面.js create mode 100644 model/damage/weapon/啜泣摇篮.js create mode 100644 model/damage/weapon/奔袭獠牙.js create mode 100644 model/damage/weapon/好斗的阿炮.js create mode 100644 model/damage/weapon/家政员.js create mode 100644 model/damage/weapon/嵌合编译器.js create mode 100644 model/damage/weapon/强音热望.js create mode 100644 model/damage/weapon/德玛拉电池Ⅱ型.js create mode 100644 model/damage/weapon/拘缚者.js create mode 100644 model/damage/weapon/旋钻机-赤轴.js create mode 100644 model/damage/weapon/时流贤者.js create mode 100644 model/damage/weapon/星徽引擎.js create mode 100644 model/damage/weapon/模板.js create mode 100644 model/damage/weapon/正版变身器.js create mode 100644 model/damage/weapon/残心青囊.js create mode 100644 model/damage/weapon/淬锋钳刺.js create mode 100644 model/damage/weapon/深海访客.js create mode 100644 model/damage/weapon/灼心摇壶.js create mode 100644 model/damage/weapon/焰心桂冠.js create mode 100644 model/damage/weapon/燃狱齿轮.js create mode 100644 model/damage/weapon/玉壶青冰.js create mode 100644 model/damage/weapon/硫磺石.js create mode 100644 model/damage/weapon/聚宝箱.js create mode 100644 model/damage/weapon/街头巨星.js create mode 100644 model/damage/weapon/触电唇彩.js create mode 100644 model/damage/weapon/轰鸣座驾.js create mode 100644 model/damage/weapon/逍遥游球.js create mode 100644 model/damage/weapon/鎏金花信.js create mode 100644 model/damage/weapon/钢铁肉垫.js create mode 100644 model/damage/weapon/防暴者Ⅵ型.js create mode 100644 model/damage/weapon/雨林饕客.js create mode 100644 model/damage/weapon/霰落星殿.js create mode 100644 resources/map/AnomalyData.json create mode 100644 resources/map/WeaponId2Data.json delete mode 100644 resources/map/weapon_effect.json diff --git a/.gitignore b/.gitignore index c6079e4..c3233e9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ data/**/*.* resources/images/**/*.* resources/data/**/*.* -resources/map/*.js +model/damage/map/ +model/damage/character/**/*_user.* diff --git a/README.md b/README.md index 974bd5e..cc3ce1c 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ > 插件依靠社区维护,发起者随缘更新,但是ZZZure组织成员会对PR进行合并,你可以在PR页面@协助者进行合并。 > > 在你使用之前请**务必**完整阅读 `README` 内容,如因无视 `README` 遇到的问题,在提问时难免遭受嘲笑。 +> +> 如需自定义伤害计算请查看[此说明](./model/damage/README.md) # 安装 diff --git a/lib/convert/property.js b/lib/convert/property.js index d4da2fb..8d6c75f 100644 --- a/lib/convert/property.js +++ b/lib/convert/property.js @@ -44,7 +44,7 @@ export function idToClassName(_id) { /** * 获取属性标识 - * @param {string} _id 属性id + * @param {string | number} _id 属性id * @returns {string | null} */ export const idToSignName = _id => { @@ -56,7 +56,7 @@ export const idToSignName = _id => { /** * 获取属性名称 - * @param {string} _id 属性id + * @param {string | number} _id 属性id * @returns {string | null} */ export const idToName = _id => { diff --git a/lib/convert/weapon.js b/lib/convert/weapon.js index 798059a..7148086 100644 --- a/lib/convert/weapon.js +++ b/lib/convert/weapon.js @@ -30,3 +30,26 @@ export const weaponFileNameToID = name => { export const getAllWeaponID = () => { return Object.keys(WeaponId2Sprite); }; + +const WeaponId2Data = getMapData('WeaponId2Data'); + +/** + * 武器名称转id + * @param {string} name 武器全称 + * @returns {number | null} + */ +export const weaponNameToID = name => { + for (const [id, data] of Object.entries(WeaponId2Data)) { + if (data.name === name) return +id; + } + return null; +} + +/** + * 武器ID转职业 + * @param {number} id 武器全称 + * @returns {number | null} + */ +export const weaponIDToProfession = id => { + return WeaponId2Data[id]?.profession ?? null; +} diff --git a/model/avatar.js b/model/avatar.js index 98fb3b0..56b7075 100644 --- a/model/avatar.js +++ b/model/avatar.js @@ -1,4 +1,4 @@ -import { element, property } from '../lib/convert.js'; +import { element } from '../lib/convert.js'; import { getRoleImage, getSmallSquareAvatar, @@ -8,9 +8,7 @@ import { imageResourcesPath } from '../lib/path.js'; import { Equip, Weapon } from './equip.js'; import { Property } from './property.js'; import { Skill } from './skill.js'; -import { relice_ability } from './damage/relice.js'; -import { weapon_ability } from './damage/weapon.js'; -import { avatar_ability, has_calculation } from './damage/avatar.js'; +import { avatar_ability } from './damage/avatar.js'; import { hasScoreData } from '../lib/score.js'; import _ from 'lodash'; @@ -180,6 +178,7 @@ export class ZZZAvatarInfo { * name_mi18n: string; * full_name_mi18n: string; * element_type: number; + * sub_element_type: number; * camp_name_mi18n: string; * avatar_profession: number; * rarity: string; @@ -201,6 +200,7 @@ export class ZZZAvatarInfo { name_mi18n, full_name_mi18n, element_type, + sub_element_type, camp_name_mi18n, avatar_profession, rarity, @@ -224,9 +224,11 @@ export class ZZZAvatarInfo { this.full_name_mi18n = full_name_mi18n; /** @type {number} 元素种类 */ this.element_type = element_type; + /** @type {number} 子元素种类 */ + this.sub_element_type = sub_element_type; /** @type {string} */ this.camp_name_mi18n = camp_name_mi18n; - /** @type {number} */ + /** @type {number} 职业 */ this.avatar_profession = avatar_profession; /** @type {string} 稀有度 */ this.rarity = rarity; @@ -298,111 +300,59 @@ export class ZZZAvatarInfo { return data; } - /** @type {{ - * base_detail: { - * hp: number; - * attack: number; - * defence: number; - * ImpactRatio: number; - * CriticalChanceBase: number; - * CriticalDamageBase: number; - * ElementAbnormalPower: number; - * ElementMystery: number; - * PenRatioBase: number; - * SpGetRatio: number; - * } - * bonus_detail: Record; - * set_detail: Record; - * }} */ - get damage_basic_properties() { - const basic_properties = this.basic_properties; - const base_detail = { - hp: Number(basic_properties.hpmax.final), - attack: Number(basic_properties.attack.final), - defence: Number(basic_properties.def.final), - ImpactRatio: Number(basic_properties.breakstun.final), - CriticalChanceBase: - Number(basic_properties.crit.final.replace('%', '')) / 100, - CriticalDamageBase: - Number(basic_properties.critdam.final.replace('%', '')) / 100, - ElementAbnormalPower: Number( - basic_properties.elementabnormalpower.final - ), - ElementMystery: Number(basic_properties.elementmystery.final), - PenRatioBase: - Number(basic_properties.penratio.final.replace('%', '')) / 100, - SpGetRatio: Number(basic_properties.sprecover.final), - }; - /** 计算伤害加成与穿透值 - * 穿透值23203 - * 伤害加成31503-31703 + /** 基础属性 */ + get base_properties() { + if (this._base_properties) return this._base_properties + const basic_properties = this.basic_properties + /** + * @param {keyof ZZZAvatarInfo['basic_properties']} name */ - const bonus_detail = {}; - /** 计算套装数量 */ - const set_detail = {}; - for (const equip_detail of this.equip) { - bonus_detail['PenDelta'] = - _.get(bonus_detail, 'PenDelta', 0) + equip_detail.get_property(23203); - if (equip_detail.equipment_type == 5) { - const propname = property.idToName( - equip_detail.main_properties[0].property_id - ); - if (propname.includes('属性伤害提高')) { - const propenname = property.idToSignName( - equip_detail.main_properties[0].property_id - ); - bonus_detail[propenname] = - _.get(bonus_detail, propenname, 0) + - Number(equip_detail.main_properties[0].base.replace('%', '')) / 100; - } - } - const suit_id = String(equip_detail.equip_suit.suit_id); - set_detail[suit_id] = _.get(set_detail, suit_id, 0) + 1; + const get = (name) => { + const data = basic_properties[name] + return Number(data.base || data.final) + } + return this._base_properties = { + HP: get('hpmax'), + ATK: get('attack'), + DEF: get('def'), + Impact: get('breakstun'), + AnomalyMastery: get('elementabnormalpower'), + AnomalyProficiency: get('elementmystery'), + EnergyRegen: get('sprecover') } - logger.debug('base_detail', base_detail); - logger.debug('bonus_detail', bonus_detail); - logger.debug('set_detail', set_detail); - const retuan_detail = { - base_detail: base_detail, - bonus_detail: bonus_detail, - set_detail: set_detail, - }; - return retuan_detail; } - /** @type {{ - * title: string; - * value: {name: string, value: number}[] - * }[]} */ + /** 初始属性 */ + get initial_properties() { + if (this._initial_properties) return this._initial_properties + const basic_properties = this.basic_properties + /** + * @param {keyof ZZZAvatarInfo['basic_properties']} name + */ + const get = (name) => { + const data = basic_properties[name].final + return Number(data.includes('%') ? data.replace('%', '') / 100 : data) + } + return this._initial_properties = { + HP: get('hpmax'), + ATK: get('attack'), + DEF: get('def'), + Impact: get('breakstun'), + CRITRate: get('crit'), + CRITDMG: get('critdam'), + /** 异常掌控 */ + AnomalyMastery: get('elementabnormalpower'), + /** 异常精通 */ + AnomalyProficiency: get('elementmystery'), + Pen: this.equip.reduce((prev, curr) => prev + curr.get_property(23203), 0), + PenRatio: get('penratio'), + EnergyRegen: get('sprecover') + } + } + + /** @type {import("./damage/Calculator.ts").damage} */ get damages() { - if (!has_calculation(this.id)) { - return []; - } - /** 处理基础数据 */ - let { base_detail, bonus_detail, set_detail } = this.damage_basic_properties; - - /** 处理驱动盘套装加成 */ - let set_detailkeys = Object.keys(set_detail); - for (const set_id of set_detailkeys) { - const set_num = set_detail[set_id]; - if (set_num >= 2) - bonus_detail = relice_ability( - set_id, - set_num, - base_detail, - bonus_detail - ); - } - logger.debug('+套装bonus_detail', bonus_detail); - - /** 处理音频加成 */ - if (this.weapon) - bonus_detail = weapon_ability(this.weapon, base_detail, bonus_detail); - logger.debug('+音频bonus_detail', bonus_detail); - - /** 处理角色加成 */ - const damagelist = avatar_ability(this, base_detail, bonus_detail); - return damagelist; + return avatar_ability(this); } /** @type {number|boolean} */ diff --git a/model/damage/BuffManager.js b/model/damage/BuffManager.js new file mode 100644 index 0000000..c50b566 --- /dev/null +++ b/model/damage/BuffManager.js @@ -0,0 +1,223 @@ +import { weaponIDToProfession } from '../../lib/convert/weapon.js'; +import _ from 'lodash'; +export var elementEnum; +(function (elementEnum) { + // 物理、火、冰、电、以太 + elementEnum[elementEnum["Physical"] = 0] = "Physical"; + elementEnum[elementEnum["Fire"] = 1] = "Fire"; + elementEnum[elementEnum["Ice"] = 2] = "Ice"; + elementEnum[elementEnum["Electric"] = 3] = "Electric"; + elementEnum[elementEnum["Ether"] = 4] = "Ether"; +})(elementEnum || (elementEnum = {})); +export var anomalyEnum; +(function (anomalyEnum) { + anomalyEnum[anomalyEnum["\u5F3A\u51FB"] = 0] = "\u5F3A\u51FB"; + anomalyEnum[anomalyEnum["\u707C\u70E7"] = 1] = "\u707C\u70E7"; + anomalyEnum[anomalyEnum["\u788E\u51B0"] = 2] = "\u788E\u51B0"; + anomalyEnum[anomalyEnum["\u611F\u7535"] = 3] = "\u611F\u7535"; + anomalyEnum[anomalyEnum["\u4FB5\u8680"] = 4] = "\u4FB5\u8680"; + anomalyEnum[anomalyEnum["\u7D0A\u4E71"] = 5] = "\u7D0A\u4E71"; +})(anomalyEnum || (anomalyEnum = {})); +export var buffTypeEnum; +(function (buffTypeEnum) { + // 通用乘区 + buffTypeEnum[buffTypeEnum["\u653B\u51FB\u529B"] = 0] = "\u653B\u51FB\u529B"; + buffTypeEnum[buffTypeEnum["\u589E\u4F24"] = 1] = "\u589E\u4F24"; + buffTypeEnum[buffTypeEnum["\u6613\u4F24"] = 2] = "\u6613\u4F24"; + buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u6297\u6027"] = 3] = "\u65E0\u89C6\u6297\u6027"; + buffTypeEnum[buffTypeEnum["\u65E0\u89C6\u9632\u5FA1"] = 4] = "\u65E0\u89C6\u9632\u5FA1"; + buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u503C"] = 5] = "\u7A7F\u900F\u503C"; + buffTypeEnum[buffTypeEnum["\u7A7F\u900F\u7387"] = 6] = "\u7A7F\u900F\u7387"; + // 直伤乘区 + buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u7387"] = 7] = "\u66B4\u51FB\u7387"; + buffTypeEnum[buffTypeEnum["\u66B4\u51FB\u4F24\u5BB3"] = 8] = "\u66B4\u51FB\u4F24\u5BB3"; + // 异常乘区 + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u7CBE\u901A"] = 9] = "\u5F02\u5E38\u7CBE\u901A"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u589E\u4F24"] = 10] = "\u5F02\u5E38\u589E\u4F24"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u7387"] = 11] = "\u5F02\u5E38\u66B4\u51FB\u7387"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"] = 12] = "\u5F02\u5E38\u66B4\u51FB\u4F24\u5BB3"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"] = 13] = "\u5F02\u5E38\u6301\u7EED\u65F6\u95F4"; + // 其他属性,一般不直接影响伤害,但可能用于buff是否生效判断/转模 + buffTypeEnum[buffTypeEnum["\u751F\u547D\u503C"] = 14] = "\u751F\u547D\u503C"; + buffTypeEnum[buffTypeEnum["\u9632\u5FA1\u529B"] = 15] = "\u9632\u5FA1\u529B"; + buffTypeEnum[buffTypeEnum["\u51B2\u51FB\u529B"] = 16] = "\u51B2\u51FB\u529B"; + buffTypeEnum[buffTypeEnum["\u5F02\u5E38\u638C\u63A7"] = 17] = "\u5F02\u5E38\u638C\u63A7"; +})(buffTypeEnum || (buffTypeEnum = {})); +let depth = 0, weakMapCheck = new WeakMap(); +/** + * Buff管理器 + * 用于管理角色局内Buff + */ +export class BuffManager { + avatar; + buffs = []; + /** 套装计数 */ + setCount = {}; + defaultBuff = {}; + constructor(avatar) { + this.avatar = avatar; + } + new(buff) { + if (Array.isArray(buff)) { + buff.forEach(b => this.new(b)); + return this.buffs; + } + // 简化参数 + if (!buff.name && (buff.source || this.defaultBuff.source) === 'Set' && this.defaultBuff.name && typeof buff.check === 'number') + buff.name = this.defaultBuff.name + buff.check; + buff = _.merge({ + status: true, + isForever: false, + ...this.defaultBuff + }, buff); + if (!buff.source) { + if (buff.name.includes('核心') || buff.name.includes('天赋')) + buff.source = 'Talent'; + else if (buff.name.includes('额外能力')) + buff.source = 'Addition'; + else if (buff.name.includes('影')) + buff.source = 'Rank'; + else if (buff.name.includes('技')) + buff.source = 'Skill'; + } + if (!buff.name || !buff.value || !buff.source || !buffTypeEnum[buffTypeEnum[buff.type]]) + return logger.warn('无效buff:', buff); + // 武器buff职业检查 + if (buff.source === 'Weapon') { + if (typeof buff.check === 'function') { + const oriCheck = buff.check; + buff.check = ({ avatar, buffM, calc }) => avatar.avatar_profession === weaponIDToProfession(avatar.weapon.id) && oriCheck({ avatar, buffM, calc }); + } + else { + buff.check = ({ avatar }) => avatar.avatar_profession === weaponIDToProfession(avatar.weapon.id); + } + } + else if (buff.source === 'Rank') { + buff.check ??= +buff.name.match(/\d/)?.[0]; + } + this.buffs.push(buff); + return this.buffs; + } + _filter(buffs, param, valueOcalc) { + depth++; + try { + if (typeof param === 'string') { + buffs = buffs.filter(buff => buff[param] === valueOcalc); + } + else if (typeof param === 'object') { + buffs = buffs.filter(buff => { + if (buff.status === false) + return false; + for (const key in param) { + if (key === 'range') { + const buffRange = buff.range; + if (!buffRange || !param.range) + continue; // 对任意类型生效 + param.range = param.range.filter(r => typeof r === 'string'); + if (!param.range.length) + continue; + // buff作用范围向后覆盖 + else if (!param.range.every(ST => buffRange.some(BT => ST.startsWith(BT)))) + return false; + else + continue; + } + else if (key === 'element') { + if (!buff.element || !param.element) + continue; // 对任意属性生效 + if (Array.isArray(buff.element)) { + if (buff.element.includes(param.element)) + continue; + return false; + } + } + // @ts-ignore + if (buff[key] !== param[key]) + return false; + } + if (buff.check) { + if (typeof buff.check === 'number') { + if (buff.source === 'Set' && (this.setCount[buff.name.replace(/\d$/, '')] < buff.check)) + return false; + else if (buff.source === 'Rank' && (this.avatar.rank < buff.check)) + return false; + } + else if (valueOcalc) { + if (weakMapCheck.has(buff)) { + // console.log(`depth:${depth} ${buff.name}:${weakMapCheck.get(buff)}`) + if (!weakMapCheck.get(buff)) + return false; + } + else { + weakMapCheck.set(buff, false); + if (!buff.check({ + avatar: this.avatar, + buffM: this, + calc: valueOcalc + })) + return false; + weakMapCheck.set(buff, true); + } + } + else { + logger.debug('未传入calc:' + buff.name); + return false; + } + } + return true; + }); + } + else { + buffs = buffs.filter(param); + } + } + catch (e) { + logger.error(e); + } + if (--depth === 0) { + // console.log('重置weakMapCheck') + weakMapCheck = new WeakMap(); + } + return buffs; + } + /** + * 遍历buff列表 + */ + forEach(fnc) { + return this.buffs.forEach(fnc); + } + filter(param, valueOcalc) { + // @ts-ignore + return this._filter(this.buffs, param, valueOcalc); + } + operator(type, value, fnc) { + this.forEach(buff => { + if (buff[type] === value) { + fnc(buff); + } + }); + } + /** + * 关闭符合条件的所有buff + */ + close(type, value) { + this.operator(type, value, buff => buff.status = false); + } + /** + * 开启符合条件的所有buff + */ + open(type, value) { + this.operator(type, value, buff => buff.status = true); + } + default(param, value) { + if (typeof param === 'object') { + this.defaultBuff = param; + } + else { + if (value === undefined) + delete this.defaultBuff[param]; + else + this.defaultBuff[param] = value; + } + } +} diff --git a/model/damage/BuffManager.ts b/model/damage/BuffManager.ts new file mode 100644 index 0000000..ce4ad9c --- /dev/null +++ b/model/damage/BuffManager.ts @@ -0,0 +1,296 @@ +import type { ZZZAvatarInfo } from '../avatar.js' +import type { Calculator } from './Calculator.ts' +import { weaponIDToProfession } from '../../lib/convert/weapon.js' +import _ from 'lodash' + +export enum elementEnum { + // 物理、火、冰、电、以太 + Physical, Fire, Ice, Electric, Ether +} + +export enum anomalyEnum { + 强击, 灼烧, 碎冰, 感电, 侵蚀, 紊乱 +} + +/** 属性类型 */ +export type element = keyof typeof elementEnum + +/** 异常类型 */ +export type anomaly = keyof typeof anomalyEnum + +/** + * Buff来源 + * @Weapon 武器 + * @Set 套装 + * @Rank 影画 + * @Talent 核心被动 + * @Addition 额外能力 + * @Skill 技能 + */ +export type buffSource = 'Weapon' | 'Set' | 'Rank' | 'Talent' | 'Addition' | 'Skill' + +export enum buffTypeEnum { + // 通用乘区 + 攻击力, 增伤, 易伤, 无视抗性, 无视防御, 穿透值, 穿透率, + // 直伤乘区 + 暴击率, 暴击伤害, + // 异常乘区 + 异常精通, 异常增伤, 异常暴击率, 异常暴击伤害, 异常持续时间, + // 其他属性,一般不直接影响伤害,但可能用于buff是否生效判断/转模 + 生命值, 防御力, 冲击力, 异常掌控 +} + +/** Buff增益类型 */ +export type buffType = keyof typeof buffTypeEnum + +export interface buff { + /** Buff状态,true生效,false无效 */ + status: boolean + /** Buff是否常驻 */ + isForever: boolean + /** Buff名称 */ + name: string + /** Buff来源 */ + source: buffSource + /** Buff增益的类型 */ + type: buffType + /** + * Buff增益数值,可为数值、数组、函数、字符串 + * @number + * - 一般情况下此值即为提高值 + * - 当buff增益类型为**攻击力/冲击力/异常精通/异常掌控/防御力/生命值**时,若此值 **<1**,则将此值理解为**初始属性**的**百分比提高** + * @array + * 根据buff.source自动选择对应等级/星级的值,支持: + * - Weapon:武器星级(进阶) + * - Talent/Addition:天赋(核心技)等级 + * @function + * 函数返回值则为提高值 + * @string + * 角色自身的buff提高值可能随技能/天赋等级提高而提高,此时可以于data.json的"buff"中添加对应的倍率信息,此时value即为键名,其首字母必须为对应技能的基类(参考技能类型命名标准) + */ + value: number | (({ avatar, buffM, calc }: { + avatar: ZZZAvatarInfo + buffM: BuffManager + calc: Calculator + }) => number) | string | number[] + /** Buff增益技能类型范围,无则对全部生效;参考技能类型命名标准 */ + range?: string[] | anomaly[] + /** Buff增益属性类型,无则对全部生效 */ + element?: element | element[] + /** + * 检查buff是否生效 + * @function + * 一般情况。武器会自动添加职业检查 + * @number + * - buff.source为Set时,判断套装数量>=该值 + * - buff.source为Rank时,判断影画数量>=该值 + */ + check?: (({ avatar, buffM, calc }: { + avatar: ZZZAvatarInfo + buffM: BuffManager + calc: Calculator + }) => boolean) | number +} + +let depth = 0, weakMapCheck = new WeakMap() +/** + * Buff管理器 + * 用于管理角色局内Buff + */ +export class BuffManager { + readonly avatar: ZZZAvatarInfo + readonly buffs: buff[] = [] + /** 套装计数 */ + setCount: { [name: string]: number } = {} + defaultBuff: { [key in keyof buff]?: buff[key] } = {} + + constructor(avatar: ZZZAvatarInfo) { + this.avatar = avatar + } + + /** 注册buff */ + new(buff: buff): buff[] + /** 注册buffs */ + new(buffs: buff[]): buff[] + new(buff: buff | buff[]) { + if (Array.isArray(buff)) { + buff.forEach(b => this.new(b)) + return this.buffs + } + // 简化参数 + if (!buff.name && (buff.source || this.defaultBuff.source) === 'Set' && this.defaultBuff.name && typeof buff.check === 'number') + buff.name = this.defaultBuff.name + buff.check + buff = _.merge({ + status: true, + isForever: false, + ...this.defaultBuff + }, buff) + if (!buff.source) { + if (buff.name.includes('核心') || buff.name.includes('天赋')) buff.source = 'Talent' + else if (buff.name.includes('额外能力')) buff.source = 'Addition' + else if (buff.name.includes('影')) buff.source = 'Rank' + else if (buff.name.includes('技')) buff.source = 'Skill' + } + if (!buff.name || !buff.value || !buff.source || !buffTypeEnum[buffTypeEnum[buff.type]]) + return logger.warn('无效buff:', buff) + // 武器buff职业检查 + if (buff.source === 'Weapon') { + if (typeof buff.check === 'function') { + const oriCheck = buff.check + buff.check = ({ avatar, buffM, calc }) => avatar.avatar_profession === weaponIDToProfession(avatar.weapon.id) && oriCheck({ avatar, buffM, calc }) + } else { + buff.check = ({ avatar }) => avatar.avatar_profession === weaponIDToProfession(avatar.weapon.id) + } + } else if (buff.source === 'Rank') { + buff.check ??= +buff.name.match(/\d/)!?.[0] + } + this.buffs.push(buff) + return this.buffs + } + + _filter(buffs: buff[], type: T, value: buff[T]): buff[] + _filter(buffs: buff[], obj: { [key in Exclude]?: buff[key] } & { element: element }, calc?: Calculator): buff[] + _filter(buffs: buff[], fnc: (buff: buff, index: number) => boolean): buff[] + _filter( + buffs: buff[], + param: + | T + | ({ [key in Exclude]?: buff[key] } & { element: element }) + | ((buff: buff, index: number) => boolean), + valueOcalc?: buff[T] | Calculator + ) { + depth++ + try { + if (typeof param === 'string') { + buffs = buffs.filter(buff => buff[param] === valueOcalc) + } else if (typeof param === 'object') { + buffs = buffs.filter(buff => { + if (buff.status === false) return false + for (const key in param) { + if (key === 'range') { + const buffRange = buff.range + if (!buffRange || !param.range) continue // 对任意类型生效 + param.range = param.range.filter(r => typeof r === 'string') + if (!param.range.length) continue + // buff作用范围向后覆盖 + else if (!param.range.every(ST => buffRange.some(BT => ST.startsWith(BT)))) return false + else continue + } else if (key === 'element') { + if (!buff.element || !param.element) continue // 对任意属性生效 + if (Array.isArray(buff.element)) { + if (buff.element.includes(param.element)) continue + return false + } + } + // @ts-ignore + if (buff[key] !== param[key]) return false + } + if (buff.check) { + if (typeof buff.check === 'number') { + if (buff.source === 'Set' && (this.setCount[buff.name.replace(/\d$/, '')] < buff.check)) return false + else if (buff.source === 'Rank' && (this.avatar.rank < buff.check)) return false + } else if (valueOcalc) { + if (weakMapCheck.has(buff)) { + // console.log(`depth:${depth} ${buff.name}:${weakMapCheck.get(buff)}`) + if (!weakMapCheck.get(buff)) return false + } else { + weakMapCheck.set(buff, false) + if (!buff.check({ + avatar: this.avatar, + buffM: this, + calc: valueOcalc as Calculator + })) return false + weakMapCheck.set(buff, true) + } + } else { + logger.debug('未传入calc:' + buff.name) + return false + } + } + return true + }) + } else { + buffs = buffs.filter(param) + } + } catch (e) { + logger.error(e) + } + if (--depth === 0) { + // console.log('重置weakMapCheck') + weakMapCheck = new WeakMap() + } + return buffs + } + + /** + * 遍历buff列表 + */ + forEach(fnc: (buff: buff, index: number) => void) { + return this.buffs.forEach(fnc) + } + + /** + * 根据单个指定属性筛选buff,不作进一步判断 + */ + filter(type: T, value: buff[T]): buff[] + /** + * 根据多个指定属性筛选 **启用状态** 的buff + */ + filter(obj: { [key in Exclude]?: buff[key] } & { element: element }, calc?: Calculator): buff[] + /** + * 根据指定函数筛选buff + */ + filter(fnc: (buff: buff, index: number) => boolean): buff[] + filter( + param: + | T + | ({ [key in Exclude]?: buff[key] } & { element: element }) + | ((buff: buff, index: number) => boolean), + valueOcalc?: buff[T] | Calculator + ) { + // @ts-ignore + return this._filter(this.buffs, param, valueOcalc) + } + + operator(type: T, value: buff[T], fnc: (buff: buff) => void) { + this.forEach(buff => { + if (buff[type] === value) { + fnc(buff) + } + }) + } + + /** + * 关闭符合条件的所有buff + */ + close(type: T, value: buff[T]) { + this.operator(type, value, buff => buff.status = false) + } + + /** + * 开启符合条件的所有buff + */ + open(type: T, value: buff[T]) { + this.operator(type, value, buff => buff.status = true) + } + + /** + * 设置后续新增buff参数的默认值 + * @param obj 直接覆盖默认值 + */ + default(obj: { [key in keyof buff]?: buff[key] }): 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 { + if (typeof param === 'object') { + this.defaultBuff = param + } else { + if (value === undefined) delete this.defaultBuff[param] + else this.defaultBuff[param] = value + } + } + +} \ No newline at end of file diff --git a/model/damage/Calculator.js b/model/damage/Calculator.js new file mode 100644 index 0000000..3d0a45a --- /dev/null +++ b/model/damage/Calculator.js @@ -0,0 +1,406 @@ +import { getMapData } from '../../utils/file.js'; +import { elementEnum, anomalyEnum } from './BuffManager.js'; +import { charData } from './avatar.js'; +import _ from 'lodash'; +const elementType2element = (elementType) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]]; +const AnomalyData = getMapData('AnomalyData'); +export class Calculator { + buffM; + avatar; + skills = []; + cache = {}; + damageCache = {}; + defaultSkill = {}; + enemy; + constructor(buffM) { + this.buffM = buffM; + this.avatar = this.buffM.avatar; + this.enemy = { + level: this.avatar.level, + basicDEF: 50, + resistance: 0.2 + }; + } + get initial_properties() { + return this.avatar.initial_properties; + } + defEnemy(param, value) { + if (typeof param === 'string' && value !== undefined) { + this.enemy[param] = value; + } + else if (typeof param === 'object') { + _.merge(this.enemy, param); + } + } + new(skill) { + if (Array.isArray(skill)) { + skill.forEach(s => this.new(s)); + return this.skills; + } + skill = _.merge({ + ...this.defaultSkill + }, skill); + if (!skill.element) + skill.element = elementType2element(this.avatar.element_type); + if (!skill.name || !skill.type) + return logger.warn('无效skill:', skill); + this.skills.push(skill); + return this.skills; + } + calc_skill(skill) { + if (typeof skill === 'string') { + const MySkill = this.skills.find(s => s.type === skill); + if (!MySkill) + return; + return this.calc_skill(MySkill); + } + if (this.damageCache[skill.type]) + return this.damageCache[skill.type]; + logger.debug(`${logger.green(skill.type)}伤害计算:`); + this.cache = {}; + if (skill.dmg) + return skill.dmg(this); + /** 缩小筛选范围 */ + const usefulBuffs = this.buffM.filter({ + element: skill.element, + range: [skill.type] + }, this); + if (skill.before) + skill.before({ avatar: this.avatar, usefulBuffs, calc: this }); + let Multiplier = 0; + const isAnomaly = typeof anomalyEnum[skill.type] === 'number'; + if (skill.fixedMultiplier) + Multiplier = skill.fixedMultiplier; + else if (isAnomaly) { + Multiplier = (skill.type === '紊乱' ? + this.get_DiscoverMultiplier(skill, usefulBuffs) : + this.get_AnomalyMultiplier(skill, usefulBuffs)) || 0; + } + else { + if (skill.skillMultiplier) + Multiplier = skill.skillMultiplier[this.get_SkillLevel(skill.type[0]) - 1]; + else + Multiplier = this.get_Multiplier(skill.type); + } + if (!Multiplier) + return logger.warn('技能倍率缺失:', skill); + const ATK = this.get_ATK(skill, usefulBuffs); + let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0; + let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0; + let CriticalArea = 0; + if (isAnomaly) { + AnomalyProficiencyArea = this.get_AnomalyProficiencyArea(skill, usefulBuffs); + AnomalyBoostArea = this.get_AnomalyBoostArea(skill, usefulBuffs); + LevelArea = this.get_LevelArea(); + AnomalyCRITRate = this.get_AnomalyCRITRate(skill, usefulBuffs); + AnomalyCRITDMG = this.get_AnomalyCRITDMG(skill, usefulBuffs); + CriticalArea = 1 + AnomalyCRITRate * (AnomalyCRITDMG - 1); + } + else { + CRITRate = this.get_CRITRate(skill, usefulBuffs); + CRITDMG = this.get_CRITDMG(skill, usefulBuffs); + CriticalArea = 1 + CRITRate * (CRITDMG - 1); + } + logger.debug(`暴击期望:${CriticalArea}`); + const BoostArea = this.get_BoostArea(skill, usefulBuffs); + const VulnerabilityArea = this.get_VulnerabilityArea(skill, usefulBuffs); + const ResistanceArea = this.get_ResistanceArea(skill, usefulBuffs); + const DefenceArea = this.get_DefenceArea(skill, usefulBuffs); + const result = isAnomaly ? + { + critDMG: AnomalyCRITRate ? ATK * Multiplier * AnomalyCRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, + expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea + } : { + critDMG: ATK * Multiplier * CRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, + expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea + }; + const detail = { + Multiplier, + ATK, + CRITRate, + CRITDMG, + CriticalArea, + BoostArea, + VulnerabilityArea, + ResistanceArea, + DefenceArea, + AnomalyCRITRate, + AnomalyCRITDMG, + AnomalyProficiencyArea, + AnomalyBoostArea, + LevelArea + }; + const damage = { skill, detail, result }; + if (skill.after) { + damage.add = (d) => { + if (typeof d === 'string') + d = this.calc_skill(d); + logger.debug('追加伤害:' + d.skill.name, d.result); + damage.result.expectDMG += d.result.expectDMG; + damage.result.critDMG += d.result.critDMG; + }; + skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs }); + } + logger.debug('最终伤害:', result); + this.damageCache[skill.type] = damage; + return damage; + } + calc() { + return this.skills.map(skill => this.calc_skill(skill)).filter(v => v && !v.skill?.isHide); + } + default(param, value) { + if (typeof param === 'object') { + this.defaultSkill = param; + } + else { + if (value === undefined) + delete this.defaultSkill[param]; + else + this.defaultSkill[param] = value; + } + } + /** + * 获取技能等级 + * @param baseType 技能基类 'A', 'E', 'C', 'R', 'T', 'L' + */ + get_SkillLevel(baseType) { + const id = ['A', 'E', 'C', 'R', , 'T', 'L'].indexOf(baseType); + if (id === -1) + return 1; + return Number(this.avatar.skills.find(({ skill_type }) => skill_type === id)?.level || 1); + } + /** + * 获取技能倍率 + * @param type 参见技能命名标准 + */ + get_Multiplier(type) { + const skillLevel = this.get_SkillLevel(type[0]); + logger.debug(`等级:${skillLevel}`); + const Multiplier = charData[this.avatar.id].skill[type]?.[skillLevel - 1]; + logger.debug(`倍率:${Multiplier}`); + return Multiplier; + } + get_AnomalyData(skill) { + let a = AnomalyData.filter(({ element_type }) => element_type === this.avatar.element_type); + if (skill.type === '紊乱') + a = a.filter(({ discover }) => discover); + else + a = a.filter(({ name, multiplier }) => name === skill.type && multiplier); + if (a.length === 1) + return a[0]; + a = a.filter(({ sub_element_type }) => sub_element_type === this.avatar.sub_element_type); + return a[0]; + } + /** 获取属性异常倍率 */ + get_AnomalyMultiplier(skill, usefulBuffs, anomalyData) { + anomalyData ||= this.get_AnomalyData(skill); + if (!anomalyData) + return; + let Multiplier = anomalyData.multiplier; + if (anomalyData.duration && anomalyData.interval) { + const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration); + const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)); + Multiplier = anomalyData.multiplier * times; + } + logger.debug(`倍率:${Multiplier}`); + return Multiplier; + } + /** 获取紊乱倍率 */ + get_DiscoverMultiplier(skill, usefulBuffs, anomalyData) { + anomalyData ||= this.get_AnomalyData(skill); + if (!anomalyData) + return; + const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration); + const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)); + const discover = anomalyData.discover; + const Multiplier = discover.fixed_multiplier + times * discover.multiplier; + logger.debug(`${anomalyData.name}紊乱 倍率:${Multiplier}`); + return Multiplier; + } + calc_value(value, buff) { + switch (typeof value) { + case 'number': return value; + case 'function': return +value({ avatar: this.avatar, buffM: this.buffM, calc: this }) || 0; + case 'string': return charData[this.avatar.id].buff?.[value]?.[this.get_SkillLevel(value[0]) - 1] || 0; + case 'object': { + if (!Array.isArray(value) || !buff) + return 0; + switch (buff.source) { + case 'Weapon': return value[this.avatar.weapon.star - 1] || 0; + case 'Talent': + case 'Addition': return value[this.get_SkillLevel('T') - 1] || 0; + } + } + default: return 0; + } + } + /** + * 获取局内属性原始值 + * @param isRatio 是否支持buff.value为数值类型且<1时按初始数值百分比提高处理 + */ + get(type, initial, skill, usefulBuffs = this.buffM.buffs, isRatio = false) { + return this.cache[type] ??= this.buffM._filter(usefulBuffs, { + element: skill?.element, + range: [skill?.type], + type + }, this).reduce((previousValue, buff) => { + const { value } = buff; + let add = 0; + if (isRatio && typeof value === 'number' && value < 1) { // 值小于1时,认为是百分比 + add = value * initial; + } + else { + add = this.calc_value(value, buff); + if (add < 1 && isRatio && Array.isArray(value)) + add *= initial; + } + logger.debug(`\tBuff:${buff.name}对${buff.range || '全类型'}增加${add}${buff.element || ''}${type}`); + return previousValue + add; + }, initial); + } + /** 攻击力 */ + get_ATK(skill, usefulBuffs) { + let ATK = this.get('攻击力', this.initial_properties.ATK, skill, usefulBuffs, true); + ATK = Math.max(0, Math.min(ATK, 10000)); + logger.debug(`攻击力:${ATK}`); + return ATK; + } + /** 暴击率 */ + get_CRITRate(skill, usefulBuffs) { + let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs); + CRITRate = Math.max(0, Math.min(CRITRate, 1)); + logger.debug(`暴击率:${CRITRate}`); + return CRITRate; + } + /** 暴击伤害 */ + get_CRITDMG(skill, usefulBuffs) { + let CRITDMG = this.get('暴击伤害', this.initial_properties.CRITDMG + 1, skill, usefulBuffs); + CRITDMG = Math.max(0, Math.min(CRITDMG, 5)); + logger.debug(`暴击伤害:${CRITDMG}`); + return CRITDMG; + } + /** 增伤区 */ + get_BoostArea(skill, usefulBuffs) { + const BoostArea = this.get('增伤', 1, skill, usefulBuffs); + logger.debug(`增伤区:${BoostArea}`); + return BoostArea; + } + /** 易伤区 */ + get_VulnerabilityArea(skill, usefulBuffs) { + const VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs); + logger.debug(`易伤区:${VulnerabilityArea}`); + return VulnerabilityArea; + } + /** 抗性区 */ + get_ResistanceArea(skill, usefulBuffs) { + const ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs); + logger.debug(`抗性区:${ResistanceArea}`); + return ResistanceArea; + } + /** 穿透值 */ + get_Pen(skill, usefulBuffs) { + let Pen = this.get('穿透值', this.initial_properties.Pen, skill, usefulBuffs); + Pen = Math.max(0, Math.min(Pen, 1000)); + logger.debug(`穿透值:${Pen}`); + return Pen; + } + /** 穿透率 */ + get_PenRatio(skill, usefulBuffs) { + let PenRatio = this.get('穿透率', this.initial_properties.PenRatio, skill, usefulBuffs); + PenRatio = Math.max(0, Math.min(PenRatio, 2)); + logger.debug(`穿透率:${PenRatio}`); + return PenRatio; + } + /** 防御区 */ + get_DefenceArea(skill, usefulBuffs) { + const get_base = (level) => Math.floor(0.1551 * Math.min(60, level) ** 2 + 3.141 * Math.min(60, level) + 47.2039); + /** 等级基数 */ + const base = get_base(this.avatar.level); + /** 基础防御 */ + const DEF = this.enemy.basicDEF / 50 * get_base(this.enemy.level); + const ignore_defence = this.get('无视防御', 0, skill, usefulBuffs); + const Pen = this.get_Pen(skill, usefulBuffs); + const PenRatio = this.get_PenRatio(skill, usefulBuffs); + /** 防御 */ + const defence = DEF * (1 - ignore_defence); + /** 有效防御 */ + const effective_defence = Math.max(0, defence * (1 - PenRatio) - Pen); + const DefenceArea = base / (effective_defence + base); + logger.debug(`防御区:${DefenceArea}`); + return DefenceArea; + } + /** 等级区 */ + get_LevelArea(level = this.avatar.level) { + const LevelArea = +(1 + 1 / 59 * (level - 1)).toFixed(4); + logger.debug(`等级区:${LevelArea}`); + return LevelArea; + } + /** 异常精通 */ + get_AnomalyProficiency(skill, usefulBuffs) { + let AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs); + AnomalyProficiency = Math.max(0, Math.min(AnomalyProficiency, 1000)); + logger.debug(`异常精通:${AnomalyProficiency}`); + return AnomalyProficiency; + } + /** 异常精通区 */ + get_AnomalyProficiencyArea(skill, usefulBuffs) { + const AnomalyProficiency = this.get_AnomalyProficiency(skill, usefulBuffs); + const AnomalyProficiencyArea = AnomalyProficiency / 100; + logger.debug(`异常精通区:${AnomalyProficiencyArea}`); + return AnomalyProficiencyArea; + } + /** 异常增伤区 */ + get_AnomalyBoostArea(skill, usefulBuffs) { + const AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs); + logger.debug(`异常增伤区:${AnomalyBoostArea}`); + return AnomalyBoostArea; + } + /** 异常暴击率 */ + get_AnomalyCRITRate(skill, usefulBuffs) { + let AnomalyCRITRate = this.get('异常暴击率', 0, skill, usefulBuffs); + AnomalyCRITRate = Math.max(0, Math.min(AnomalyCRITRate, 1)); + logger.debug(`异常暴击率:${AnomalyCRITRate}`); + return AnomalyCRITRate; + } + /** 异常暴击伤害 */ + get_AnomalyCRITDMG(skill, usefulBuffs) { + let AnomalyCRITDMG = this.get('异常暴击伤害', 1, skill, usefulBuffs); + AnomalyCRITDMG = Math.max(0, Math.min(AnomalyCRITDMG, 5)); + logger.debug(`异常暴击伤害:${AnomalyCRITDMG}`); + return AnomalyCRITDMG; + } + /** 异常持续时间 */ + get_AnomalyDuration(skill, usefulBuffs, duration = 0) { + const AnomalyDuration = +this.get('异常持续时间', duration, skill, usefulBuffs).toFixed(1); + logger.debug(`异常持续时间:${AnomalyDuration}`); + return AnomalyDuration; + } + /** 生命值 */ + get_HP(skill, usefulBuffs) { + let HP = this.get('生命值', this.initial_properties.HP, skill, usefulBuffs, true); + HP = Math.max(0, Math.min(HP, 100000)); + logger.debug(`生命值:${HP}`); + return HP; + } + /** 防御力 */ + get_DEF(skill, usefulBuffs) { + let DEF = this.get('防御力', this.initial_properties.DEF, skill, usefulBuffs, true); + DEF = Math.max(0, Math.min(DEF, 1000)); + logger.debug(`防御力:${DEF}`); + return DEF; + } + /** 冲击力 */ + get_Impact(skill, usefulBuffs) { + let Impact = this.get('冲击力', this.initial_properties.Impact, skill, usefulBuffs, true); + Impact = Math.max(0, Math.min(Impact, 1000)); + logger.debug(`冲击力:${Impact}`); + return Impact; + } + /** 异常掌控 */ + get_AnomalyMastery(skill, 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; + } +} diff --git a/model/damage/Calculator.ts b/model/damage/Calculator.ts new file mode 100644 index 0000000..3061db4 --- /dev/null +++ b/model/damage/Calculator.ts @@ -0,0 +1,545 @@ +import type { BuffManager, anomaly, buff, element } from './BuffManager.ts' +import type { ZZZAvatarInfo } from '../avatar.js' +import { getMapData } from '../../utils/file.js' +import { elementEnum, anomalyEnum } from './BuffManager.js' +import { charData } from './avatar.js' +import _ from 'lodash' + +/** 技能类型 */ +export interface skill { + /** 技能名,唯一 */ + name: string + /** 技能类型,唯一,参考技能类型命名标准 */ + type: string + /** 属性类型,不指定时,默认取角色属性 */ + element: element + /** 技能倍率数组,于character/角色名/data.json中自动获取,该json中不存在相应数据时可填写该值,以填写值为准 */ + skillMultiplier?: number[] + /** 指定固定技能倍率 */ + fixedMultiplier?: number + /** 角色面板伤害统计中是否隐藏显示 */ + isHide?: boolean + /** 自定义计算逻辑 */ + dmg?: (calc: Calculator) => damage + /** 额外处理 */ + before?: ({ avatar, usefulBuffs, calc }: { + avatar: ZZZAvatarInfo + usefulBuffs: buff[] + calc: Calculator + }) => void + after?: ({ avatar, damage, calc, usefulBuffs }: { + avatar: ZZZAvatarInfo + damage: damage + calc: Calculator + usefulBuffs: buff[] + }) => void +} + +export interface damage { + /** 技能类型 */ + skill: skill + /** 各乘区详细数据 */ + detail: { + /** 伤害倍率 */ + Multiplier: number + /** 攻击力 */ + ATK: number + /** 暴击率 */ + CRITRate: number + /** 暴伤伤害 */ + CRITDMG: number + /** 暴击区期望 */ + CriticalArea: number + /** 增伤区 */ + BoostArea: number + /** 易伤区 */ + VulnerabilityArea: number + /** 抗性区 */ + ResistanceArea: number + /** 防御区 */ + DefenceArea: number + /** 异常暴击率 */ + AnomalyCRITRate: number + /** 异常暴击伤害 */ + AnomalyCRITDMG: number + /** 异常精通区 */ + AnomalyProficiencyArea: number + /** 异常增伤区 */ + AnomalyBoostArea: number + /** 等级区 */ + LevelArea: number + } + /** 伤害结果 */ + result: { + /** 暴击伤害 */ + critDMG: number + /** 期望伤害 */ + expectDMG: number + } + add?: (damage: string | damage) => void +} + +const elementType2element = (elementType: number) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]] as element + +const AnomalyData = getMapData('AnomalyData') as { + name: string, + element: element, + element_type: number, + sub_element_type: number, + duration: number, + interval: number, + multiplier: number, + discover?: { + multiplier: number, + fixed_multiplier: number + } +}[] + +interface enemy { + /** 等级 */ + level: number + /** 1级基础防御力 */ + basicDEF: number + /** 抗性 */ + resistance: number +} + +export class Calculator { + readonly buffM: BuffManager + readonly avatar: ZZZAvatarInfo + readonly skills: skill[] = [] + private cache: { [key in buff['type']]?: number } = {} + private damageCache: { [type: string]: damage } = {} + defaultSkill: { [key in keyof skill]?: skill[key] } = {} + enemy: enemy + + constructor(buffM: BuffManager) { + this.buffM = buffM + this.avatar = this.buffM.avatar + this.enemy = { + level: this.avatar.level, + basicDEF: 50, + resistance: 0.2 + } + } + + get initial_properties() { + return this.avatar.initial_properties + } + + /** 定义敌方属性 */ + defEnemy(key: T, value: enemy[T]): void + defEnemy(enemy: enemy): void + defEnemy(param: T | enemy, value?: enemy[T]) { + if (typeof param === 'string' && value !== undefined) { + this.enemy[param] = value + } else if (typeof param === 'object') { + _.merge(this.enemy, param) + } + } + + /** 注册skill */ + new(skill: skill): skill[] + /** 注册skills */ + new(skills: skill[]): skill[] + new(skill: skill | skill[]) { + if (Array.isArray(skill)) { + skill.forEach(s => this.new(s)) + return this.skills + } + skill = _.merge({ + ...this.defaultSkill + }, skill) + if (!skill.element) skill.element = elementType2element(this.avatar.element_type) + if (!skill.name || !skill.type) return logger.warn('无效skill:', skill) + this.skills.push(skill) + return this.skills + } + + /** + * 计算已注册的技能伤害 + * @param type 技能类型名 + */ + calc_skill(type: skill['type']): damage + /** + * 计算技能伤害 + * @param skill 技能 + */ + calc_skill(skill: skill): damage + calc_skill(skill: skill['type'] | skill) { + if (typeof skill === 'string') { + const MySkill = this.skills.find(s => s.type === skill) + if (!MySkill) return + return this.calc_skill(MySkill) + } + if (this.damageCache[skill.type]) return this.damageCache[skill.type] + logger.debug(`${logger.green(skill.type)}伤害计算:`) + this.cache = {} + if (skill.dmg) return skill.dmg(this) + /** 缩小筛选范围 */ + const usefulBuffs = this.buffM.filter({ + element: skill.element, + range: [skill.type] + }, this) + if (skill.before) skill.before({ avatar: this.avatar, usefulBuffs, calc: this }) + let Multiplier = 0 + const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number' + if (skill.fixedMultiplier) Multiplier = skill.fixedMultiplier + else if (isAnomaly) { + Multiplier = ( + skill.type === '紊乱' ? + this.get_DiscoverMultiplier(skill, usefulBuffs) : + this.get_AnomalyMultiplier(skill, usefulBuffs) + ) || 0 + } else { + if (skill.skillMultiplier) Multiplier = skill.skillMultiplier[this.get_SkillLevel(skill.type[0]) - 1] + else Multiplier = this.get_Multiplier(skill.type) + } + if (!Multiplier) return logger.warn('技能倍率缺失:', skill) + const ATK = this.get_ATK(skill, usefulBuffs) + let CRITRate = 0, CRITDMG = 0, AnomalyCRITRate = 0, AnomalyCRITDMG = 0 + let AnomalyProficiencyArea = 0, AnomalyBoostArea = 0, LevelArea = 0 + let CriticalArea = 0 + if (isAnomaly) { + AnomalyProficiencyArea = this.get_AnomalyProficiencyArea(skill, usefulBuffs) + AnomalyBoostArea = this.get_AnomalyBoostArea(skill, usefulBuffs) + LevelArea = this.get_LevelArea() + AnomalyCRITRate = this.get_AnomalyCRITRate(skill, usefulBuffs) + AnomalyCRITDMG = this.get_AnomalyCRITDMG(skill, usefulBuffs) + CriticalArea = 1 + AnomalyCRITRate * (AnomalyCRITDMG - 1) + } else { + CRITRate = this.get_CRITRate(skill, usefulBuffs) + CRITDMG = this.get_CRITDMG(skill, usefulBuffs) + CriticalArea = 1 + CRITRate * (CRITDMG - 1) + } + logger.debug(`暴击期望:${CriticalArea}`) + const BoostArea = this.get_BoostArea(skill, usefulBuffs) + const VulnerabilityArea = this.get_VulnerabilityArea(skill, usefulBuffs) + const ResistanceArea = this.get_ResistanceArea(skill, usefulBuffs) + const DefenceArea = this.get_DefenceArea(skill, usefulBuffs) + const result: damage['result'] = isAnomaly ? + { + critDMG: AnomalyCRITRate ? ATK * Multiplier * AnomalyCRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea : 0, + expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea * AnomalyProficiencyArea * LevelArea * AnomalyBoostArea + } : { + critDMG: ATK * Multiplier * CRITDMG * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, + expectDMG: ATK * Multiplier * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea + } + const detail: damage['detail'] = { + Multiplier, + ATK, + CRITRate, + CRITDMG, + CriticalArea, + BoostArea, + VulnerabilityArea, + ResistanceArea, + DefenceArea, + AnomalyCRITRate, + AnomalyCRITDMG, + AnomalyProficiencyArea, + AnomalyBoostArea, + LevelArea + } + const damage: damage = { skill, detail, result } + if (skill.after) { + damage.add = (d) => { + if (typeof d === 'string') d = this.calc_skill(d) + logger.debug('追加伤害:' + d.skill.name, d.result) + damage.result.expectDMG += d.result.expectDMG + damage.result.critDMG += d.result.critDMG + } + skill.after({ avatar: this.avatar, damage, calc: this, usefulBuffs }) + } + logger.debug('最终伤害:', result) + this.damageCache[skill.type] = damage + return damage + } + + calc() { + return this.skills.map(skill => this.calc_skill(skill)).filter(v => v && !v.skill?.isHide) + } + + /** + * 设置后续新增buff参数的默认值 + * @param obj 直接覆盖默认值 + */ + default(obj: { [key in keyof skill]?: skill[key] }): void + /** + * 设置后续新增技能参数的默认值 + * @param value 为undefined时删除默认值 + */ + default(type: T, value?: skill[T]): void + default(param: T | { [key in keyof skill]?: skill[key] }, value?: skill[T]): void { + if (typeof param === 'object') { + this.defaultSkill = param + } else { + if (value === undefined) delete this.defaultSkill[param] + else this.defaultSkill[param] = value + } + } + + /** + * 获取技能等级 + * @param baseType 技能基类 'A', 'E', 'C', 'R', 'T', 'L' + */ + get_SkillLevel(baseType: string) { + const id = ['A', 'E', 'C', 'R', , 'T', 'L'].indexOf(baseType) + if (id === -1) return 1 + return Number(this.avatar.skills.find(({ skill_type }) => skill_type === id)?.level || 1) + } + + /** + * 获取技能倍率 + * @param type 参见技能命名标准 + */ + get_Multiplier(type: string) { + const skillLevel = this.get_SkillLevel(type[0]) + logger.debug(`等级:${skillLevel}`) + const Multiplier = charData[this.avatar.id].skill[type]?.[skillLevel - 1] + logger.debug(`倍率:${Multiplier}`) + return Multiplier + } + + get_AnomalyData(skill: skill) { + let a = AnomalyData.filter(({ element_type }) => element_type === this.avatar.element_type) + if (skill.type === '紊乱') a = a.filter(({ discover }) => discover) + else a = a.filter(({ name, multiplier }) => name === skill.type && multiplier) + if (a.length === 1) return a[0] + a = a.filter(({ sub_element_type }) => sub_element_type === this.avatar.sub_element_type) + return a[0] + } + + /** 获取属性异常倍率 */ + get_AnomalyMultiplier(skill: skill, usefulBuffs: buff[], anomalyData?: typeof AnomalyData[number]) { + anomalyData ||= this.get_AnomalyData(skill) + if (!anomalyData) return + let Multiplier = anomalyData.multiplier + if (anomalyData.duration && anomalyData.interval) { + const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration) + const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)) + Multiplier = anomalyData.multiplier * times + } + logger.debug(`倍率:${Multiplier}`) + return Multiplier + } + + /** 获取紊乱倍率 */ + get_DiscoverMultiplier(skill: skill, usefulBuffs: buff[], anomalyData?: typeof AnomalyData[number]) { + anomalyData ||= this.get_AnomalyData(skill) + if (!anomalyData) return + const AnomalyDuration = this.get_AnomalyDuration(skill, usefulBuffs, anomalyData.duration) + const times = Math.floor((AnomalyDuration * 10) / (anomalyData.interval * 10)) + const discover = anomalyData.discover! + const Multiplier = discover.fixed_multiplier + times * discover.multiplier + logger.debug(`${anomalyData.name}紊乱 倍率:${Multiplier}`) + return Multiplier + } + + calc_value(value: buff['value'], buff?: buff) { + switch (typeof value) { + case 'number': return value + case 'function': return +value({ avatar: this.avatar, buffM: this.buffM, calc: this }) || 0 + case 'string': return charData[this.avatar.id].buff?.[value]?.[this.get_SkillLevel(value[0]) - 1] || 0 + case 'object': { + if (!Array.isArray(value) || !buff) return 0 + switch (buff.source) { + case 'Weapon': return value[this.avatar.weapon.star - 1] || 0 + case 'Talent': + case 'Addition': return value[this.get_SkillLevel('T') - 1] || 0 + } + } + default: return 0 + } + } + + /** + * 获取局内属性原始值 + * @param isRatio 是否支持buff.value为数值类型且<1时按初始数值百分比提高处理 + */ + get(type: buff['type'], initial: number, skill: skill, usefulBuffs: buff[] = this.buffM.buffs, isRatio = false): number { + return this.cache[type] ??= this.buffM._filter(usefulBuffs, { + element: skill?.element, + range: [skill?.type], + type + }, this).reduce((previousValue, buff) => { + const { value } = buff + let add = 0 + if (isRatio && typeof value === 'number' && value < 1) { // 值小于1时,认为是百分比 + add = value * initial + } else { + add = this.calc_value(value, buff) + if (add < 1 && isRatio && Array.isArray(value)) + add *= initial + } + logger.debug(`\tBuff:${buff.name}对${buff.range || '全类型'}增加${add}${buff.element || ''}${type}`) + return previousValue + add + }, initial) + } + + /** 攻击力 */ + get_ATK(skill: skill, usefulBuffs: buff[]) { + let ATK = this.get('攻击力', this.initial_properties.ATK, skill, usefulBuffs, true) + ATK = Math.max(0, Math.min(ATK, 10000)) + logger.debug(`攻击力:${ATK}`) + return ATK + } + + /** 暴击率 */ + get_CRITRate(skill: skill, usefulBuffs: buff[]) { + let CRITRate = this.get('暴击率', this.initial_properties.CRITRate, skill, usefulBuffs) + CRITRate = Math.max(0, Math.min(CRITRate, 1)) + logger.debug(`暴击率:${CRITRate}`) + return CRITRate + } + + /** 暴击伤害 */ + get_CRITDMG(skill: skill, usefulBuffs: buff[]) { + let CRITDMG = this.get('暴击伤害', this.initial_properties.CRITDMG + 1, skill, usefulBuffs) + CRITDMG = Math.max(0, Math.min(CRITDMG, 5)) + logger.debug(`暴击伤害:${CRITDMG}`) + return CRITDMG + } + + /** 增伤区 */ + get_BoostArea(skill: skill, usefulBuffs: buff[]) { + const BoostArea = this.get('增伤', 1, skill, usefulBuffs) + logger.debug(`增伤区:${BoostArea}`) + return BoostArea + } + + /** 易伤区 */ + get_VulnerabilityArea(skill: skill, usefulBuffs: buff[]) { + const VulnerabilityArea = this.get('易伤', 1, skill, usefulBuffs) + logger.debug(`易伤区:${VulnerabilityArea}`) + return VulnerabilityArea + } + + /** 抗性区 */ + get_ResistanceArea(skill: skill, usefulBuffs: buff[]) { + const ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs) + logger.debug(`抗性区:${ResistanceArea}`) + return ResistanceArea + } + + /** 穿透值 */ + get_Pen(skill: skill, usefulBuffs: buff[]) { + let Pen = this.get('穿透值', this.initial_properties.Pen, skill, usefulBuffs) + Pen = Math.max(0, Math.min(Pen, 1000)) + logger.debug(`穿透值:${Pen}`) + return Pen + } + + /** 穿透率 */ + get_PenRatio(skill: skill, usefulBuffs: buff[]) { + let PenRatio = this.get('穿透率', this.initial_properties.PenRatio, skill, usefulBuffs) + PenRatio = Math.max(0, Math.min(PenRatio, 2)) + logger.debug(`穿透率:${PenRatio}`) + return PenRatio + } + + /** 防御区 */ + get_DefenceArea(skill: skill, usefulBuffs: buff[]) { + const get_base = (level: number) => Math.floor(0.1551 * Math.min(60, level) ** 2 + 3.141 * Math.min(60, level) + 47.2039) + /** 等级基数 */ + const base = get_base(this.avatar.level) + /** 基础防御 */ + const DEF = this.enemy.basicDEF / 50 * get_base(this.enemy.level) + const ignore_defence = this.get('无视防御', 0, skill, usefulBuffs) + const Pen = this.get_Pen(skill, usefulBuffs) + const PenRatio = this.get_PenRatio(skill, usefulBuffs) + /** 防御 */ + const defence = DEF * (1 - ignore_defence) + /** 有效防御 */ + const effective_defence = Math.max(0, defence * (1 - PenRatio) - Pen) + const DefenceArea = base / (effective_defence + base) + logger.debug(`防御区:${DefenceArea}`) + return DefenceArea + } + + /** 等级区 */ + get_LevelArea(level = this.avatar.level) { + const LevelArea = +(1 + 1 / 59 * (level - 1)).toFixed(4) + logger.debug(`等级区:${LevelArea}`) + return LevelArea + } + + /** 异常精通 */ + get_AnomalyProficiency(skill: skill, usefulBuffs: buff[]) { + let AnomalyProficiency = this.get('异常精通', this.initial_properties.AnomalyProficiency, skill, usefulBuffs) + AnomalyProficiency = Math.max(0, Math.min(AnomalyProficiency, 1000)) + logger.debug(`异常精通:${AnomalyProficiency}`) + return AnomalyProficiency + } + + /** 异常精通区 */ + get_AnomalyProficiencyArea(skill: skill, usefulBuffs: buff[]) { + const AnomalyProficiency = this.get_AnomalyProficiency(skill, usefulBuffs) + const AnomalyProficiencyArea = AnomalyProficiency / 100 + logger.debug(`异常精通区:${AnomalyProficiencyArea}`) + return AnomalyProficiencyArea + } + + /** 异常增伤区 */ + get_AnomalyBoostArea(skill: skill, usefulBuffs: buff[]) { + const AnomalyBoostArea = this.get('异常增伤', 1, skill, usefulBuffs) + logger.debug(`异常增伤区:${AnomalyBoostArea}`) + return AnomalyBoostArea + } + + /** 异常暴击率 */ + get_AnomalyCRITRate(skill: skill, usefulBuffs: buff[]) { + let AnomalyCRITRate = this.get('异常暴击率', 0, skill, usefulBuffs) + AnomalyCRITRate = Math.max(0, Math.min(AnomalyCRITRate, 1)) + logger.debug(`异常暴击率:${AnomalyCRITRate}`) + return AnomalyCRITRate + } + + /** 异常暴击伤害 */ + get_AnomalyCRITDMG(skill: skill, usefulBuffs: buff[]) { + let AnomalyCRITDMG = this.get('异常暴击伤害', 1, skill, usefulBuffs) + AnomalyCRITDMG = Math.max(0, Math.min(AnomalyCRITDMG, 5)) + logger.debug(`异常暴击伤害:${AnomalyCRITDMG}`) + return AnomalyCRITDMG + } + + /** 异常持续时间 */ + get_AnomalyDuration(skill: skill, usefulBuffs: buff[], duration: number = 0) { + const AnomalyDuration = +this.get('异常持续时间', duration, skill, usefulBuffs).toFixed(1) + logger.debug(`异常持续时间:${AnomalyDuration}`) + return AnomalyDuration + } + + /** 生命值 */ + get_HP(skill: skill, usefulBuffs: buff[]) { + let HP = this.get('生命值', this.initial_properties.HP, skill, usefulBuffs, true) + HP = Math.max(0, Math.min(HP, 100000)) + logger.debug(`生命值:${HP}`) + return HP + } + + /** 防御力 */ + get_DEF(skill: skill, usefulBuffs: buff[]) { + let DEF = this.get('防御力', this.initial_properties.DEF, skill, usefulBuffs, true) + DEF = Math.max(0, Math.min(DEF, 1000)) + logger.debug(`防御力:${DEF}`) + return DEF + } + + /** 冲击力 */ + get_Impact(skill: skill, usefulBuffs: buff[]) { + let Impact = this.get('冲击力', this.initial_properties.Impact, skill, usefulBuffs, true) + Impact = Math.max(0, Math.min(Impact, 1000)) + logger.debug(`冲击力:${Impact}`) + return Impact + } + + /** 异常掌控 */ + get_AnomalyMastery(skill: skill, usefulBuffs: buff[]) { + let AnomalyMastery = this.get('异常掌控', this.initial_properties.AnomalyMastery, skill, usefulBuffs, true) + AnomalyMastery = Math.max(0, Math.min(AnomalyMastery, 1000)) + logger.debug(`异常掌控:${AnomalyMastery}`) + return AnomalyMastery + } + +} \ No newline at end of file diff --git a/model/damage/README.md b/model/damage/README.md new file mode 100644 index 0000000..464051d --- /dev/null +++ b/model/damage/README.md @@ -0,0 +1,314 @@ + +# 伤害计算自定义 + +> 注意:如需自定义伤害计算,请先确保你拥有对伤害计算的基础知识,可参考[此帖][伤害计算]了解伤害计算的组成部分与计算方法。 + +角色伤害计算文件为[character/角色名/calc.js](./character/),如需自定义,将**calc.js**复制一份并重命名为**calc_user.js**后自行修改其中逻辑即可。**data.json**为技能倍率、天赋加成数据,如需自定义,同理将**data.json**复制一份并重命名为**data_user.json**后自行修改即可。[套装计算](./set/)和[武器计算](./weapon/)同理。 + +如需新增角色伤害计算,可复制[模板](./character/模板/)并重命名后修改相应逻辑。 + +后文将带你入门插件伤害计算逻辑,只要你需要自定义伤害计算,都建议你完整阅读: + +> 开发者已对伤害计算进行了规范化、模块化,只需要填参数就可以实现常见的伤害计算逻辑,即使不懂代码也可以参考模板独立完成。若存在问题,可于插件群内提问,或联系我的邮箱:UCPr251@gmail.com + +伤害计算需要明确这几部分:**初始属性**、**局内Buff**、**技能属性**、**敌方属性**,后文将分别说明 + +## 初始属性 + +初始属性,即**局外**时代理人的面板属性(参见[上帖][伤害计算]完整描述) + +在伤害计算逻辑中,此部分数据可直接获取,不作过多说明 + +## 局内Buff + +局内Buff,即仅在局内生效的所有增益,一个buff只能对单一属性进行增益,但可以同时作用于多个技能。合理管控buff,是做好伤害计算最重要的一步 + +### 认识buff + +每个buff由各项[buff参数](./BuffManager.ts#L46)组成,重要参数: + +```JS +{ + /** Buff名称 */ + name: string + /** Buff来源 */ + source: buffSource + /** Buff增益的类型 */ + type: buffType + /** + * Buff增益数值,可为数值、数组、函数、字符串 + * @number + * - 一般情况下此值即为提高值 + * - 当buff增益类型为攻击力/冲击力/异常精通/异常掌控/防御力/生命值时,若此值<1,则将此值理解为初始属性的百分比提高 + * @array + * 根据buff.source自动选择对应等级/星级的值,支持: + * - Weapon:武器星级(进阶) + * - Talent/Addition:天赋(核心技)等级 + * @function + * 函数返回值则为提高值 + * @string + * 角色自身的buff提高值可能随技能/天赋等级提高而提高,此时可以于data.json的"buff"中添加对应的倍率信息,此时value即为键名,其首字母必须为对应技能的基类(参考技能类型命名标准) + */ + value: number | Function | string + /** Buff增益技能类型范围,无则对全部生效;参考技能类型命名标准 */ + range?: string[] + /** Buff增益属性类型,无则对全部属性生效 */ + element?: element | element[] +} +``` + +额外说明: + +- **name**:Buff名称。可重复 + +- **source**:Buff来源。用于管理buff、简化参数、判断生效条件等。查看[buff来源](./BuffManager.ts#L30) + +- **type**:Buff增益的类型。查看[增益类型](./BuffManager.ts#L32) + +- **value**:Buff增益值。具体解释如上述 + +- **range**:Buff增益技能类型范围。该参数用于鉴别不同buff的[生效范围](#技能类型命名对buff作用的影响)(比如只对普攻生效),[填写方法](#技能类型命名标准)会在技能属性中详细说明 + +- **element**:Buff增益属性类型。该参数用于鉴别不同buff的生效属性(比如只对冰属性伤害生效)。查看[属性类型](./BuffManager.ts#L6) + +### 注册buff + +伤害计算模块提供了注册、管理各buff的接口[BuffManager](./BuffManager.ts),所有buff都需要通过此类的实例**buffM**进行注册、管理 + +Buff来源可分为三大类:武器、套装、角色(影画、核心被动、额外能力、技能),buff的注册也分为此三步骤、三部分。具体计算文件分别位于[武器](./weapon/) [套装](./set/) [角色](./character/) + +- 自定义方法:复制对应的计算文件,重命名为**原名_user.js**(如:星见雅_user.js),修改相应逻辑重启即可 + +- buff注册方法:武器、套装、角色每部分buff的注册,我都为你提供了两种方式: + + - 一:[直接导出](./character/模板/calc.js#L66):在计算文件中导出buffs数组,数组中保存各buff即可 + - 二:[函数导出](./character/模板/calc.js#L1):调用[BuffManager](./BuffManager.ts)实例**buffM**的**new**方法,传入你需要注册的buff即可 + - 当上述两方式同时存在时,会**先**注册**直接导出**的buffs,**然后**调用**导出的函数** + - 一般情况下,都更推荐**直接导出**的方法。如果你需要更灵活的导出方式,可以选择使用函数导出。 + +- buff注册原则:应尽可能地将能够吃到的buff都注册上;不考虑失衡易伤和任何需失衡触发的buff + +后文将说明武器、套装、角色三部分各自的buff注册细则 + +### 武器Buff + +[武器buff计算文件模板](./weapon/模板.js) +[武器buff两种导出方式实例](./weapon/霰落星殿.js) + +注意事项: + +- 武器的**基础属性**和**高级属性**皆已计入[**初始属性**](#初始属性),无需处理 + +- 武器的**音擎效果**需要自己注册 + +- 武器buff的**value**值一般为数组类型,具体参考[认识buff](#认识buff)部分 + +- 武器与角色的职业检查会自动进行 + +### 套装Buff + +[套装buff计算文件模板](./set/模板.js) +[套装buff两种导出方式实例](./set/折枝剑歌.js) + +注意事项: + +- 主词条的提升会自动注册,无需处理 + +- 二件套效果只有**属性伤害提升**需要注册,其他已包含于初始属性 + +- 四件套效果需要自己注册 + +### 角色Buff + +[角色buff计算文件模板](./character/模板/calc.js) +[角色buff两种导出方式实例](./character/星见雅/calc.js) + +角色buff分为影画、核心被动、额外能力、技能此四个来源 + +- **影画·Rank** + + **name**建议按照模板填写,此时命座检查会自动进行 + +- **核心被动·Talent** + + 核心被动中的**buff增益值**可能随核心技等级提升而提升,此时**value**的类型对应字符串情况,并且需要于data.json中添加对应的倍率信息,你可参考[安东伤害计算](./character/安东/calc.js#L17)的处理 + +- **额外能力·Addition** + + 额外能力的阵营效果直接视为生效,正常注册即可 + +- **技能·Skill** + + 部分角色释放技能后会给自己附加增益,正常注册即可 + +### 管理buff + +- [BuffManager](./BuffManager.ts)提供了部分管理buff的函数,可自行查看使用 + +- 较为推荐的管理buff方式为:在使用**直接导出**注册相应的buff的基础上,通过**导出函数**来管理buff,在函数中调整各buff + +- **在线调试**:将云崽底层的日志类型(根目录/config/config/bot.yaml中的log_level)修改为**debug**并重启后,插件会自动监听当前各计算文件,实时更新,并会在控制台输出伤害计算的详细过程:初始属性、buff情况、技能数据、buff生效情况、各乘区数据,可据此调试 + +- 游戏中的buff生效情况难以确定,但通过[自定义敌方属性](#自定义敌方属性)和对buff的精确管控,插件的计算结果将与游戏实机十分吻合 + +

+ + +

+ +## 技能属性 + +技能属性,即在代理人技能详情界面对该技能的描述:技能名、伤害倍率、伤害类型等 + +### 认识技能 + +技能为泛指,任何可以造成伤害的输出手段,此处都可称为技能,包括属性异常、紊乱等 + +每个技能由各项[技能参数](./Calculator.ts#L9)组成,必需参数: + +```JS +{ + /** 技能名,唯一 */ + name: string + /** 技能类型,唯一,参考技能类型命名标准 */ + type: string + /** 属性类型,不指定时,默认取角色属性 */ + element: element +} +``` + +### 技能类型 + +由于不同Buff作用的技能类型不同,为了统一判断某Buff是否对某技能生效,规定**技能类型命名标准** + +#### 技能类型命名标准 + +> - A 攻击 +> - AP 普通攻击 +> - AX 重击/蓄力攻击 +> - AQ 强化普攻 + +> - C 闪避 +> - CP 普通闪避 +> - CF 闪避反击 +> - CC 冲刺攻击 +> - CX 蓄力闪避 + +> - L 支援技 +> - LK 快速支援 +> - LZ 招架支援 +> - LT 突击支援 + +> - E 特殊技 +> - EP 普通特殊技 +> - EQ 强化特殊技 + +> - R +> - RZ 终结技 +> - RL 连携技 + +> - T 核心技 +> - 核心技中的技能各不相同,自行定义即可 + +> - 属性异常(特殊) +> - 强击 +> - 灼烧 +> - 碎冰 +> - 感电 +> - 侵蚀 +> - 紊乱 + +#### 技能类型命名解释说明 + +1. 首字母为技能所属基类,不可更改、不可单独作为技能名,后跟字母表示技能分支 + +2. 树状命名,后一位字母代表基于其前一位字母的分支,取技能名发音(倒着读);属性异常较特殊,直接以异常名作为技能类型名 + +3. 后跟数字可表示段数,如AP1表示第一段普攻;为避免混淆,数字仅表示同一技能不同段数,不用于区分不同技能 + +4. 当不需要进一步细分分支时,必须遵守此标准命名,否则可能导致Buff计算错误 + +5. 当需进一步细分多种分支时,应基于此标准已有的命名拓展命名,并确保前后一致 + +#### 技能类型命名示例 + +艾莲的冲刺攻击有三种不同的类型,为了区分,需要对其进行拓展 + +冲刺攻击类型命名标准为**CC**,故: + +- “冲刺攻击:寒潮”可表示为**CCP**(普通冲刺攻击); +- “冲刺攻击:骇浪”可表示为**CCQ**(强化冲刺攻击); +- “冲刺攻击:冰渊潜袭”可表示为**CCX**(巡游冲刺攻击),其又分为普通和蓄力两种,又可分别表示为: + - 普通巡游冲刺攻击:**CCXP** + - 蓄力巡游冲刺攻击:**CCXX** + +[点此查看](./character/艾莲/calc.js#L40)艾莲实际伤害计算文件 + +当然,若冲刺攻击不存在变体,则直接使用**CC**命名即可 + +#### 技能类型命名对Buff作用的影响 + +技能类型命名将直接决定某buff是否作用于某技能,这也是规定标准命名的原因 + +buff作用范围将以技能类型命名为依据向后覆盖。以上述[艾莲冲刺攻击命名示例](#技能类型命名示例)为例,在某buff的作用范围数组(即buff的**range**参数)中: + +- 如果包括**C**,则代表对所有基于C(闪避)的分支都生效(包括CP、CF、CC、CX等) +- 如果包括**CC**,则代表对所有基于CC(冲刺攻击)的分支都生效 +- 如果只包括**CCQ**(强化冲刺攻击),则代表只对“冲刺攻击:骇浪”生效 +- 如果只包括**CCX**(巡游冲刺攻击),则代表对“冲刺攻击:冰渊潜袭”生效(无论普通或蓄力) +- 如果只包括**CCXX**(蓄力巡游冲刺攻击),则代表只对“冲刺攻击:冰渊潜袭”的蓄力巡游冲刺攻击生效 + +### 技能倍率 + +[点此查看模板技能倍率](./character/模板/data.json) + +不同等级的技能倍率不同,新增某技能的伤害计算时需要你手动添加对应的倍率信息 + +技能倍率保存在character/角色名/**data.json**中,json数据的skill中的各个**键**即为技能类型**type**,**值**即为每个等级对应的倍率数组(长度16) + +需要自定义data.json时,同样复制一份重命名为**data_user.json**即可 + +### 注册技能 + +伤害计算模块提供了注册各技能的接口[Calculator](./Calculator.ts),所有技能都需要通过此类的实例**calc**进行注册 + +技能的注册较为简单: + +1. 参考[模板](./character/模板/calc.js#L115),填入各技能相应参数(一般只需要填name、type参数) + +2. 于[data.json](./character/模板/data.json)中为每个技能填写倍率(异常伤害无需填写) + +注意事项: + +- 若某技能所造成伤害的属性与角色属性不符,应指定该技能的属性**element** + +- 技能的参数有较多可选的拓展,用于处理更复杂的情况,请自行查看[源码](./Calculator.ts)和已有角色的计算案例 + +- 目前只可注册角色的技能,部分武器有独立的造成额外伤害的机制,暂不考虑 + +## 敌方属性 + +影响伤害计算的敌方属性有:防御力、抗性、弱点 + +### 敌方属性的影响 + +- **防御力**:影响防御乘区。影响直伤和异常伤害计算 +- **抗性、弱点**:影响抗性乘区。影响直伤和异常伤害计算 + +敌方属性可查看[此表](https://img.nga.178.com/attachments/mon_202407/16/axvkQq44x-2xpiZyT3cSwm-1hf.png) + +### 自定义敌方属性 + +可以用 **等级、1级基础防御力、抗性区常量** 三个参数来替代**防御力、抗性、弱点**: + +- **等级、1级基础防御力**:根据这两个参数可计算出敌方防御力。插件默认敌方等级=角色等级,1级基础防御力=50 + +- **抗性区常量**:敌方存在对应的弱点时,此值为 **0.2**;敌方存在对应的抗性时,此值为 **-0.2**;既不抗也不弱,此值为 **0**。插件默认抗性区常量=0.2 + +通过在角色伤害计算文件中导出**calc**函数,调用Calculator的[defEnemy](./Calculator.ts)方法,你可以对此三个参数进行自定义 + +目前暂不支持指令自定义,暂不支持直接指定具体敌人 + +--- + +[伤害计算]:https://www.miyoushe.com/zzz/article/55265618 \ No newline at end of file diff --git a/model/damage/avatar.js b/model/damage/avatar.js index e295756..1dcdfea 100644 --- a/model/damage/avatar.js +++ b/model/damage/avatar.js @@ -1,806 +1,149 @@ -import _ from 'lodash'; -import { getMapData } from '../../utils/file.js'; -import { calculate_damage } from './role.js'; -import { ZZZAvatarInfo } from '../avatar.js'; -const skilldict = getMapData('SkillData'); -/** - * 角色加成 - * @param {ZZZAvatarInfo} data 角色信息 - * @param {ZZZAvatarInfo['damage_basic_properties']['base_detail']} base_detail 基础属性 - * @param {ZZZAvatarInfo['damage_basic_properties']['bonus_detail']} bonus_detail 套装加成 - * @returns {{ - * title: string, - * value: {name: string, value: number}[] - * }[]} 伤害列表 - */ -export const avatar_ability = (data, base_detail, bonus_detail) => { - const damagelist = []; - switch (data.id) { - // 艾莲 - case 1191: { - /** 处理命座加成 */ - if (data.rank >= 1) { - const CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.12; - } - if (data.rank >= 2) { - const ES_CriticalDamageBase = _.get( - bonus_detail, - 'ES_CriticalDamageBase', - 0 - ); - bonus_detail['ES_CriticalDamageBase'] = ES_CriticalDamageBase + 0.6; - const EH_CriticalDamageBase = _.get( - bonus_detail, - 'EH_CriticalDamageBase', - 0 - ); - bonus_detail['EH_CriticalDamageBase'] = EH_CriticalDamageBase + 0.6; - } - if (data.rank >= 6) { - const PenRatio = _.get(bonus_detail, 'PenRatioBase', 0); - bonus_detail['PenRatioBase'] = PenRatio + 0.2; - - const C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + 2.5; - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const CDB = getskilllevelnum(data.id, data.skills, 'T', 'T'); - const C_CriticalDamageBase = _.get( - bonus_detail, - 'C_CriticalDamageBase', - 0 - ); - bonus_detail['C_CriticalDamageBase'] = C_CriticalDamageBase + CDB; - const A_CriticalDamageBase = _.get( - bonus_detail, - 'A_CriticalDamageBase', - 0 - ); - bonus_detail['A_CriticalDamageBase'] = A_CriticalDamageBase + CDB; - - const IceDmgAdd = _.get(bonus_detail, 'Ice_DmgAdd', 0); - bonus_detail['Ice_DmgAdd'] = IceDmgAdd + 0.3; - - /** 计算伤害 */ - /** 计算普攻伤害 */ - const skill_multiplier1 = getskilllevelnum( - data.id, - data.skills, - 'A', - 'A' - ); - const damagelist1 = calculate_damage( - base_detail, - bonus_detail, - 'A', - 'A', - 'Ice', - skill_multiplier1, - data.level - ); - const damage1 = { - title: '普通攻击:急冻修剪法', - value: damagelist1, - }; - damagelist.push(damage1); - - /** 计算冲刺伤害 */ - const skill_multiplier2 = getskilllevelnum( - data.id, - data.skills, - 'C', - 'C' - ); - const damagelist2 = calculate_damage( - base_detail, - bonus_detail, - 'C', - 'C', - 'Ice', - skill_multiplier2, - data.level - ); - const damage2 = { - title: '冲刺攻击:冰渊潜袭', - value: damagelist2, - }; - damagelist.push(damage2); - - /** 计算特殊技伤害 */ - const skill_multiplier3 = getskilllevelnum( - data.id, - data.skills, - 'E', - 'EH' - ); - const damagelist3 = calculate_damage( - base_detail, - bonus_detail, - 'EUP', - 'EH', - 'Ice', - skill_multiplier3, - data.level - ); - const damage3 = { - title: '强化特殊技:横扫', - value: damagelist3, - }; - damagelist.push(damage3); - - const skill_multiplier4 = getskilllevelnum( - data.id, - data.skills, - 'E', - 'ES' - ); - const damagelist4 = calculate_damage( - base_detail, - bonus_detail, - 'EUP', - 'ES', - 'Ice', - skill_multiplier4, - data.level - ); - const damage4 = { - title: '强化特殊技:鲨卷风', - value: damagelist4, - }; - damagelist.push(damage4); - - /** 计算连携技伤害 */ - const skill_multiplier5 = getskilllevelnum( - data.id, - data.skills, - 'R', - 'RL' - ); - const damagelist5 = calculate_damage( - base_detail, - bonus_detail, - 'RL', - 'RL', - 'Ice', - skill_multiplier5, - data.level - ); - const damage5 = { - title: '连携技:雪崩', - value: damagelist5, - }; - damagelist.push(damage5); - - /** 计算终结技伤害 */ - const skill_multiplier6 = getskilllevelnum( - data.id, - data.skills, - 'R', - 'R' - ); - const damagelist6 = calculate_damage( - base_detail, - bonus_detail, - 'R', - 'R', - 'Ice', - skill_multiplier6, - data.level - ); - const damage6 = { - title: '终结技:永冬狂宴', - value: damagelist6, - }; - damagelist.push(damage6); - break; - } - // 朱鸢 - case 1241: { - /** 处理命座加成 */ - if (data.rank >= 2) { - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + 0.5; - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + 0.5; - } - if (data.rank >= 4) { - let A_ResistancePenetration = _.get( - bonus_detail, - 'A_ResistancePenetration', - 0 - ); - bonus_detail['A_ResistancePenetration'] = A_ResistancePenetration + 0.25; - let C_ResistancePenetration = _.get( - bonus_detail, - 'C_ResistancePenetration', - 0 - ); - bonus_detail['C_ResistancePenetration'] = C_ResistancePenetration + 0.25; - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const DMG_ADD = getskilllevelnum(data.id, data.skills, 'T', 'T'); - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + DMG_ADD; - - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + DMG_ADD; - - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.3; - - /** 计算伤害 */ - /** 计算普攻伤害 */ - const skill_multiplier1 = getskilllevelnum( - data.id, - data.skills, - 'A', - 'A' - ); - const damagelist1 = calculate_damage( - base_detail, - bonus_detail, - 'A', - 'A', - 'Ether', - skill_multiplier1, - data.level - ); - const damage1 = { - title: '普通攻击:请勿抵抗', - value: damagelist1, - }; - damagelist.push(damage1); - - /** 计算冲刺伤害 */ - const skill_multiplier2 = getskilllevelnum( - data.id, - data.skills, - 'C', - 'C' - ); - const damagelist2 = calculate_damage( - base_detail, - bonus_detail, - 'C', - 'C', - 'Ether', - skill_multiplier2, - data.level - ); - const damage2 = { - title: '冲刺攻击:火力压制', - value: damagelist2, - }; - damagelist.push(damage2); - - /** 计算强化特殊技伤害 */ - const skill_multiplier3 = getskilllevelnum( - data.id, - data.skills, - 'E', - 'EUP' - ); - let damagelist3 = calculate_damage( - base_detail, - bonus_detail, - 'EUP', - 'EUP', - 'Ether', - skill_multiplier3, - data.level - ); - if (data.rank >= 6) { - let damagelist_add = calculate_damage( - base_detail, - bonus_detail, - 'EUP', - 'EUP', - 'Ether', - 2.2, - data.level - ); - damagelist3['cd'] = damagelist3['cd'] + damagelist_add['cd'] * 4; - damagelist3['qw'] = damagelist3['qw'] + damagelist_add['qw'] * 4; - } - const damage3 = { - title: '强化特殊技:全弹连射', - value: damagelist3, - }; - damagelist.push(damage3); - - /** 计算连携技伤害 */ - const skill_multiplier4 = getskilllevelnum( - data.id, - data.skills, - 'R', - 'RL' - ); - const damagelist4 = calculate_damage( - base_detail, - bonus_detail, - 'RL', - 'RL', - 'Ether', - skill_multiplier4, - data.level - ); - const damage4 = { - title: '连携技:歼灭模式', - value: damagelist4, - }; - damagelist.push(damage4); - - /** 计算终结技伤害 */ - const skill_multiplier5 = getskilllevelnum( - data.id, - data.skills, - 'R', - 'R' - ); - const damagelist5 = calculate_damage( - base_detail, - bonus_detail, - 'R', - 'R', - 'Ether', - skill_multiplier5, - data.level - ); - const damage5 = { - title: '终结技:歼灭模式MAX', - value: damagelist5, - }; - damagelist.push(damage5); - break; - } - // 11号 - case 1041: { - /** 处理命座加成 */ - if (data.rank >= 2) { - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + 0.36; - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + 0.36; - } - if (data.rank >= 6) { - let A_ResistancePenetration = _.get(bonus_detail, 'A_ResistancePenetration', 0); - bonus_detail['A_ResistancePenetration'] = A_ResistancePenetration + 0.25; - let C_ResistancePenetration = _.get(bonus_detail, 'C_ResistancePenetration', 0); - bonus_detail['C_ResistancePenetration'] = C_ResistancePenetration + 0.25; - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const DMG_ADD = getskilllevelnum(data.id, data.skills, 'T', 'T'); - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + DMG_ADD; - - let Fire_DmgAdd = _.get(bonus_detail, 'Fire_DmgAdd', 0); - bonus_detail['Fire_DmgAdd'] = Fire_DmgAdd + 0.1; - - /** 计算伤害 */ - /** 计算普攻伤害 */ - const skill_multiplier1 = getskilllevelnum(data.id, data.skills, 'A', 'A'); - const damagelist1 = calculate_damage(base_detail, bonus_detail, 'A', 'A', 'Fire', skill_multiplier1, data.level); - const damage1 = { - title: '普通攻击:火力镇压', - value: damagelist1, - }; - damagelist.push(damage1); - - /** 计算冲刺伤害 */ - const skill_multiplier2 = getskilllevelnum(data.id, data.skills, 'C', 'C'); - const damagelist2 = calculate_damage(base_detail, bonus_detail, 'C', 'C', 'Fire', skill_multiplier2, data.level); - const damage2 = { - title: '闪避反击:逆火', - value: damagelist2, - }; - damagelist.push(damage2); - - /** 计算强化特殊技伤害 */ - const skill_multiplier3 = getskilllevelnum(data.id, data.skills, 'E', 'E'); - let damagelist3 = calculate_damage(base_detail, bonus_detail, 'E', 'E', 'Fire', skill_multiplier3, data.level); - const damage3 = { - title: '强化特殊技:盛燃烈火', - value: damagelist3, - }; - damagelist.push(damage3); - - /** 计算连携技伤害 */ - const skill_multiplier4 = getskilllevelnum(data.id, data.skills, 'R', 'RL'); - const damagelist4 = calculate_damage(base_detail, bonus_detail, 'RL', 'RL', 'Fire', skill_multiplier4, data.level); - const damage4 = { - title: '连携技:昂扬烈焰', - value: damagelist4, - }; - damagelist.push(damage4); - - /** 计算终结技伤害 */ - const skill_multiplier5 = getskilllevelnum(data.id, data.skills, 'R', 'R'); - const damagelist5 = calculate_damage(base_detail, bonus_detail, 'R', 'R', 'Fire', skill_multiplier5, data.level); - const damage5 = { - title: '终结技:轰鸣烈焰', - value: damagelist5, - }; - damagelist.push(damage5); - break; - } - // 青衣 - case 1251: { - /** 处理命座加成 */ - if (data.rank >= 1) { - let ignore_defence = _.get(bonus_detail, 'ignore_defence', 0); - bonus_detail['ignore_defence'] = ignore_defence + 0.15; - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.2; - } - if (data.rank >= 2) { - let T_stundmgmultiplier = _.get(bonus_detail, 'T_stundmgmultiplier', 0); - bonus_detail['T_stundmgmultiplier'] = T_stundmgmultiplier * 1.35; - } - if (data.rank >= 6) { - let All_ResistancePenetration = _.get(bonus_detail, 'All_ResistancePenetration', 0); - bonus_detail['All_ResistancePenetration'] = All_ResistancePenetration + 0.20; - let A_CriticalDamageBase = _.get(bonus_detail, 'A_CriticalDamageBase', 0); - bonus_detail['A_CriticalDamageBase'] = A_CriticalDamageBase + 1; - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const TF = getskilllevelnum(data.id, data.skills, 'T', 'T'); - let stundmgmultiplier = _.get(bonus_detail, 'stundmgmultiplier', 0); - bonus_detail['stundmgmultiplier'] = stundmgmultiplier + TF * 20; - let ImpactRatio = _.get(bonus_detail, 'ImpactRatio', 0); - if (ImpactRatio > 120) { - if (ImpactRatio >= 220) { - let AttackDelta = _.get(bonus_detail, 'AttackDelta', 0); - bonus_detail['AttackDelta'] = AttackDelta + 600; - } else { - let AttackDelta = _.get(bonus_detail, 'AttackDelta', 0); - bonus_detail['AttackDelta'] = (AttackDelta - 120) * 6; - } - } - - /** 计算伤害 */ - /** 计算普攻伤害 */ - const skill_multiplier1 = getskilllevelnum(data.id, data.skills, 'A', 'A'); - const damagelist1 = calculate_damage(base_detail, bonus_detail, 'A', 'A', 'Electric', skill_multiplier1, data.level); - const damage1 = { - title: '普通攻击:醉花月云转', - value: damagelist1, - }; - damagelist.push(damage1); - - /** 计算冲刺伤害 */ - const skill_multiplier2 = getskilllevelnum(data.id, data.skills, 'C', 'C'); - const damagelist2 = calculate_damage(base_detail, bonus_detail, 'C', 'C', 'Electric', skill_multiplier2, data.level); - const damage2 = { - title: '闪避反击:意不尽', - value: damagelist2, - }; - damagelist.push(damage2); - - /** 计算强化特殊技伤害 */ - const skill_multiplier3 = getskilllevelnum(data.id, data.skills, 'E', 'E'); - let damagelist3 = calculate_damage(base_detail, bonus_detail, 'E', 'E', 'Ether', skill_multiplier3, data.level); - const damage3 = { - title: '强化特殊技:月上海棠', - value: damagelist3, - }; - damagelist.push(damage3); - - /** 计算连携技伤害 */ - const skill_multiplier4 = getskilllevelnum(data.id, data.skills, 'R', 'RL'); - const damagelist4 = calculate_damage(base_detail, bonus_detail, 'RL', 'RL', 'Electric', skill_multiplier4, data.level); - const damage4 = { - title: '连携技:太平令', - value: damagelist4, - }; - damagelist.push(damage4); - - /** 计算终结技伤害 */ - const skill_multiplier5 = getskilllevelnum(data.id, data.skills, 'R', 'R'); - const damagelist5 = calculate_damage(base_detail, bonus_detail, 'R', 'R', 'Electric', skill_multiplier5, data.level); - const damage5 = { - title: '终结技:八声甘州', - value: damagelist5, - }; - damagelist.push(damage5); - break; - } - // 猫又 - case 1021: { - /** 处理命座加成 */ - if (data.rank >= 1) { - let Physical_ResistancePenetration = _.get(bonus_detail, 'Physical_ResistancePenetration', 0); - bonus_detail['Physical_ResistancePenetration'] = Physical_ResistancePenetration + 0.16; - } - if (data.rank >= 4) { - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase * 0.14; - } - if (data.rank >= 6) { - let CriticalDamageBase = _.get(bonus_detail, 'CriticalDamageBase', 0); - bonus_detail['CriticalDamageBase'] = CriticalDamageBase + 0.54; - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const TF = getskilllevelnum(data.id, data.skills, 'T', 'T'); - let All_DmgAdd = _.get(bonus_detail, 'All_DmgAdd', 0); - bonus_detail['All_DmgAdd'] = All_DmgAdd + TF; - let E_DmgAdd = _.get(bonus_detail, 'E_DmgAdd', 0); - bonus_detail['E_DmgAdd'] = E_DmgAdd + 0.7; - - /** 计算伤害 */ - /** 计算普攻伤害 */ - const skill_multiplier1 = getskilllevelnum(data.id, data.skills, 'A', 'A'); - const damagelist1 = calculate_damage(base_detail, bonus_detail, 'A', 'A', 'Physical', skill_multiplier1, data.level); - const damage1 = { - title: '普通攻击:猫猫爪刺', - value: damagelist1, - }; - damagelist.push(damage1); - - /** 计算冲刺伤害 */ - const skill_multiplier2 = getskilllevelnum(data.id, data.skills, 'C', 'C'); - const damagelist2 = calculate_damage(base_detail, bonus_detail, 'C', 'C', 'Physical', skill_multiplier2, data.level); - const damage2 = { - title: '闪避反击:虚影双刺', - value: damagelist2, - }; - damagelist.push(damage2); - - /** 计算强化特殊技伤害 */ - const skill_multiplier3 = getskilllevelnum(data.id, data.skills, 'E', 'E'); - let damagelist3 = calculate_damage(base_detail, bonus_detail, 'E', 'E', 'Physical', skill_multiplier3, data.level); - const damage3 = { - title: '强化特殊技:超~凶奇袭!', - value: damagelist3, - }; - damagelist.push(damage3); - - /** 计算连携技伤害 */ - const skill_multiplier4 = getskilllevelnum(data.id, data.skills, 'R', 'RL'); - const damagelist4 = calculate_damage(base_detail, bonus_detail, 'RL', 'RL', 'Physical', skill_multiplier4, data.level); - const damage4 = { - title: '连携技:刃爪挥击', - value: damagelist4, - }; - damagelist.push(damage4); - - /** 计算终结技伤害 */ - const skill_multiplier5 = getskilllevelnum(data.id, data.skills, 'R', 'R'); - const damagelist5 = calculate_damage(base_detail, bonus_detail, 'R', 'R', 'Physical', skill_multiplier5, data.level); - const damage5 = { - title: '终结技:刃爪强袭', - value: damagelist5, - }; - damagelist.push(damage5); - break; - } - // 安东 - case 1111: { - /** 处理命座加成 */ - if (data.rank >= 4) { - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.1; - } - if (data.rank >= 6) { - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + 0.24; - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + 0.24; - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const TF = getskilllevelnum(data.id, data.skills, 'T', 'T'); - const TF2 = getskilllevelnum(data.id, data.skills, 'T', 'T2'); - - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + TF2; - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + TF2; - - let E_DmgAdd = _.get(bonus_detail, 'E_DmgAdd', 0); - bonus_detail['E_DmgAdd'] = E_DmgAdd + TF; - let RL_DmgAdd = _.get(bonus_detail, 'RL_DmgAdd', 0); - bonus_detail['RL_DmgAdd'] = RL_DmgAdd + TF; - let R_DmgAdd = _.get(bonus_detail, 'R_DmgAdd', 0); - bonus_detail['R_DmgAdd'] = R_DmgAdd + TF; - - /** 计算伤害 */ - /** 计算普攻伤害 */ - const skill_multiplier1 = getskilllevelnum(data.id, data.skills, 'A', 'A'); - const damagelist1 = calculate_damage(base_detail, bonus_detail, 'A', 'A', 'Electric', skill_multiplier1, data.level); - const damage1 = { - title: '普通攻击:热血上工操', - value: damagelist1, - }; - damagelist.push(damage1); - - /** 计算冲刺伤害 */ - const skill_multiplier2 = getskilllevelnum(data.id, data.skills, 'C', 'C'); - const damagelist2 = calculate_damage(base_detail, bonus_detail, 'C', 'C', 'Electric', skill_multiplier2, data.level); - const damage2 = { - title: '闪避反击:过载钻击', - value: damagelist2, - }; - damagelist.push(damage2); - - /** 计算强化特殊技伤害 */ - const skill_multiplier3 = getskilllevelnum(data.id, data.skills, 'E', 'E'); - let damagelist3 = calculate_damage(base_detail, bonus_detail, 'E', 'E', 'Electric', skill_multiplier3, data.level); - const damage3 = { - title: '特殊技:爆发钻击', - value: damagelist3, - }; - damagelist.push(damage3); - - /** 计算连携技伤害 */ - const skill_multiplier4 = getskilllevelnum(data.id, data.skills, 'R', 'RL'); - const damagelist4 = calculate_damage(base_detail, bonus_detail, 'RL', 'RL', 'Electric', skill_multiplier4, data.level); - const damage4 = { - title: '连携技:转转转!', - value: damagelist4, - }; - damagelist.push(damage4); - - /** 计算终结技伤害 */ - const skill_multiplier5 = getskilllevelnum(data.id, data.skills, 'R', 'R'); - const damagelist5 = calculate_damage(base_detail, bonus_detail, 'R', 'R', 'Electric', skill_multiplier5, data.level); - const damage5 = { - title: '终结技:转转转转转!', - value: damagelist5, - }; - damagelist.push(damage5); - break; - } - // 星见雅 - case 1091: { - /** 处理命座加成 */ - if (data.rank >= 1) { - const AUP_IgnoreDefence = _.get(bonus_detail, 'AUP_IgnoreDefence', 0) - bonus_detail['AUP_IgnoreDefence'] = AUP_IgnoreDefence + 0.36 - } - if (data.rank >= 2) { - const A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0) - bonus_detail['A_DmgAdd'] = A_DmgAdd + 0.30 - const C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0) - bonus_detail['C_DmgAdd'] = C_DmgAdd + 0.30 - const CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0) - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.15 - } - if (data.rank >= 4) { - const TP_DmgAdd = _.get(bonus_detail, 'TP_DmgAdd', 0) - bonus_detail['TP_DmgAdd'] = TP_DmgAdd + 0.3 - } - if (data.rank >= 6) { - const AUP_DmgAdd = _.get(bonus_detail, 'AUP_DmgAdd', 0) - bonus_detail['AUP_DmgAdd'] = AUP_DmgAdd + 0.3 - } - - /** 处理天赋加成 */ - /** 获取天赋等级与加成倍率 */ - const AUP_DmgAdd = _.get(bonus_detail, 'AUP_DmgAdd', 0) - bonus_detail['AUP_DmgAdd'] = AUP_DmgAdd + 0.6 - const AUP_ResistancePenetration = _.get(bonus_detail, 'AUP_ResistancePenetration', 0) - bonus_detail['AUP_ResistancePenetration'] = AUP_ResistancePenetration + 0.3 // 紊乱无视冰抗 - const R_DmgAdd = _.get(bonus_detail, 'R_DmgAdd', 0) // 终结技30%冰伤加成 - bonus_detail['R_DmgAdd'] = R_DmgAdd + 0.3 - - logger.debug('最终bonus_detail', bonus_detail) - /** 计算伤害 */ - /** 计算普攻伤害 */ - const A_multiplier = getskilllevelnum(data.id, data.skills, 'A', 'A') - const A_damage = calculate_damage(base_detail, bonus_detail, 'A', 'A', 'Ice', A_multiplier, data.level) - damagelist.push({ - title: '普通攻击:风花五段', - value: A_damage, - }) - - /** 计算闪避反击伤害 */ - const C_multiplier = getskilllevelnum(data.id, data.skills, 'C', 'C') - const C_damage = calculate_damage(base_detail, bonus_detail, 'C', 'C', 'Ice', C_multiplier, data.level) - damagelist.push({ - title: '闪避反击:寒雀', - value: C_damage, - }) - - /** 计算霜灼·破伤害 */ - const TP_multiplier = getskilllevelnum(data.id, data.skills, 'T', 'TP') - const TP_damage = calculate_damage(base_detail, bonus_detail, 'TP', 'TP', 'Ice', TP_multiplier, data.level) - damagelist.push({ - title: '霜灼·破', - value: TP_damage, - }) - - /** 计算蓄力攻击伤害 */ - const AUP1_multiplier = getskilllevelnum(data.id, data.skills, 'A', 'AUP1') - const AUP1_damage = calculate_damage(base_detail, bonus_detail, 'AUP', 'AUP1', 'Ice', AUP1_multiplier, data.level) - damagelist.push({ - title: '蓄力攻击:一段蓄', - value: AUP1_damage, - }) - const AUP2_multiplier = getskilllevelnum(data.id, data.skills, 'A', 'AUP2') - const AUP2_damage = calculate_damage(base_detail, bonus_detail, 'AUP', 'AUP2', 'Ice', AUP2_multiplier, data.level) - if (data.rank >= 6) { // 6命累加蓄力伤害 - AUP2_damage['cd'] += AUP1_damage['cd'] - AUP2_damage['qw'] += AUP1_damage['qw'] - } - damagelist.push({ - title: '蓄力攻击:二段蓄', - value: AUP2_damage, - }) - const AUP3_multiplier = getskilllevelnum(data.id, data.skills, 'A', 'AUP3') - const AUP3_damage = calculate_damage(base_detail, bonus_detail, 'AUP', 'AUP3', 'Ice', AUP3_multiplier, data.level) - if (data.rank >= 6) { - AUP3_damage['cd'] += AUP2_damage['cd'] - AUP3_damage['qw'] += AUP2_damage['qw'] - } - damagelist.push({ - title: '蓄力攻击:三段蓄', - value: AUP3_damage, - }) - - /** 计算特殊技伤害 */ - const E_multiplier = getskilllevelnum(data.id, data.skills, 'E', 'EF') - const E_damage = calculate_damage(base_detail, bonus_detail, 'EUP', 'EF', 'Ice', E_multiplier, data.level) - damagelist.push({ - title: '强化特殊技:飞雪', - value: E_damage, - }) - - const EZ_multiplier = getskilllevelnum(data.id, data.skills, 'E', 'EF2') - const EZ_damage = calculate_damage(base_detail, bonus_detail, 'EUP', 'EF2', 'Ice', EZ_multiplier, data.level) - damagelist.push({ - title: '强化特殊技:飞雪(二段)', - value: EZ_damage, - }) - - /** 计算连携技伤害 */ - const RL_multiplier = getskilllevelnum(data.id, data.skills, 'R', 'RL') - const RL_damage = calculate_damage(base_detail, bonus_detail, 'RL', 'RL', 'Ice', RL_multiplier, data.level) - damagelist.push({ - title: '连携技:春临', - value: RL_damage, - }) - - /** 计算终结技伤害 */ - const R_multiplier = getskilllevelnum(data.id, data.skills, 'R', 'R') - const R_damage = calculate_damage(base_detail, bonus_detail, 'R', 'R', 'Ice', R_multiplier, data.level) - damagelist.push({ - title: '终结技:名残雪', - value: R_damage, - }) - break - } - } - logger.debug(logger.green(data.name_mi18n + '伤害:'), damagelist) - return damagelist -}; - -export const getskilllevelnum = (avatarId, skills, skilltype, skillname) => { - let skill_typeid = 0; - if (skilltype == 'A') skill_typeid = 0; - else if (skilltype == 'C') skill_typeid = 2; - else if (skilltype == 'E') skill_typeid = 1; - else if (skilltype == 'R') skill_typeid = 3; - else if (skilltype == 'L') skill_typeid = 6; - else if (skilltype == 'T') skill_typeid = 5; - let skilllevel = - Number( - skills.find(property => property.skill_type === skill_typeid)?.level || 1 - ) - 1; - return skilldict[avatarId][skillname][skilllevel]; -}; - -export const has_calculation = avatarId => { - return Object.keys(skilldict).includes(avatarId.toString()); +import { aliasToID } from '../../lib/convert/char.js'; +import config from '../../../../lib/config/config.js'; +import { BuffManager } from './BuffManager.js'; +import { pluginPath } from '../../lib/path.js'; +import { elementEnum } from './BuffManager.js'; +import { Calculator } from './Calculator.js'; +import chokidar from 'chokidar'; +import path from 'path'; +import fs from 'fs'; +const damagePath = path.join(pluginPath, 'model', 'damage'); +export const charData = {}; +const calcFnc = { + character: {}, + weapon: {}, + set: {} }; +async function init() { + const isWatch = config.bot.log_level === 'debug'; // debug模式下监听文件变化 + await Promise.all(fs.readdirSync(path.join(damagePath, 'character')).filter(v => v !== '模板').map(v => importChar(v, isWatch))); + for (const type of ['weapon', 'set']) { + await Promise.all(fs.readdirSync(path.join(damagePath, type)).filter(v => v !== '模板.js' && !v.endsWith('_user.js') && v.endsWith('.js')) + .map(v => importFile(type, v.replace('.js', ''), isWatch))); + } +} +function watchFile(path, fnc) { + if (!fs.existsSync(path)) + return; + const watcher = chokidar.watch(path, { + awaitWriteFinish: { + stabilityThreshold: 50 + } + }); + watcher.on('change', (path) => { + logger.debug('重载' + path); + fnc(); + }); +} +async function importChar(charName, isWatch = false) { + const id = aliasToID(charName); + if (!id) + return logger.warn(`未找到角色${charName}的ID`); + const dir = path.join(damagePath, 'character', charName); + const calcFile = fs.existsSync(path.join(dir, 'calc_user.js')) ? 'calc_user.js' : 'calc.js'; + const dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json')); + try { + if (isWatch) { + watchFile(path.join(dir, calcFile), () => importChar(charName)); + watchFile(dataPath, () => charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8'))); + } + charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8')); + const m = await import(`./character/${charName}/${calcFile}?${Date.now()}`); + if (!m.calc && (!m.buffs || !m.skills)) + throw new Error('伤害计算文件格式错误'); + calcFnc.character[id] = m; + } + catch (e) { + logger.error(`导入角色${charName}伤害计算错误:`, e); + } +} +async function importFile(type, name, isWatch = false) { + const defaultFilePath = path.join(damagePath, type, `${name}.js`); + const userFilePath = path.join(damagePath, type, `${name}_user.js`); + const isUser = fs.existsSync(userFilePath); + const filePath = isUser ? userFilePath : defaultFilePath; + try { + if (isWatch) { + watchFile(filePath, () => importFile(type, name)); + } + const m = await import(`./${type}/${name}${isUser ? '_user' : ''}.js?${Date.now()}`); + if (!m.calc && !m.buffs) + throw new Error(type + ' Buff计算文件格式错误'); + calcFnc[type][name] = m; + } + catch (e) { + logger.error(`导入${type}/${name}.js错误:`, e); + } +} +await init(); +/** 角色计算 */ +export function avatar_ability(avatar) { + const m = calcFnc.character[avatar.id]; + if (!m) + return []; + const buffM = new BuffManager(avatar); + const calc = new Calculator(buffM); + logger.debug('initial_properties', avatar.initial_properties); + weapon_buff(avatar.weapon, buffM); + set_buff(avatar.equip, buffM); + if (m.buffs) + buffM.new(m.buffs); + if (m.skills) + calc.new(m.skills); + if (m.calc) + m.calc(buffM, calc, avatar); + logger.debug(`Buff*${buffM.buffs.length}:`, buffM.buffs); + return calc.calc(); +} +/** 武器加成 */ +export function weapon_buff(equipment, buffM) { + const name = equipment.name; + logger.debug('武器:' + name); + const m = calcFnc.weapon[name]; + if (!m) + return; + buffM.default({ name, source: 'Weapon' }); + if (m.buffs) + buffM.new(m.buffs); + if (m.calc) + m.calc(buffM, equipment.star); + buffM.default({}); +} +/** 套装加成 */ +export function set_buff(equip, buffM) { + buffM.default({ name: '', source: 'Set' }); + const setCount = {}; + for (const equip_detail of equip) { + if (equip_detail.equipment_type == 5) { + // 属伤加成 + const index = [31503, 31603, 31703, 31803, 31903].indexOf(equip_detail.main_properties[0].property_id); + if (index > -1 && elementEnum[index]) { + // @ts-ignore + buffM.new({ + name: '驱动盘5号位', + type: '增伤', + value: Number(equip_detail.main_properties[0].base.replace('%', '')) / 100, + isForever: true, + element: elementEnum[index] + }); + } + } + const suit_name = String(equip_detail.equip_suit.name); + setCount[suit_name] = (setCount[suit_name] || 0) + 1; + } + buffM.setCount = setCount; + for (const [name, count] of Object.entries(setCount)) { + if (count < 2) + continue; + logger.debug(`套装:${name}*${count}`); + const m = calcFnc.set[name]; + if (!m) + continue; + buffM.default('name', name); + if (m.buffs) + buffM.new(m.buffs); + if (m.calc) + m.calc(buffM, count); + } + buffM.default({}); +} diff --git a/model/damage/avatar.ts b/model/damage/avatar.ts new file mode 100644 index 0000000..7691e64 --- /dev/null +++ b/model/damage/avatar.ts @@ -0,0 +1,172 @@ +import type { skill } from './Calculator.js' +import type { ZZZAvatarInfo } from '../avatar.js' +import { aliasToID } from '../../lib/convert/char.js' +import config from '../../../../lib/config/config.js' +import { buff, BuffManager } from './BuffManager.js' +import { pluginPath } from '../../lib/path.js' +import { elementEnum } from './BuffManager.js' +import { Calculator } from './Calculator.js' +import chokidar from 'chokidar' +import path from 'path' +import fs from 'fs' + +const damagePath = path.join(pluginPath, 'model', 'damage') + +export const charData: { + [id: number]: { + skill: { [skillName: string]: number[] } + buff: { [buffName: string]: number[] } + } +} = {} + +const calcFnc: { + character: { + [id: number]: { + calc?: (buffM: BuffManager, calc: Calculator, avatar: ZZZAvatarInfo) => void + buffs: buff[] + skills: skill[] + } + } + weapon: { + [name: string]: { + calc?: (buffM: BuffManager, star: number) => void + buffs: buff[] + } + } + set: { + [name: string]: { + calc?: (buffM: BuffManager, count: number) => void + buffs: buff[] + } + } +} = { + character: {}, + weapon: {}, + set: {} +} + +async function init() { + const isWatch = config.bot.log_level === 'debug' // debug模式下监听文件变化 + await Promise.all(fs.readdirSync(path.join(damagePath, 'character')).filter(v => v !== '模板').map(v => importChar(v, isWatch))) + for (const type of ['weapon', 'set']) { + await Promise.all( + fs.readdirSync(path.join(damagePath, type)).filter(v => v !== '模板.js' && !v.endsWith('_user.js') && v.endsWith('.js')) + .map(v => importFile(type as 'weapon' | 'set', v.replace('.js', ''), isWatch)) + ) + } +} + +function watchFile(path: string, fnc: () => void) { + if (!fs.existsSync(path)) return + const watcher = chokidar.watch(path, { + awaitWriteFinish: { + stabilityThreshold: 50 + } + }) + watcher.on('change', (path) => { + logger.debug('重载' + path) + fnc() + }) +} + +async function importChar(charName: string, isWatch = false) { + const id = aliasToID(charName) + if (!id) return logger.warn(`未找到角色${charName}的ID`) + const dir = path.join(damagePath, 'character', charName) + const calcFile = fs.existsSync(path.join(dir, 'calc_user.js')) ? 'calc_user.js' : 'calc.js' + const dataPath = path.join(dir, (fs.existsSync(path.join(dir, 'data_user.json')) ? 'data_user.json' : 'data.json')) + try { + if (isWatch) { + watchFile(path.join(dir, calcFile), () => importChar(charName)) + watchFile(dataPath, () => charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8'))) + } + charData[id] = JSON.parse(fs.readFileSync(dataPath, 'utf8')) + const m = await import(`./character/${charName}/${calcFile}?${Date.now()}`) + if (!m.calc && (!m.buffs || !m.skills)) throw new Error('伤害计算文件格式错误') + calcFnc.character[id] = m + } catch (e) { + logger.error(`导入角色${charName}伤害计算错误:`, e) + } +} + +async function importFile(type: 'weapon' | 'set', name: string, isWatch = false) { + const defaultFilePath = path.join(damagePath, type, `${name}.js`) + const userFilePath = path.join(damagePath, type, `${name}_user.js`) + const isUser = fs.existsSync(userFilePath) + const filePath = isUser ? userFilePath : defaultFilePath + try { + if (isWatch) { + watchFile(filePath, () => importFile(type, name)) + } + const m = await import(`./${type}/${name}${isUser ? '_user' : ''}.js?${Date.now()}`) + if (!m.calc && !m.buffs) throw new Error(type + ' Buff计算文件格式错误') + calcFnc[type][name] = m + } catch (e) { + logger.error(`导入${type}/${name}.js错误:`, e) + } +} + +await init() + +/** 角色计算 */ +export function avatar_ability(avatar: ZZZAvatarInfo) { + const m = calcFnc.character[avatar.id] + if (!m) return [] + const buffM = new BuffManager(avatar) + const calc = new Calculator(buffM) + logger.debug('initial_properties', avatar.initial_properties) + weapon_buff(avatar.weapon, buffM) + set_buff(avatar.equip, buffM) + if (m.buffs) buffM.new(m.buffs) + if (m.skills) calc.new(m.skills) + if (m.calc) m.calc(buffM, calc, avatar) + logger.debug(`Buff*${buffM.buffs.length}:`, buffM.buffs) + return calc.calc() +} + +/** 武器加成 */ +export function weapon_buff(equipment: ZZZAvatarInfo['weapon'], buffM: BuffManager) { + const name = equipment.name + logger.debug('武器:' + name) + const m = calcFnc.weapon[name] + if (!m) return + buffM.default({ name, source: 'Weapon' }) + if (m.buffs) buffM.new(m.buffs) + if (m.calc) m.calc(buffM, equipment.star) + buffM.default({}) +} + +/** 套装加成 */ +export function set_buff(equip: ZZZAvatarInfo['equip'], buffM: BuffManager) { + buffM.default({ name: '', source: 'Set' }) + const setCount: { [name: string]: number } = {} + for (const equip_detail of equip) { + if (equip_detail.equipment_type == 5) { + // 属伤加成 + const index = [31503, 31603, 31703, 31803, 31903].indexOf(equip_detail.main_properties[0].property_id) + if (index > -1 && elementEnum[index]) { + // @ts-ignore + buffM.new({ + name: '驱动盘5号位', + type: '增伤', + value: Number(equip_detail.main_properties[0].base.replace('%', '')) / 100, + isForever: true, + element: elementEnum[index] + }) + } + } + const suit_name = String(equip_detail.equip_suit.name) + setCount[suit_name] = (setCount[suit_name] || 0) + 1 + } + buffM.setCount = setCount + for (const [name, count] of Object.entries(setCount)) { + if (count < 2) continue + logger.debug(`套装:${name}*${count}`) + const m = calcFnc.set[name] + if (!m) continue + buffM.default('name', name) + if (m.buffs) buffM.new(m.buffs) + if (m.calc) m.calc(buffM, count) + } + buffM.default({}) +} diff --git a/model/damage/character/11号/calc.js b/model/damage/character/11号/calc.js new file mode 100644 index 0000000..84dbea9 --- /dev/null +++ b/model/damage/character/11号/calc.js @@ -0,0 +1,37 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '无视抗性', + value: 0.25, + element: 'Fire', + range: ['AQ'] + }, + { + name: '2影', + type: '增伤', + value: 0.03 * 12, + range: ['AP', 'CC', 'CF'] + }, + { + name: '核心被动:热浪', + type: '增伤', + value: 'T', + range: ['AQ', 'CCQ'] + }, + { + name: '额外能力:燎原', + type: '增伤', + value: 0.1, // 暂不计入失衡0.225 + element: 'Fire' + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '普攻:火力镇压四段', type: 'AQ4' }, + { name: '闪避反击:逆火', type: 'CF' }, + { name: '强化特殊技:盛燃烈火', type: 'EQ' }, + { name: '连携技:昂扬烈焰', type: 'RL' }, + { name: '终结技:轰鸣烈焰', type: 'RZ' } +] diff --git a/model/damage/character/11号/data.json b/model/damage/character/11号/data.json new file mode 100644 index 0000000..413ca5a --- /dev/null +++ b/model/damage/character/11号/data.json @@ -0,0 +1,22 @@ +{ + "skill": { + "AQ4": [ + 3.407,3.717,4.027,4.337,4.647,4.957,5.267,5.577,5.887,6.197,6.507,6.817,7.127,7.437,7.747,8.057 + ], + "CF": [ + 2.62,2.859,3.098,3.337,3.576,3.815,4.054,4.293,4.532,4.771,5.01,5.249,5.488,5.727,5.966,6.205 + ], + "EQ": [ + 6.75,7.364,7.978,8.592,9.206,9.82,10.434,11.048,11.662,12.276,12.89,13.504,14.118,14.732,15.346,15.96 + ], + "RL": [ + 6.325,6.9,7.475,8.05,8.625,9.2,9.775,10.35,10.925,11.5,12.075,12.65,13.225,13.8,14.375,14.95 + ], + "RZ": [ + 21.03,22.942,24.854,26.766,28.678,30.59,32.502,34.414,36.326,38.238,40.15,42.062,43.974,45.886,47.798,49.71 + ] + }, + "buff": { + "T": [0.35,0.408,0.466,0.525,0.583,0.641,0.7] + } +} \ No newline at end of file diff --git a/model/damage/character/安东/calc.js b/model/damage/character/安东/calc.js new file mode 100644 index 0000000..0cca921 --- /dev/null +++ b/model/damage/character/安东/calc.js @@ -0,0 +1,35 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '增伤', + value: 0.04 * 6, + range: ['AQ', 'CFQ'] + }, + { + name: '4影', + type: '暴击率', + value: 0.1 + }, + { + name: '核心被动:兄弟齐心', + type: '增伤', + value: 'T1', + range: ['AP4', 'AQ3', 'EPP', 'EPQ', 'EQ', 'RL', 'RZ'] + }, + { + name: '核心被动:兄弟齐心', + type: '增伤', + value: 'T2', + range: ['AQ2', 'CFQ', 'LKQ', 'LT'] // 为什么支援突击一半电钻攻击一半打桩攻击???还只写了一个倍率…… + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '普攻二段(爆发)', type: 'AQ2' }, + { name: '闪避反击:过载钻击(爆发)', type: 'CFQ' }, + { name: '特殊技:爆发钻击(爆发)', type: 'EPQ' }, + { name: '连携技:转转转!', type: 'RL' }, + { name: '终结技:转转转转转!', type: 'RZ' } +] \ 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..9a86ce9 --- /dev/null +++ b/model/damage/character/安东/data.json @@ -0,0 +1,27 @@ +{ + "skill": { + "AQ2": [ + 4.692,5.119,5.546,5.973,6.4,6.827,7.254,7.681,8.108,8.535,8.962,9.389,9.816,10.243,10.67,11.097 + ], + "CFQ": [ + 4.654,5.078,5.502,5.926,6.35,6.774,7.198,7.622,8.046,8.47,8.894,9.318,9.742,10.166,10.59,11.014 + ], + "EPQ": [ + 2.314,2.525,2.736,2.947,3.158,3.369,3.58,3.791,4.002,4.213,4.424,4.635,4.846,5.057,5.268,5.479 + ], + "RL": [ + 6.407,6.99,7.573,8.156,8.739,9.322,9.905,10.488,11.071,11.654,12.237,12.82,13.403,13.986,14.569,15.152 + ], + "RZ": [ + 18.164,19.816,21.468,23.12,24.772,26.424,28.076,29.728,31.38,33.032,34.684,36.336,37.988,39.64,41.292,42.944 + ] + }, + "buff": { + "T1": [ + 0.12,0.14,0.16,0.18,0.2,0.22,0.24 + ], + "T2": [ + 0.2,0.233,0.266,0.3,0.333,0.366,0.4 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/悠真/calc.js b/model/damage/character/悠真/calc.js new file mode 100644 index 0000000..94d3165 --- /dev/null +++ b/model/damage/character/悠真/calc.js @@ -0,0 +1,42 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '无视抗性', + value: 0.15 + }, + { + name: '2影', + type: '增伤', + value: 0.5, + range: ['CCQ'] + }, + { + name: '核心被动:破晓', + type: '暴击率', + value: 'T1', + isForever: true, + range: ['CCQ'] + }, + { + name: '核心被动:破晓', + type: '暴击伤害', + value: 'T2', + range: ['CCQ'] + }, + { + name: '额外能力:超频', + type: '增伤', + value: 0.4 + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '普攻:穿云五段', type: 'AP5' }, + { name: '普攻:落羽', type: 'AX' }, + { name: '冲刺攻击:飞弦·斩', type: 'CCQ3' }, + { name: '强化特殊技:地网', type: 'EQ' }, + { name: '连携技:会·离', type: 'RL' }, + { name: '终结技:残心', type: 'RZ' } +] \ 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..e890e14 --- /dev/null +++ b/model/damage/character/悠真/data.json @@ -0,0 +1,30 @@ +{ + "skill": { + "AP5": [ + 1.329,1.45,1.571,1.692,1.813,1.934,2.055,2.176,2.297,2.418,2.539,2.66,2.781,2.902,3.023,3.144 + ], + "AX": [ + 1.054,1.15,1.246,1.342,1.438,1.534,1.63,1.726,1.822,1.918,2.014,2.11,2.206,2.302,2.398,2.494 + ], + "CCQ3": [ + 1.896,2.069,2.242,2.415,2.588,2.761,2.934,3.107,3.28,3.453,3.626,3.799,3.972,4.145,4.318,4.491 + ], + "EQ": [ + 4.493,4.902,5.311,5.72,6.129,6.538,6.947,7.356,7.765,8.174,8.583,8.992,9.401,9.81,10.219,10.628 + ], + "RL": [ + 5.176,5.647,6.118,6.589,7.06,7.531,8.002,8.473,8.944,9.415,9.886,10.357,10.828,11.299,11.77,12.241 + ], + "RZ": [ + 19.539,21.316,23.093,24.87,26.647,28.424,30.201,31.978,33.755,35.532,37.309,39.086,40.863,42.64,44.417,46.194 + ] + }, + "buff": { + "T1": [ + 0.106,0.13,0.154,0.178,0.202,0.226,0.25 + ], + "T2": [ + 0.36,0.42,0.48,0.54,0.6,0.66,0.72 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/星见雅/calc.js b/model/damage/character/星见雅/calc.js new file mode 100644 index 0000000..3f1bece --- /dev/null +++ b/model/damage/character/星见雅/calc.js @@ -0,0 +1,173 @@ +// 函数导出: + +/** + * @param {import('../../BuffManager.ts').BuffManager} buffM + * @param {import('../../Calculator.ts').Calculator} calc + * @param {import('../../../avatar.js').ZZZAvatarInfo} avatar + */ +// export function calc(buffM, calc, avatar) { +// /** 注册buff */ +// // 影画加成 +// buffM.new({ +// name: '6影', +// type: '增伤', +// isForever: true, +// value: 0.30, +// range: ['AX'] +// }) +// buffM.new({ +// name: '4影', +// type: '增伤', +// isForever: true, +// value: 0.30, +// range: ['TP'] +// }) +// buffM.new({ +// name: '2影', +// type: '增伤', +// isForever: true, +// value: 0.30, +// range: ['AP', 'CF'] +// }) +// buffM.new({ +// name: '2影', +// type: '暴击率', +// isForever: true, +// value: 0.15 +// }) +// buffM.new({ +// name: '1影', +// type: '无视防御', +// value: 0.36, +// range: ['AX'] +// }) +// // 额外能力加成 +// buffM.new({ +// name: '额外能力:同沐霜雪', +// type: '增伤', +// isForever: true, +// value: 0.6, +// range: ['AX'] +// }) +// buffM.new({ +// name: '额外能力:同沐霜雪', +// type: '无视抗性', +// isForever: true, +// value: 0.3, +// range: ['AX'] +// }) +// // 技能加成 +// buffM.new({ +// name: '终结技', +// type: '增伤', +// element: 'Ice', +// range: ['RZ'], +// value: 0.3 +// }) + +// /** 注册技能 */ +// calc.new({ name: '碎冰', type: '碎冰' }) +// calc.new({ name: '紊乱', type: '紊乱' }) +// calc.new({ name: '普通攻击:风花五段', type: 'AP5' }) +// calc.new({ name: '闪避反击:寒雀', type: 'CF' }) +// calc.new({ name: '霜灼·破', type: 'TP' }) +// calc.new({ name: '蓄力攻击:一段蓄', type: 'AX1' }) +// calc.new({ +// name: '蓄力攻击:二段蓄', +// type: 'AX2', +// after: ({ avatar, damage }) => avatar.rank >= 6 && damage.add('AX1') +// }) +// calc.new({ +// name: '蓄力攻击:三段蓄', +// type: 'AX3', +// after: ({ avatar, damage }) => avatar.rank >= 6 && damage.add('AX2') +// }) +// calc.new({ name: '强化特殊技:飞雪', type: 'EQ' }) +// calc.new({ name: '强化特殊技:飞雪(二段)', type: 'EQ2' }) +// calc.new({ name: '连携技:春临', type: 'RL' }) +// calc.new({ name: '终结技:名残雪', type: 'RZ' }) +// } + +// 直接导出: + +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '增伤', + isForever: true, + value: 0.30, + range: ['AX'] + }, + { + name: '4影', + type: '增伤', + isForever: true, + value: 0.30, + range: ['TP'] + }, + { + name: '2影', + type: '暴击率', + isForever: true, + value: 0.15 + }, + { + name: '2影', + type: '增伤', + isForever: true, + value: 0.30, + range: ['AP', 'CF'] + }, + { + name: '1影', + type: '无视防御', + value: 0.36, + range: ['AX'] + }, + { + name: '额外能力:同沐霜雪', + type: '增伤', + isForever: true, + value: 0.6, + range: ['AX'] + }, + { + name: '额外能力:同沐霜雪', + type: '无视抗性', + isForever: true, + value: 0.3, + range: ['AX'] + }, + { + name: '终结技', + type: '增伤', + element: 'Ice', + range: ['RZ'], + value: 0.3 + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '碎冰', type: '碎冰' }, + { name: '紊乱', type: '紊乱' }, + { name: '普攻:风花五段', type: 'AP5' }, + { name: '闪避反击:寒雀', type: 'CF' }, + { name: '霜灼·破', type: 'TP' }, + { name: '蓄力攻击:一段蓄', type: 'AX1' }, + { + name: '蓄力攻击:二段蓄', + type: 'AX2', + after: ({ avatar, damage }) => avatar.rank >= 6 && damage.add('AX1') + }, + { + name: '蓄力攻击:三段蓄', + type: 'AX3', + after: ({ avatar, damage }) => avatar.rank >= 6 && damage.add('AX2') + }, + { name: '强化特殊技:飞雪', type: 'EQ' }, + { name: '强化特殊技:飞雪(二段)', type: 'EQ2' }, + { name: '连携技:春临', type: 'RL' }, + { name: '终结技:名残雪', type: 'RZ' } +] diff --git a/model/damage/character/星见雅/data.json b/model/damage/character/星见雅/data.json new file mode 100644 index 0000000..8e4cb39 --- /dev/null +++ b/model/damage/character/星见雅/data.json @@ -0,0 +1,34 @@ +{ + "skill": { + "AP5": [ + 1.29,1.408,1.526,1.644,1.762,1.88,1.998,2.116,2.234,2.352,2.47,2.588,2.706,2.824,2.942,3.06 + ], + "CF": [ + 2.459,2.683,2.907,3.131,3.355,3.579,3.803,4.027,4.251,4.475,4.699,4.923,5.147,5.371,5.595,5.819 + ], + "AX1": [ + 4.547,4.961,5.375,5.789,6.203,6.617,7.031,7.445,7.859,8.273,8.687,9.101,9.515,9.929,10.343,10.757 + ], + "AX2": [ + 8.581,9.362,10.143,10.924,11.705,12.486,13.267,14.048,14.829,15.61,16.391,17.172,17.953,18.734,19.515,20.296 + ], + "AX3": [ + 21.411,23.358,25.305,27.252,29.199,31.146,33.093,35.04,36.987,38.934,40.881,42.828,44.775,46.722,48.669,50.616 + ], + "EQ": [ + 3.934,4.293,4.652,5.011,5.37,5.729,6.088,6.447,6.806,7.165,7.524,7.883,8.242,8.601,8.96,9.319 + ], + "EQ2": [ + 4.83,5.272,5.712,6.152,6.592,7.032,7.472,7.912,8.352,8.792,9.232,9.672,10.112,10.552,10.992,11.432 + ], + "RL": [ + 6.28,6.853,7.426,7.999,8.572,9.145,9.718,10.291,10.864,11.437,12.01,12.583,13.156,13.729,14.302,14.875 + ], + "RZ": [ + 23.88,26.051,28.222,30.393,32.564,34.735,36.906,39.077,41.248,43.419,45.59,47.761,49.932,52.103,54.274,56.445 + ], + "TP": [ + 7.5,8.75,10,11.25,12.5,13.75,15 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/朱鸢/calc.js b/model/damage/character/朱鸢/calc.js new file mode 100644 index 0000000..f0df2f5 --- /dev/null +++ b/model/damage/character/朱鸢/calc.js @@ -0,0 +1,50 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '4影', + type: '无视抗性', + value: 0.25, + range: ['AQ', 'CCQ'] + }, + { + name: '2影', + type: '增伤', + value: 0.1 * 5, + element: 'Ether', + range: ['AQ', 'CCQ'] + }, + { + name: '核心被动:特种弹药', + type: '增伤', + value: 'T', + range: ['AQ', 'CCQ'] + }, + { + name: '额外能力:武装协同', + type: '暴击率', + value: 0.3 + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '普攻三段(以太)', type: 'AQY3' }, + { name: '冲刺攻击:火力压制', type: 'CCQ' }, + { + name: '强化特殊技:全弹连射', + type: 'EQ', + after: ({ damage, calc }) => { + if (calc.avatar.rank >= 6) { + const EQ2 = calc.calc_skill({ + name: '6影以太鹿弹', + type: 'EQ2', + fixedMultiplier: 2.2 * 4, + element: 'Ether' + }) + damage.add(EQ2) + } + } + }, + { name: '连携技:歼灭模式', type: 'RL' }, + { name: '终结技:歼灭模式MAX', type: 'RZ' } +] \ 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..c6d4b15 --- /dev/null +++ b/model/damage/character/朱鸢/data.json @@ -0,0 +1,24 @@ +{ + "skill": { + "AQY3": [ + 4.077,4.448,4.819,5.19,5.561,5.932,6.303,6.674,7.045,7.416,7.787,8.158,8.529,8.9,9.271,9.642 + ], + "CCQ": [ + 1.359,1.483,1.607,1.731,1.855,1.979,2.103,2.227,2.351,2.475,2.599,2.723,2.847,2.971,3.095,3.219 + ], + "EQ": [ + 5.874,6.408,6.942,7.476,8.01,8.544,9.078,9.612,10.146,10.68,11.214,11.748,12.282,12.816,13.35,13.88 + ], + "RL": [ + 5.875,6.41,6.945,7.48,8.015,8.55,9.085,9.62,10.155,10.69,11.225,11.76,12.295,12.83,13.35,13.9 + ], + "RZ": [ + 19.776,21.574,23.372,25.17,26.968,28.766,30.564,32.362,34.16,35.958,37.756,39.554,41.352,43.15,44.948,46.746 + ] + }, + "buff": { + "T": [ + 0.4,0.466,0.532,0.6,0.666,0.732,0.8 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/模板/calc.js b/model/damage/character/模板/calc.js new file mode 100644 index 0000000..b4b4ea1 --- /dev/null +++ b/model/damage/character/模板/calc.js @@ -0,0 +1,123 @@ +/** + * @param {import('../../BuffManager.ts').BuffManager} buffM + * @param {import('../../Calculator.ts').Calculator} calc + * @param {import('../../../avatar.js').ZZZAvatarInfo} avatar + */ +export function calc(buffM, calc, avatar) { + /** 注册buff */ + // 影画加成 + buffM.new({ + name: '6影', + type: , + value: 0, + range: [''] + }) + buffM.new({ + name: '4影', + type: , + value: 0, + range: [''] + }) + buffM.new({ + name: '2影', + type: , + value: 0, + range: [''] + }) + buffM.new({ + name: '1影', + type: , + value: 0, + range: [''] + }) + // 核心被动加成 + buffM.new({ + name: '核心被动:', + type: , + value: 0, + element: , + range: [''] + }) + // 额外能力加成 + buffM.new({ + name: '额外能力:', + type: , + value: 0, + element: , + range: [''] + }) + // 技能加成 + buffM.new({ + name: '技能:', + type: , + value: 0, + element: , + range: [''] + }) + /** 注册技能 */ + calc.new({ name: '普通攻击:', type: '' }) + calc.new({ name: '强化特殊技:', type: '' }) + calc.new({ name: '连携技:', type: '' }) + calc.new({ name: '终结技:', type: '' }) + calc.new({ name: '', type: '' }) +} + +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: , + value: 0, + range: [''] + }, + { + name: '4影', + type: , + value: 0, + range: [''] + }, + { + name: '2影', + type: , + value: 0, + range: [''] + }, + { + name: '1影', + type: , + value: 0, + range: [''] + }, + { + name: '核心被动:', + type: , + value: 0, + element: , + range: [''] + }, + { + name: '额外能力:', + type: , + value: 0, + element: , + range: [''] + }, + { + name: '技能:', + type: , + value: 0, + element: , + range: [''] + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '普攻:', type: 'AP' }, + { name: '冲刺攻击:', type: 'CC' }, + { name: '闪避反击:', type: 'CF' }, + { name: '强化特殊技:', type: 'EQ' }, + { name: '连携技:', type: 'RL' }, + { name: '终结技:', type: 'RZ' }, + { name: '', type: '' }, +] \ 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..dbe5020 --- /dev/null +++ b/model/damage/character/模板/data.json @@ -0,0 +1,27 @@ +{ + "skill": { + "AP": [ + + ], + "CC": [ + + ], + "CF": [ + + ], + "EQ": [ + + ], + "RL": [ + + ], + "RZ": [ + + ] + }, + "buff": { + "T": [ + + ] + } +} \ No newline at end of file diff --git a/model/damage/character/猫又/calc.js b/model/damage/character/猫又/calc.js new file mode 100644 index 0000000..72af1d4 --- /dev/null +++ b/model/damage/character/猫又/calc.js @@ -0,0 +1,40 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '暴击伤害', + value: 0.18 * 3 + }, + { + name: '4影', + type: '暴击率', + value: 0.07 * 2 + }, + { + name: '1影', + type: '无视抗性', + value: 0.16, + element: 'Physical' + }, + { + name: '核心被动:猫步诡影', + type: '增伤', + value: 'T' + }, + { + name: '额外能力:猫步秀', + type: '增伤', + value: 0.35 * 2, + range: ['EQ'] + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '强击', type: '强击' }, + { name: '普攻:猫猫爪刺四段', type: 'AP4' }, + { name: '闪避反击:虚影双刺', type: 'CF' }, + { name: '强化特殊技:超~凶奇袭!', type: 'EQ' }, + { name: '连携技:刃爪挥击', type: 'RL' }, + { name: '终结技:刃爪强袭', type: 'RZ' } +] diff --git a/model/damage/character/猫又/data.json b/model/damage/character/猫又/data.json new file mode 100644 index 0000000..ecf2ff9 --- /dev/null +++ b/model/damage/character/猫又/data.json @@ -0,0 +1,24 @@ +{ + "skill": { + "AP4": [ + 1.702,1.857,2.012,2.167,2.322,2.477,2.632,2.787,2.942,3.097,3.252,3.407,3.562,3.717,3.872,4.027 + ], + "CF": [ + 2.279,2.487,2.695,2.903,3.111,3.319,3.527,3.735,3.943,4.151,4.359,4.567,4.775,4.983,5.191,5.399 + ], + "EQ": [ + 5.397,5.888,6.379,6.87,7.361,7.852,8.343,8.834,9.325,9.816,10.307,10.798,11.289,11.78,12.271,12.762 + ], + "RL": [ + 5.362,5.85,6.338,6.826,7.314,7.802,8.29,8.778,9.266,9.754,10.242,10.73,11.218,11.706,12.194,12.682 + ], + "RZ": [ + 15.711,17.14,18.569,19.998,21.427,22.856,24.285,25.714,27.143,28.572,30.001,31.43,32.859,34.288,35.717,37.146 + ] + }, + "buff": { + "T": [ + 0.3,0.35,0.4,0.45,0.5,0.55,0.6 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/艾莲/calc.js b/model/damage/character/艾莲/calc.js new file mode 100644 index 0000000..33883a3 --- /dev/null +++ b/model/damage/character/艾莲/calc.js @@ -0,0 +1,64 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '穿透率', + value: 0.2 + }, + { + name: '6影', + type: '增伤', + value: 2.5, + range: ['CCXX'] + }, + { + name: '2影', + type: '暴击伤害', + value: 0.6, + range: ['EQ'] + }, + { + name: '1影', + type: '暴击率', + value: 0.2 * 6 + }, + { + name: '核心被动:凌牙厉齿', + type: '暴击伤害', + value: 'T', + range: ['CCXX', 'AQ'] + }, + { + name: '额外能力:风暴潮', + type: '增伤', + value: 0.03 * 10, + element: 'Ice' + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '碎冰', type: '碎冰' }, + { name: '普攻:急冻修剪法三段', type: 'AQ3' }, + { name: '闪避反击:暗礁', type: 'CF' }, + { name: '冲刺攻击:寒潮', type: 'CCP' }, + { + name: '冲刺攻击:冰渊潜袭回旋斩击', + isHide: true, + type: 'CCX0' + }, + { + name: '冲刺攻击:冰渊潜袭点按', + type: 'CCXP', + after: ({ damage }) => damage.add('CCX0') + }, + { + name: '冲刺攻击:冰渊潜袭蓄力', + type: 'CCXX', + after: ({ damage }) => damage.add('CCX0') + }, + { name: '强化特殊技:横扫', type: 'EQ1' }, + { name: '强化特殊技:鲨卷风', type: 'EQ2' }, + { name: '连携技:雪崩', type: 'RL' }, + { name: '终结技:永冬狂宴', type: 'RZ' } +] \ 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..327161a --- /dev/null +++ b/model/damage/character/艾莲/data.json @@ -0,0 +1,39 @@ +{ + "skill": { + "AQ3": [ + 4.962,5.414,5.866,6.318,6.77,7.22,7.674,8.126,8.578,9.03,9.482,9.934,10.386,10.838,11.29,11.742 + ], + "CF": [ + 1.526,1.665,1.804,1.943,2.082,2.221,2.36,2.499,2.638,2.777,2.916,3.055,3.194,3.333,3.472,3.611 + ], + "CCP": [ + 1.457,1.59,1.723,1.856,1.989,2.122,2.255,2.388,2.521,2.654,2.787,2.92,3.053,3.186,3.319,3.452 + ], + "CCX0": [ + 0.623,0.68,0.737,0.794,0.851,0.908,0.965,1.022,1.079,1.136,1.193,1.25,1.307,1.364,1.421,1.478 + ], + "CCXP": [ + 1.276,1.392,1.508,1.624,1.74,1.856,1.972,2.088,2.204,2.32,2.436,2.552,2.668,2.784,2.9,3.016 + ], + "CCXX": [ + 1.582,1.726,1.87,2.014,2.158,2.302,2.446,2.59,2.734,2.878,3.022,3.166,3.31,3.454,3.598,3.742 + ], + "EQ1": [ + 3.772,4.115,4.458,4.801,5.144,5.487,5.83,6.173,6.516,6.859,7.202,7.545,7.888,8.231,8.574,8.917 + ], + "EQ2": [ + 5.533,6.036,6.539,7.042,7.545,8.048,8.551,9.054,9.557,10.06,10.56,11.066,11.569,12.072,12.575,13.078 + ], + "RL": [ + 7.946,8.669,9.392,10.115,10.838,11.561,12.284,13.007,13.73,14.453,15.176,15.899,16.622,17.345,18.068,18.791 + ], + "RZ": [ + 18.908,20.627,22.346,24.065,25.784,27.503,29.222,30.941,32.66,34.379,36.098,37.817,39.536,41.255,42.974,44.693 + ] + }, + "buff": { + "T": [ + 0.5,0.583,0.666,0.75,0.833,0.916,1 + ] + } +} \ No newline at end of file diff --git a/model/damage/character/青衣/calc.js b/model/damage/character/青衣/calc.js new file mode 100644 index 0000000..e993787 --- /dev/null +++ b/model/damage/character/青衣/calc.js @@ -0,0 +1,53 @@ +/** @type {import('../../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '6影', + type: '暴击伤害', + value: 1, + range: ['AQ'], + isForever: true + }, + { + name: '6影', + type: '无视抗性', + value: 0.2 + }, + { + name: '1影', + type: '无视防御', + value: 0.15 + }, + { + name: '1影', + type: '暴击率', + value: 0.2 + }, + { + name: '额外能力:阳关三叠', + type: '攻击力', + isForever: true, + value: ({ calc }) => Math.max(0, Math.min((calc.get_Impact() - 120) * 6, 600)) + }, + { + name: '连携技:太平令', + type: '增伤', + value: 0.03 * 20, + range: ['RL'] + }, + { + name: '闪络', + source: 'Skill', + type: '增伤', + value: 0.01 * 25, + range: ['AQ'] + } +] + +/** @type {import('../../Calculator.ts').Calculator['skills']} */ +export const skills = [ + { name: '普攻:醉花月云转', type: 'AQ' }, + { name: '闪避反击:意不尽', type: 'CF' }, + { name: '强化特殊技:月上海棠', type: 'EQ' }, + { name: '连携技:太平令', type: 'RL' }, + { name: '终结技:八声甘州', type: 'RZ' } +] \ 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..ed2614d --- /dev/null +++ b/model/damage/character/青衣/data.json @@ -0,0 +1,19 @@ +{ + "skill": { + "AQ": [ + 4.487,4.895,5.303,5.711,6.119,6.527,6.935,7.343,7.751,8.159,8.567,8.975,9.383,9.791,10.199,10.607 + ], + "CF": [ + 2.84,3.099,3.358,3.617,3.876,4.135,4.394,4.653,4.912,5.171,5.43,5.689,5.948,6.207,6.466,6.725 + ], + "EQ": [ + 6.028,6.577,7.126,7.675,8.224,8.773,9.322,9.871,10.42,10.969,11.518,12.067,12.616,13.165,13.714,14.263 + ], + "RL": [ + 6.479,7.068,7.657,8.246,8.835,9.424,10.013,10.602,11.191,11.78,12.369,12.958,13.547,14.136,14.725,15.314 + ], + "RZ": [ + 16.707,18.226,19.745,21.264,22.783,24.302,25.821,27.34,28.859,30.378,31.897,33.416,34.935,36.454,37.973,39.492 + ] + } +} \ No newline at end of file diff --git a/model/damage/relice.js b/model/damage/relice.js deleted file mode 100644 index 36bfbda..0000000 --- a/model/damage/relice.js +++ /dev/null @@ -1,119 +0,0 @@ -import _ from 'lodash'; -import { ZZZAvatarInfo } from '../avatar.js'; - -/** - * - * @param {string} set_id 套装id - * @param {number} set_num 套装数量 - * @param {ZZZAvatarInfo['damage_basic_properties']['base_detail']} base_detail 基础属性 - * @param {ZZZAvatarInfo['damage_basic_properties']['bonus_detail']} bonus_detail 套装加成 - * @returns {ZZZAvatarInfo['damage_basic_properties']['bonus_detail']} 套装加成 - */ -export const relice_ability = (set_id, set_num, base_detail, bonus_detail) => { - switch (set_id) { - case '32700': - if (set_num >= 4) { - let CriticalDamageBase = _.get(bonus_detail, 'CriticalDamageBase', 0); - bonus_detail['CriticalDamageBase'] = CriticalDamageBase + 0.3; - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.12; - logger.debug('32700,4,CriticalDamageBase'); - logger.debug('32700,4,CriticalChanceBase'); - } - break; - case '31100': - if (set_num >= 4) { - let R_DmgAdd = _.get(bonus_detail, 'R_DmgAdd', 0); - bonus_detail['R_DmgAdd'] = R_DmgAdd + 0.2; - let AttackAddedRatio = _.get(bonus_detail, 'AttackAddedRatio', 0); - bonus_detail['AttackAddedRatio'] = AttackAddedRatio + 0.15; - logger.debug('relicGetter,4,R_DmgAdd'); - } - break; - case '32500': - if (set_num >= 2) { - let IceDmgAdd = _.get(bonus_detail, 'Ice_DmgAdd', 0); - bonus_detail['Ice_DmgAdd'] = IceDmgAdd + 0.1; - logger.debug('32500,2,Ice_DmgAdd'); - } - if (set_num >= 4) { - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + 0.4; - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + 0.4; - logger.debug('32500,4,A_DmgAdd'); - logger.debug('32500,4,C_DmgAdd'); - } - break; - case '32600': - if (set_num >= 2) { - let PhysicalDmgAdd = _.get(bonus_detail, 'Physical_DmgAdd', 0); - bonus_detail['Physical_DmgAdd'] = PhysicalDmgAdd + 0.1; - logger.debug('32600,2,Physical_DmgAdd'); - } - if (set_num >= 4) { - let AllDmgAdd = _.get(bonus_detail, 'All_DmgAdd', 0); - bonus_detail['All_DmgAdd'] = AllDmgAdd + 0.35; - logger.debug('32600,4,All_DmgAdd'); - } - break; - case '32400': - if (set_num >= 2) { - let Electric_DmgAdd = _.get(bonus_detail, 'Electric_DmgAdd', 0); - bonus_detail['Electric_DmgAdd'] = Electric_DmgAdd + 0.1; - logger.debug('32400,2,Electric_DmgAdd'); - } - if (set_num >= 4) { - let AttackAddedRatio = _.get(bonus_detail, 'AttackAddedRatio', 0); - bonus_detail['AttackAddedRatio'] = AttackAddedRatio + 0.28; - logger.debug('32400,4,AttackAddedRatio'); - } - break; - case '32200': - if (set_num >= 2) { - let Fire_DmgAdd = _.get(bonus_detail, 'Fire_DmgAdd', 0); - bonus_detail['Fire_DmgAdd'] = Fire_DmgAdd + 0.1; - logger.debug('32200,4,Fire_DmgAdd'); - } - if (set_num >= 4) { - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase + 0.28; - logger.debug('32200,4,CriticalChanceBase'); - } - break; - case '32300': - if (set_num >= 2) { - let Ether_DmgAdd = _.get(bonus_detail, 'Ether_DmgAdd', 0); - bonus_detail['Ether_DmgAdd'] = Ether_DmgAdd + 0.1; - logger.debug('32300,4,Ether_DmgAdd'); - } - if (set_num >= 4) { - let CriticalDamageBase = _.get(bonus_detail, 'CriticalDamageBase', 0); - bonus_detail['CriticalDamageBase'] = CriticalDamageBase + 0.53; - logger.debug('32300,4,CriticalDamageBase'); - } - break; - case '31600': - if (set_num >= 4) { - let All_DmgAdd = _.get(bonus_detail, 'All_DmgAdd', 0); - bonus_detail['All_DmgAdd'] = All_DmgAdd + 0.53; - logger.debug('31600,4,All_DmgAdd'); - } - break; - case '31400': - if (set_num >= 4) { - let AttackAddedRatio = _.get(bonus_detail, 'AttackAddedRatio', 0); - bonus_detail['AttackAddedRatio'] = AttackAddedRatio + 0.25; - logger.debug('31400,4,AttackAddedRatio'); - } - break; - case '31000': - if (set_num >= 4) { - let AttackAddedRatio = _.get(bonus_detail, 'AttackAddedRatio', 0); - bonus_detail['AttackAddedRatio'] = AttackAddedRatio + 0.27; - logger.debug('31400,4,AttackAddedRatio'); - } - break; - } - return bonus_detail; -}; diff --git a/model/damage/role.js b/model/damage/role.js deleted file mode 100644 index ace85b9..0000000 --- a/model/damage/role.js +++ /dev/null @@ -1,304 +0,0 @@ -import _ from 'lodash'; - -export const calculate_damage = ( - base_detail, - bonus_detail, - skill_type, - add_skill_type, - avatar_element, - skill_multiplier, - level -) => { - const merged_attr = merge_attribute(base_detail, bonus_detail); - logger.debug('merged_attr', merged_attr); - - logger.debug(logger.green(skill_type + `(${add_skill_type})伤害乘区计算:`)); - logger.debug('倍率', skill_multiplier); - - const attack = merged_attr.attack; - logger.debug('攻击力', attack); - - const critical_chance_base = get_critical_chance_base( - merged_attr, - skill_type, - add_skill_type, - avatar_element - ); - logger.debug('暴击率', critical_chance_base); - - const critical_damage_base = get_critical_damage_base( - merged_attr, - skill_type, - add_skill_type, - avatar_element - ); - logger.debug('暴击伤害', critical_damage_base); - - const qiwang_damage = critical_chance_base * (critical_damage_base - 1) + 1; - logger.debug('暴击期望', qiwang_damage); - - const injury_area = get_injury_area( - merged_attr, - skill_type, - add_skill_type, - avatar_element - ); - logger.debug('增伤区', injury_area); - - const damage_ratio = get_damage_ratio( - merged_attr, - skill_type, - add_skill_type, - avatar_element - ); - logger.debug('易伤区', damage_ratio); - - const resistance_area = get_resistance_area( - merged_attr, - skill_type, - add_skill_type, - avatar_element - ); - logger.debug('抗性区', resistance_area); - - const defence_multiplier = get_defence_multiplier( - merged_attr, - level, - skill_type, - add_skill_type, - ); - logger.debug('防御区', defence_multiplier); - - const damage_cd = - attack * - skill_multiplier * - defence_multiplier * - resistance_area * - injury_area * - damage_ratio * - critical_damage_base * - 1.0; // 失衡易伤不计 - const damage_qw = - attack * - skill_multiplier * - defence_multiplier * - resistance_area * - injury_area * - damage_ratio * - qiwang_damage * - 1.0; - - const damagelist = { - cd: damage_cd, - qw: damage_qw, - }; - logger.debug('最终伤害', damagelist); - return damagelist; -}; - -export const get_critical_chance_base = ( - merged_attr, - skill_type, - add_skill_type, - avatar_element -) => { - let critical_chance_base = _.get(merged_attr, 'CriticalChanceBase', 0); - const merged_attrkey = Object.keys(merged_attr); - for (const attr of merged_attrkey) { - if (attr.search('_CriticalChanceBase') != -1) { - const attr_name = attr.split('_CriticalChanceBase')[0]; - if ( - [skill_type, add_skill_type, 'All', avatar_element].includes(attr_name) - ) { - logger.debug( - attr + '对' + attr_name + '有' + merged_attr[attr] + '暴击加成' - ); - critical_chance_base = critical_chance_base + merged_attr[attr]; - } - } - } - critical_chance_base = Math.min(1, critical_chance_base); - return critical_chance_base; -}; - -export const get_critical_damage_base = ( - merged_attr, - skill_type, - add_skill_type, - avatar_element -) => { - let critical_damage_base = _.get(merged_attr, 'CriticalDamageBase', 0); - const merged_attrkey = Object.keys(merged_attr); - for (const attr of merged_attrkey) { - if (attr.search('_CriticalDamageBase') != -1) { - const attr_name = attr.split('_CriticalDamageBase')[0]; - if ( - [skill_type, add_skill_type, 'All', avatar_element].includes(attr_name) - ) { - logger.debug( - attr + '对' + attr_name + '有' + merged_attr[attr] + '爆伤加成' - ); - critical_damage_base = critical_damage_base + merged_attr[attr]; - } - } - } - return critical_damage_base + 1; -}; - -export const get_damage_ratio = ( - merged_attr, - skill_type, - add_skill_type, - avatar_element -) => { - let damage_ratio = _.get(merged_attr, 'DmgRatio', 0); - const merged_attrkey = Object.keys(merged_attr); - for (const attr of merged_attrkey) { - if (attr.search('_DmgRatio') != -1) { - const attr_name = attr.split('_DmgRatio')[0]; - if ( - [skill_type, add_skill_type, 'All', avatar_element].includes(attr_name) - ) { - logger.debug( - attr + '对' + attr_name + '有' + merged_attr[attr] + '易伤加成' - ); - damage_ratio = damage_ratio + merged_attr[attr]; - } - } - } - return damage_ratio + 1; -}; - -export const get_resistance_area = ( - merged_attr, - skill_type, - add_skill_type, - avatar_element -) => { - let resistance_area = 1.2 - for (const attr in merged_attr) { - if (attr.search('_ResistancePenetration') != -1) { - const attr_name = attr.split('_ResistancePenetration')[0] - if ( - [skill_type, add_skill_type, 'All', avatar_element].includes(attr_name) - ) { - logger.debug(attr + '对' + attr_name + '有' + merged_attr[attr] + '减抗') - resistance_area += merged_attr[attr] - } - } - } - return resistance_area -} - -export const get_injury_area = ( - merged_attr, - skill_type, - add_skill_type, - avatar_element -) => { - let injury_area = 1.0; - const merged_attrkey = Object.keys(merged_attr); - for (const attr of merged_attrkey) { - if (attr.search('_DmgAdd') != -1) { - const attr_name = attr.split('_DmgAdd')[0]; - if ( - [skill_type, add_skill_type, 'All', avatar_element].includes(attr_name) - ) { - logger.debug( - attr + '对' + attr_name + '有' + merged_attr[attr] + '增伤' - ); - injury_area = injury_area + merged_attr[attr]; - } - } - if (attr.search('AddedRatio') != -1) { - const attr_name = attr.split('AddedRatio')[0]; - if ([avatar_element, 'AllDamage'].includes(attr_name)) { - logger.debug( - attr + '对' + attr_name + '有' + merged_attr[attr] + '增伤' - ); - injury_area = injury_area + merged_attr[attr]; - } - } - } - return injury_area; -}; - -export const get_defence_multiplier = ( - merged_attr, - level, - skill_type, - add_skill_type -) => { - /** 计算防御基础值 */ - const defadd = 0.155 * (level * level) + 3.12 * level + 46.95; - /** 计算降防 */ - let ignore_defence = 1.0; - const merged_attrkey = Object.keys(merged_attr); - for (const attr of merged_attrkey) { - if (attr.search('_IgnoreDefence') != -1) { - const attr_name = attr.split('_IgnoreDefence')[0]; - if ( - [skill_type, add_skill_type, 'All'].includes(attr_name) - ) { - logger.debug( - attr + '对' + attr_name + '有' + merged_attr[attr] + '无视防御' - ); - ignore_defence -= merged_attr[attr]; - } - } - } - if (merged_attr.ignore_defence) { - ignore_defence -= merged_attr.ignore_defence; - } - /** 计算穿透率 */ - let penratio = 1.0; - if (merged_attr.PenRatioBase) { - penratio = 1 - merged_attr.PenRatioBase; - } - /** 计算穿透值 */ - const pendelta = _.get(merged_attr, 'PenDelta', 0); - /** 计算防御乘区 */ - const defence_multiplier = - defadd / (defadd + (defadd * ignore_defence * penratio - pendelta)); - return defence_multiplier; -}; - -export const merge_attribute = (base_detail, bonus_detail) => { - const merged_attr = {}; - const bonus_detailkey = Object.keys(bonus_detail); - for (const merged of bonus_detailkey) { - if ( - merged.search('Attack') != -1 || - (merged.search('Defence') != -1 && merged.search('IgnoreDefence') === -1) || - merged.search('HP') != -1 - ) { - continue; - } else if (merged.search('Base') != -1) { - merged_attr[merged] = - _.get(base_detail, merged, 0) + _.get(bonus_detail, merged, 0); - } else { - merged_attr[merged] = _.get(bonus_detail, merged, 0); - } - } - merged_attr['hp'] = - _.get(bonus_detail, 'HPDelta', 0) + - _.get(base_detail, 'hp', 0) * (_.get(bonus_detail, 'HPAddedRatio', 0) + 1); - merged_attr['attack'] = - _.get(bonus_detail, 'AttackDelta', 0) + - _.get(base_detail, 'attack', 0) * - (_.get(bonus_detail, 'AttackAddedRatio', 0) + 1); - merged_attr['defence'] = - _.get(bonus_detail, 'DefenceDelta', 0) + - _.get(base_detail, 'defence', 0) * - (_.get(bonus_detail, 'DefenceAddedRatio', 0) + 1); - merged_attr['CriticalChanceBase'] = - _.get(bonus_detail, 'CriticalChanceBase', 0) + - _.get(base_detail, 'CriticalChanceBase', 0); - merged_attr['CriticalDamageBase'] = - _.get(bonus_detail, 'CriticalDamageBase', 0) + - _.get(base_detail, 'CriticalDamageBase', 0); - merged_attr['PenRatioBase'] = - _.get(bonus_detail, 'PenRatioBase', 0) + - _.get(base_detail, 'PenRatioBase', 0); - return merged_attr; -}; diff --git a/model/damage/set/原始朋克.js b/model/damage/set/原始朋克.js new file mode 100644 index 0000000..fbe650c --- /dev/null +++ b/model/damage/set/原始朋克.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.15, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/啄木鸟电音.js b/model/damage/set/啄木鸟电音.js new file mode 100644 index 0000000..8ba654c --- /dev/null +++ b/model/damage/set/啄木鸟电音.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: 0.09 * 3, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/折枝剑歌.js b/model/damage/set/折枝剑歌.js new file mode 100644 index 0000000..4fad58b --- /dev/null +++ b/model/damage/set/折枝剑歌.js @@ -0,0 +1,42 @@ +// 函数导出: + +/** + * @param {import('../BuffManager.ts').BuffManager} buffM + * @param {number} count 套装数量 + */ +// export function calc(buffM, count) { +// const name = buffM.defaultBuff.name +// switch (true) { +// case (count >= 4): +// buffM.new({ +// name: name + '4', +// type: '暴击伤害', +// value: 0.3, +// isForever: true, +// check: ({ buffM, calc }) => calc.get_AnomalyMastery() >= 115 +// }) +// buffM.new({ +// name: name + '4', +// type: '暴击率', +// value: 0.12 +// }) +// } +// } + +// 直接导出: + +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + name: '折枝剑歌4', + type: '暴击伤害', + value: 0.3, + isForever: true, + check: ({ buffM, calc }) => buffM.setCount.折枝剑歌 >= 4 && calc.get_AnomalyMastery() >= 115 + }, + { + type: '暴击率', + value: 0.12, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/摇摆爵士.js b/model/damage/set/摇摆爵士.js new file mode 100644 index 0000000..fbe650c --- /dev/null +++ b/model/damage/set/摇摆爵士.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.15, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/极地重金属.js b/model/damage/set/极地重金属.js new file mode 100644 index 0000000..b4a8436 --- /dev/null +++ b/model/damage/set/极地重金属.js @@ -0,0 +1,23 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.1, + isForever: true, + element: 'Ice', + check: 2 + }, + { + type: '增伤', + value: 0.2, + isForever: true, + range: ['A', 'CC'], + check: 4 + }, + { + type: '增伤', + value: 0.2, + range: ['A', 'CC'], + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/模板.js b/model/damage/set/模板.js new file mode 100644 index 0000000..819f7ac --- /dev/null +++ b/model/damage/set/模板.js @@ -0,0 +1,43 @@ +/** + * @param {import('../BuffManager.ts').BuffManager} buffM + * @param {number} count 套装数量 + */ +export function calc(buffM, count) { + const name = buffM.defaultBuff.name + switch (true) { + case (count >= 4): + buffM.new({ + name: name + '4', + type: , + value: 0, + element: , + range: [''] + }) + case (count >= 2): + buffM.new({ + name: name + '2', + type: , + value: 0, + element: , + range: [''] + }) + } +} + +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: , + value: 0, + check: 2, + element: , + range: [''] + }, + { + type: , + value: 0, + check: 4, + element: , + range: [''] + } +] \ No newline at end of file diff --git a/model/damage/set/河豚电音.js b/model/damage/set/河豚电音.js new file mode 100644 index 0000000..590444d --- /dev/null +++ b/model/damage/set/河豚电音.js @@ -0,0 +1,15 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.2, + isForever: true, + range: ['RZ'], + check: 4 + }, + { + type: '攻击力', + value: 0.15, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/混沌爵士.js b/model/damage/set/混沌爵士.js new file mode 100644 index 0000000..83e46cd --- /dev/null +++ b/model/damage/set/混沌爵士.js @@ -0,0 +1,16 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.15, + isForever: true, + element: ['Electric', 'Fire'], + check: 4 + }, + { + type: '增伤', + value: 0.2, + check: 4, + range: ['EQ', 'L'] + } +] \ No newline at end of file diff --git a/model/damage/set/混沌重金属.js b/model/damage/set/混沌重金属.js new file mode 100644 index 0000000..92c30fc --- /dev/null +++ b/model/damage/set/混沌重金属.js @@ -0,0 +1,21 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.1, + isForever: true, + element: 'Ether', + check: 2 + }, + { + type: '暴击伤害', + value: 0.2, + isForever: true, + check: 4 + }, + { + type: '暴击伤害', + value: 0.055 * 6, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/激素朋克.js b/model/damage/set/激素朋克.js new file mode 100644 index 0000000..80af4df --- /dev/null +++ b/model/damage/set/激素朋克.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: 0.25, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/炎狱重金属.js b/model/damage/set/炎狱重金属.js new file mode 100644 index 0000000..9eb2e46 --- /dev/null +++ b/model/damage/set/炎狱重金属.js @@ -0,0 +1,15 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.1, + element: 'Fire', + isForever: true, + check: 2 + }, + { + type: '暴击率', + value: 0.28, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/獠牙重金属.js b/model/damage/set/獠牙重金属.js new file mode 100644 index 0000000..8df2ccb --- /dev/null +++ b/model/damage/set/獠牙重金属.js @@ -0,0 +1,15 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.1, + check: 2, + isForever: true, + element: 'Physical' + }, + { + type: '增伤', + value: 0.35, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/雷暴重金属.js b/model/damage/set/雷暴重金属.js new file mode 100644 index 0000000..58a301f --- /dev/null +++ b/model/damage/set/雷暴重金属.js @@ -0,0 +1,15 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.1, + check: 2, + isForever: true, + element: 'Electric' + }, + { + type: '攻击力', + value: 0.28, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/set/静听嘉音.js b/model/damage/set/静听嘉音.js new file mode 100644 index 0000000..542aec4 --- /dev/null +++ b/model/damage/set/静听嘉音.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: 0.08 * 3, + check: 4 + } +] \ No newline at end of file diff --git a/model/damage/weapon.js b/model/damage/weapon.js deleted file mode 100644 index 2f78258..0000000 --- a/model/damage/weapon.js +++ /dev/null @@ -1,84 +0,0 @@ -import _ from 'lodash'; -import { getMapData } from '../../utils/file.js'; -import { ZZZAvatarInfo } from '../avatar.js'; -const weapon_effect = getMapData('weapon_effect'); - -/** - * 武器加成 - * @param {ZZZAvatarInfo['weapon']} equipment 武器 - * @param {ZZZAvatarInfo['damage_basic_properties']['base_detail']} base_detail 基础属性 - * @param {ZZZAvatarInfo['damage_basic_properties']['bonus_detail']} bonus_detail 套装加成 - * @returns {ZZZAvatarInfo['damage_basic_properties']['bonus_detail']} 套装加成 - */ -export const weapon_ability = (equipment, base_detail, bonus_detail) => { - let equipid = equipment.id - switch (equipid) { - case 14109:{ - let CriticalDamageBase = _.get(bonus_detail, 'CriticalDamageBase', 0); - bonus_detail['CriticalDamageBase'] = CriticalDamageBase + weapon_effect[equipment.id]['Param']['CriticalDamageBase'][equipment.star - 1]; - - let IceDmgAdd = _.get(bonus_detail, 'Ice_DmgAdd', 0); - bonus_detail['Ice_DmgAdd'] = IceDmgAdd + weapon_effect[equipment.id]['Param']['IceDmgAdd'][equipment.star - 1] * 2; - break; - } - case 14119:{ - let IceDmgAdd = _.get(bonus_detail, 'Ice_DmgAdd', 0); - bonus_detail['Ice_DmgAdd'] = IceDmgAdd + weapon_effect[equipment.id]['Param']['IceDmgAdd'][equipment.star - 1]; - - let CriticalChanceBase = _.get(bonus_detail, 'CriticalChanceBase', 0); - bonus_detail['CriticalChanceBase'] = CriticalChanceBase +weapon_effect[equipment.id]['Param']['CriticalChanceBase'][equipment.star - 1]; - break; - } - case 14102:{ - let Physical_DmgAdd = _.get(bonus_detail, 'Physical_DmgAdd', 0); - bonus_detail['Physical_DmgAdd'] = Physical_DmgAdd + weapon_effect[equipment.id]['Param']['Physical_DmgAdd'][equipment.star - 1]; - - let All_DmgAdd = _.get(bonus_detail, 'All_DmgAdd', 0); - bonus_detail['All_DmgAdd'] = All_DmgAdd + weapon_effect[equipment.id]['Param']['All_DmgAdd'][equipment.star - 1]; - break; - } - case 14104:{ - let AttackAddedRatio = _.get(bonus_detail, 'AttackAddedRatio', 0); - bonus_detail['AttackAddedRatio'] = AttackAddedRatio + weapon_effect[equipment.id]['Param']['AttackAddedRatio'][equipment.star - 1] * 8; - break; - } - case 14124:{ - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + weapon_effect[equipment.id]['Param']['A_DmgAdd'][equipment.star - 1] * 8; - break; - } - case 13001:{ - let R_DmgAdd = _.get(bonus_detail, 'R_DmgAdd', 0); - bonus_detail['R_DmgAdd'] = R_DmgAdd + weapon_effect[equipment.id]['Param']['R_DmgAdd'][equipment.star - 1] * 3; - break; - } - case 13004:{ - let AttackAddedRatio = _.get(bonus_detail, 'AttackAddedRatio', 0); - bonus_detail['AttackAddedRatio'] = AttackAddedRatio + weapon_effect[equipment.id]['Param']['AttackAddedRatio'][equipment.star - 1]; - break; - } - case 13013:{ - let EUP_DmgAdd = _.get(bonus_detail, 'EUP_DmgAdd', 0); - bonus_detail['EUP_DmgAdd'] = EUP_DmgAdd + weapon_effect[equipment.id]['Param']['EUP_DmgAdd'][equipment.star - 1]; - break; - } - case 13106:{ - let EUP_DmgAdd = _.get(bonus_detail, 'EUP_DmgAdd', 0); - bonus_detail['EUP_DmgAdd'] = EUP_DmgAdd + weapon_effect[equipment.id]['Param']['EUP_DmgAdd'][equipment.star - 1] * 15; - break; - } - case 13108:{ - let Physical_DmgAdd = _.get(bonus_detail, 'Physical_DmgAdd', 0); - bonus_detail['Physical_DmgAdd'] = Physical_DmgAdd + weapon_effect[equipment.id]['Param']['Physical_DmgAdd'][equipment.star - 1]; - break; - } - case 13111:{ - let A_DmgAdd = _.get(bonus_detail, 'A_DmgAdd', 0); - bonus_detail['A_DmgAdd'] = A_DmgAdd + weapon_effect[equipment.id]['Param']['A_DmgAdd'][equipment.star - 1]; - let C_DmgAdd = _.get(bonus_detail, 'C_DmgAdd', 0); - bonus_detail['C_DmgAdd'] = C_DmgAdd + weapon_effect[equipment.id]['Param']['A_DmgAdd'][equipment.star - 1]; - break; - } - } - return bonus_detail; -}; diff --git a/model/damage/weapon/「恒等式」-本格.js b/model/damage/weapon/「恒等式」-本格.js new file mode 100644 index 0000000..5f80d70 --- /dev/null +++ b/model/damage/weapon/「恒等式」-本格.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '防御力', + value: [0.2, 0.23, 0.26, 0.29, 0.32] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「月相」-晦.js b/model/damage/weapon/「月相」-晦.js new file mode 100644 index 0000000..ee0b0bb --- /dev/null +++ b/model/damage/weapon/「月相」-晦.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.15, 0.175, 0.2, 0.225, 0.25] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「月相」-望.js b/model/damage/weapon/「月相」-望.js new file mode 100644 index 0000000..96a7775 --- /dev/null +++ b/model/damage/weapon/「月相」-望.js @@ -0,0 +1,9 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.12, 0.14, 0.16, 0.18, 0.2], + isForever: true, + range: ['A', 'CC', 'CF'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「残响」-Ⅰ型.js b/model/damage/weapon/「残响」-Ⅰ型.js new file mode 100644 index 0000000..0aa2cb2 --- /dev/null +++ b/model/damage/weapon/「残响」-Ⅰ型.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '冲击力', + value: [0.08, 0.09, 0.1, 0.11, 0.12] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「残响」-Ⅱ型.js b/model/damage/weapon/「残响」-Ⅱ型.js new file mode 100644 index 0000000..03746b4 --- /dev/null +++ b/model/damage/weapon/「残响」-Ⅱ型.js @@ -0,0 +1,11 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '异常掌控', + value: [10, 12, 13, 15, 16] + }, + { + type: '异常精通', + value: [10, 12, 13, 15, 16] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「残响」-Ⅲ型.js b/model/damage/weapon/「残响」-Ⅲ型.js new file mode 100644 index 0000000..3a18fe0 --- /dev/null +++ b/model/damage/weapon/「残响」-Ⅲ型.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.08, 0.09, 0.1, 0.11, 0.12] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「湍流」-斧型.js b/model/damage/weapon/「湍流」-斧型.js new file mode 100644 index 0000000..3b5f7f7 --- /dev/null +++ b/model/damage/weapon/「湍流」-斧型.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '冲击力', + value: [0.09, 0.1, 0.11, 0.12, 0.13] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「电磁暴」-壹式.js b/model/damage/weapon/「电磁暴」-壹式.js new file mode 100644 index 0000000..34a3a72 --- /dev/null +++ b/model/damage/weapon/「电磁暴」-壹式.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '异常掌控', + value: [25, 28, 32, 36, 40] + } +] \ No newline at end of file diff --git a/model/damage/weapon/「电磁暴」-贰式.js b/model/damage/weapon/「电磁暴」-贰式.js new file mode 100644 index 0000000..98c220f --- /dev/null +++ b/model/damage/weapon/「电磁暴」-贰式.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '异常精通', + value: [25, 28, 32, 36, 40] + } +] \ No newline at end of file diff --git a/model/damage/weapon/人为刀俎.js b/model/damage/weapon/人为刀俎.js new file mode 100644 index 0000000..754a5e7 --- /dev/null +++ b/model/damage/weapon/人为刀俎.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '冲击力', + value: [0.02, 0.023, 0.026, 0.029, 0.032].map(v => v * 8) + } +] \ No newline at end of file diff --git a/model/damage/weapon/仿制星徽引擎.js b/model/damage/weapon/仿制星徽引擎.js new file mode 100644 index 0000000..f4f5332 --- /dev/null +++ b/model/damage/weapon/仿制星徽引擎.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.36, 0.41, 0.465, 0.52, 0.575], + element: 'Physical' + } +] \ No newline at end of file diff --git a/model/damage/weapon/兔能环.js b/model/damage/weapon/兔能环.js new file mode 100644 index 0000000..70c9117 --- /dev/null +++ b/model/damage/weapon/兔能环.js @@ -0,0 +1,12 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '生命值', + value: [0.08, 0.092, 0.104, 0.116, 0.128], + isForever: true + }, + { + type: '攻击力', + value: [0.1, 0.115, 0.13, 0.145, 0.16] + } +] \ No newline at end of file diff --git a/model/damage/weapon/加农转子.js b/model/damage/weapon/加农转子.js new file mode 100644 index 0000000..bed24c7 --- /dev/null +++ b/model/damage/weapon/加农转子.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.075, 0.086, 0.097, 0.108, 0.12], + isForever: true + } +] \ No newline at end of file diff --git a/model/damage/weapon/双生泣星.js b/model/damage/weapon/双生泣星.js new file mode 100644 index 0000000..ed46370 --- /dev/null +++ b/model/damage/weapon/双生泣星.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '异常精通', + value: [30, 34, 38, 42, 48].map(v => v * 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..aa966ed --- /dev/null +++ b/model/damage/weapon/含羞恶面.js @@ -0,0 +1,13 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.15, 0.175, 0.2, 0.22, 0.24], + element: 'Ice', + isForever: true + }, + { + type: '攻击力', + value: [0.02, 0.023, 0.026, 0.029, 0.032].map(v => v * 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..de0b5bc --- /dev/null +++ b/model/damage/weapon/啜泣摇篮.js @@ -0,0 +1,11 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.1, 0.125, 0.15, 0.175, 0.2] + }, + { + type: '增伤', + value: [0.017, 0.02, 0.025, 0.03, 0.033].map(v => v * 6) + } +] \ No newline at end of file diff --git a/model/damage/weapon/奔袭獠牙.js b/model/damage/weapon/奔袭獠牙.js new file mode 100644 index 0000000..260e975 --- /dev/null +++ b/model/damage/weapon/奔袭獠牙.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.18, 0.225, 0.27, 0.315, 0.36] + } +] \ No newline at end of file diff --git a/model/damage/weapon/好斗的阿炮.js b/model/damage/weapon/好斗的阿炮.js new file mode 100644 index 0000000..f03f4a4 --- /dev/null +++ b/model/damage/weapon/好斗的阿炮.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.025, 0.028, 0.032, 0.036, 0.04].map(v => v * 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..7885cae --- /dev/null +++ b/model/damage/weapon/家政员.js @@ -0,0 +1,9 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.03, 0.035, 0.04, 0.044, 0.048].map(v => v * 15), + element: 'Physical', + range: ['EQ'] // 只持续1s + } +] \ No newline at end of file diff --git a/model/damage/weapon/嵌合编译器.js b/model/damage/weapon/嵌合编译器.js new file mode 100644 index 0000000..a585768 --- /dev/null +++ b/model/damage/weapon/嵌合编译器.js @@ -0,0 +1,12 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.12, 0.15, 0.18, 0.21, 0.24], + isForever: true + }, + { + type: '异常精通', + value: [25, 31, 37, 43, 50].map(v => v * 3) + } +] \ No newline at end of file diff --git a/model/damage/weapon/强音热望.js b/model/damage/weapon/强音热望.js new file mode 100644 index 0000000..5eac9c1 --- /dev/null +++ b/model/damage/weapon/强音热望.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.06, 0.069, 0.078, 0.087, 0.096].map(v => v * 2) + } +] \ No newline at end of file diff --git a/model/damage/weapon/德玛拉电池Ⅱ型.js b/model/damage/weapon/德玛拉电池Ⅱ型.js new file mode 100644 index 0000000..f4bbcf2 --- /dev/null +++ b/model/damage/weapon/德玛拉电池Ⅱ型.js @@ -0,0 +1,9 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.15, 0.175, 0.2, 0.22, 0.24], + element: 'Electric', + isForever: true + } +] \ No newline at end of file diff --git a/model/damage/weapon/拘缚者.js b/model/damage/weapon/拘缚者.js new file mode 100644 index 0000000..934268d --- /dev/null +++ b/model/damage/weapon/拘缚者.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.06, 0.075, 0.09, 0.105, 0.12].map(v => v * 5), + range: ['A'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/旋钻机-赤轴.js b/model/damage/weapon/旋钻机-赤轴.js new file mode 100644 index 0000000..80e8527 --- /dev/null +++ b/model/damage/weapon/旋钻机-赤轴.js @@ -0,0 +1,9 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.5, 0.575, 0.65, 0.725, 0.8], + element: 'Electric', + range: ['A', 'CC'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/时流贤者.js b/model/damage/weapon/时流贤者.js new file mode 100644 index 0000000..dbff25c --- /dev/null +++ b/model/damage/weapon/时流贤者.js @@ -0,0 +1,13 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '异常精通', + value: [75, 85, 95, 105, 115] + }, + { + type: '异常增伤', + value: [0.25, 0.275, 0.3, 0.325, 0.35], + check: ({ calc }) => calc.get_AnomalyProficiency() >= 375, + range: ['紊乱'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/星徽引擎.js b/model/damage/weapon/星徽引擎.js new file mode 100644 index 0000000..b1d6f65 --- /dev/null +++ b/model/damage/weapon/星徽引擎.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.12, 0.138, 0.156, 0.174, 0.192] + } +] \ No newline at end of file diff --git a/model/damage/weapon/模板.js b/model/damage/weapon/模板.js new file mode 100644 index 0000000..fefb2dd --- /dev/null +++ b/model/damage/weapon/模板.js @@ -0,0 +1,28 @@ +/** + * @param {import('../BuffManager.ts').BuffManager} buffM + * @param {number} star 进阶星数 + */ +export function calc(buffM, star) { + buffM.new({ + type: , + value: [][star - 1], + element: , + range: [] + }) +} + +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: , + value: [], + element: , + range: [] + }, + { + type: , + value: [], + element: , + range: [] + } +] \ No newline at end of file diff --git a/model/damage/weapon/正版变身器.js b/model/damage/weapon/正版变身器.js new file mode 100644 index 0000000..7e3472f --- /dev/null +++ b/model/damage/weapon/正版变身器.js @@ -0,0 +1,12 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '生命值', + value: [0.08, 0.09, 0.1, 0.11, 0.125], + isForever: true + }, + { + type: '冲击力', + value: [0.1, 0.115, 0.13, 0.145, 0.16] + } +] \ No newline at end of file diff --git a/model/damage/weapon/残心青囊.js b/model/damage/weapon/残心青囊.js new file mode 100644 index 0000000..a1c1e22 --- /dev/null +++ b/model/damage/weapon/残心青囊.js @@ -0,0 +1,19 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '暴击率', + value: [0.1, 0.115, 0.13, 0.145, 0.16], + isForever: true + }, + { + type: '增伤', + value: [0.4, 0.46, 0.52, 0.58, 0.64], + element: 'Electric', + isForever: true, + range: ['CC'] + }, + { + type: '暴击率', + value: [0.1, 0.115, 0.13, 0.145, 0.16] + } +] \ No newline at end of file diff --git a/model/damage/weapon/淬锋钳刺.js b/model/damage/weapon/淬锋钳刺.js new file mode 100644 index 0000000..1fb7098 --- /dev/null +++ b/model/damage/weapon/淬锋钳刺.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.12, 0.15, 0.18, 0.21, 0.24].map(v => v * 3), + element: 'Physical' + } +] \ No newline at end of file diff --git a/model/damage/weapon/深海访客.js b/model/damage/weapon/深海访客.js new file mode 100644 index 0000000..2ee03b7 --- /dev/null +++ b/model/damage/weapon/深海访客.js @@ -0,0 +1,13 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.25, 0.315, 0.38, 0.445, 0.5], + element: 'Ice', + isForever: true + }, + { + type: '暴击率', + value: [0.1, 0.125, 0.15, 0.175, 0.2].map(v => v * 2) + } +] \ No newline at end of file diff --git a/model/damage/weapon/灼心摇壶.js b/model/damage/weapon/灼心摇壶.js new file mode 100644 index 0000000..b866076 --- /dev/null +++ b/model/damage/weapon/灼心摇壶.js @@ -0,0 +1,11 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.035, 0.044, 0.052, 0.061, 0.07].map(v => v * 10) + }, + { + type: '异常精通', + value: [50, 62, 75, 87, 100] + } +] \ No newline at end of file diff --git a/model/damage/weapon/焰心桂冠.js b/model/damage/weapon/焰心桂冠.js new file mode 100644 index 0000000..685175e --- /dev/null +++ b/model/damage/weapon/焰心桂冠.js @@ -0,0 +1,12 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '冲击力', + value: [0.25, 0.2875, 0.325, 0.3625, 0.4] + }, + { + type: '暴击伤害', + value: [0.015, 0.0172, 0.0195, 0.0217, 0.024].map(v => v * 20), + element: ['Ice', 'Fire'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/燃狱齿轮.js b/model/damage/weapon/燃狱齿轮.js new file mode 100644 index 0000000..8a2f0ed --- /dev/null +++ b/model/damage/weapon/燃狱齿轮.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '冲击力', + value: [0.1, 0.125, 0.15, 0.175, 0.2].map(v => v * 2) + } +] \ No newline at end of file diff --git a/model/damage/weapon/玉壶青冰.js b/model/damage/weapon/玉壶青冰.js new file mode 100644 index 0000000..232193b --- /dev/null +++ b/model/damage/weapon/玉壶青冰.js @@ -0,0 +1,11 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '冲击力', + value: [0.007, 0.0088, 0.0105, 0.0122, 0.014].map(v => v * 30) + }, + { + type: '增伤', + value: [0.2, 0.23, 0.26, 0.29, 0.32] + } +] \ No newline at end of file diff --git a/model/damage/weapon/硫磺石.js b/model/damage/weapon/硫磺石.js new file mode 100644 index 0000000..e6b75b6 --- /dev/null +++ b/model/damage/weapon/硫磺石.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.035, 0.044, 0.052, 0.06, 0.07].map(v => v * 8) + } +] \ No newline at end of file diff --git a/model/damage/weapon/聚宝箱.js b/model/damage/weapon/聚宝箱.js new file mode 100644 index 0000000..c9e01c1 --- /dev/null +++ b/model/damage/weapon/聚宝箱.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.15, 0.175, 0.2, 0.22, 0.24], + check: ({ avatar }) => avatar.element_type === 205 + } +] \ No newline at end of file diff --git a/model/damage/weapon/街头巨星.js b/model/damage/weapon/街头巨星.js new file mode 100644 index 0000000..cea48d1 --- /dev/null +++ b/model/damage/weapon/街头巨星.js @@ -0,0 +1,8 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.15, 0.172, 0.195, 0.217, 0.24].map(v => v * 3), + range: ['RZ'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/触电唇彩.js b/model/damage/weapon/触电唇彩.js new file mode 100644 index 0000000..29e198a --- /dev/null +++ b/model/damage/weapon/触电唇彩.js @@ -0,0 +1,11 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.1, 0.115, 0.13, 0.145, 0.16] + }, + { + type: '增伤', + value: [0.15, 0.175, 0.2, 0.225, 0.25] + } +] \ No newline at end of file diff --git a/model/damage/weapon/轰鸣座驾.js b/model/damage/weapon/轰鸣座驾.js new file mode 100644 index 0000000..94f81b2 --- /dev/null +++ b/model/damage/weapon/轰鸣座驾.js @@ -0,0 +1,11 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.08, 0.092, 0.104, 0.116, 0.128] + }, + { + type: '异常精通', + value: [40, 46, 52, 58, 64] + } +] \ No newline at end of file diff --git a/model/damage/weapon/逍遥游球.js b/model/damage/weapon/逍遥游球.js new file mode 100644 index 0000000..1787a88 --- /dev/null +++ b/model/damage/weapon/逍遥游球.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '暴击率', + value: [0.12, 0.135, 0.155, 0.175, 0.2] + } +] \ No newline at end of file diff --git a/model/damage/weapon/鎏金花信.js b/model/damage/weapon/鎏金花信.js new file mode 100644 index 0000000..d5d755c --- /dev/null +++ b/model/damage/weapon/鎏金花信.js @@ -0,0 +1,13 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.06, 0.069, 0.078, 0.087, 0.096], + isForever: true + }, + { + type: '增伤', + value: [0.15, 0.172, 0.195, 0.218, 0.24], + range: ['EQ'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/钢铁肉垫.js b/model/damage/weapon/钢铁肉垫.js new file mode 100644 index 0000000..9ce875b --- /dev/null +++ b/model/damage/weapon/钢铁肉垫.js @@ -0,0 +1,13 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '增伤', + value: [0.2, 0.25, 0.3, 0.35, 0.4], + element: 'Physical', + isForever: true + }, + { + type: '增伤', + value: [0.25, 0.315, 0.38, 0.44, 0.5] + } +] \ No newline at end of file diff --git a/model/damage/weapon/防暴者Ⅵ型.js b/model/damage/weapon/防暴者Ⅵ型.js new file mode 100644 index 0000000..f160cbd --- /dev/null +++ b/model/damage/weapon/防暴者Ⅵ型.js @@ -0,0 +1,14 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '暴击率', + value: [0.15, 0.188, 0.226, 0.264, 0.3], + isForever: true + }, + { + type: '增伤', + value: [0.35, 0.435, 0.52, 0.605, 0.7], + element: 'Ether', + range: ['A'] + } +] \ No newline at end of file diff --git a/model/damage/weapon/雨林饕客.js b/model/damage/weapon/雨林饕客.js new file mode 100644 index 0000000..65cb876 --- /dev/null +++ b/model/damage/weapon/雨林饕客.js @@ -0,0 +1,7 @@ +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '攻击力', + value: [0.025, 0.028, 0.032, 0.036, 0.04].map(v => v * 10) + } +] \ No newline at end of file diff --git a/model/damage/weapon/霰落星殿.js b/model/damage/weapon/霰落星殿.js new file mode 100644 index 0000000..619dd81 --- /dev/null +++ b/model/damage/weapon/霰落星殿.js @@ -0,0 +1,34 @@ +// 函数导出: + +/** + * @param {import('../BuffManager.ts').BuffManager} buffM + * @param {number} star 进阶星数 + */ +// export function calc(buffM, star) { +// buffM.new({ +// type: '暴击伤害', +// value: [0.5, 0.57, 0.65, 0.72, 0.8][star - 1], +// isForever: true +// }) +// buffM.new({ +// type: '增伤', +// value: [0.2, 0.23, 0.26, 0.29, 0.32][star - 1] * 2, +// element: 'Ice' +// }) +// } + +// 直接导出: + +/** @type {import('../BuffManager.ts').BuffManager['buffs']} */ +export const buffs = [ + { + type: '暴击伤害', + value: [0.5, 0.57, 0.65, 0.72, 0.8], + isForever: true + }, + { + type: '增伤', + value: [0.2, 0.23, 0.26, 0.29, 0.32].map(v => v * 2), + element: 'Ice' + } +] \ No newline at end of file diff --git a/model/equip.js b/model/equip.js index ca373a0..ed64d85 100644 --- a/model/equip.js +++ b/model/equip.js @@ -206,7 +206,7 @@ export class Equip { /** * @param {number} id - * @returns {EquipProperty} + * @returns {number} */ get_property(id) { const result = diff --git a/resources/map/AnomalyData.json b/resources/map/AnomalyData.json new file mode 100644 index 0000000..59792e3 --- /dev/null +++ b/resources/map/AnomalyData.json @@ -0,0 +1,98 @@ +[ + { + "name": "强击", + "element": "Physical", + "element_type": 200, + "sub_element_type": 0, + "duration": 0, + "interval": 0, + "multiplier": 7.13 + }, + { + "name": "畏缩", + "element": "Physical", + "element_type": 200, + "sub_element_type": 0, + "duration": 10, + "interval": 1, + "multiplier": 0, + "discover": { + "multiplier": 0.075, + "fixed_multiplier": 4.5 + } + }, + { + "name": "灼烧", + "element": "Fire", + "element_type": 201, + "sub_element_type": 0, + "duration": 10, + "interval": 0.5, + "multiplier": 0.5, + "discover": { + "multiplier": 0.5, + "fixed_multiplier": 4.5 + } + }, + { + "name": "碎冰", + "element": "Ice", + "element_type": 202, + "sub_element_type": 0, + "duration": 0, + "interval": 0, + "multiplier": 5 + }, + { + "name": "霜寒", + "element": "Ice", + "element_type": 202, + "sub_element_type": 0, + "duration": 10, + "interval": 1, + "multiplier": 0, + "discover": { + "multiplier": 0.075, + "fixed_multiplier": 4.5 + } + }, + { + "name": "霜寒", + "element": "Ice", + "element_type": 202, + "sub_element_type": 1, + "duration": 20, + "interval": 1, + "multiplier": 0, + "discover": { + "multiplier": 0.75, + "fixed_multiplier": 6 + } + }, + { + "name": "感电", + "element": "Electric", + "element_type": 203, + "sub_element_type": 0, + "duration": 10, + "interval": 1, + "multiplier": 1.25, + "discover": { + "multiplier": 1.25, + "fixed_multiplier": 4.5 + } + }, + { + "name": "侵蚀", + "element": "Ether", + "element_type": 205, + "sub_element_type": 0, + "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/map/Property2Name.json b/resources/map/Property2Name.json index 3314cc3..59e013a 100644 --- a/resources/map/Property2Name.json +++ b/resources/map/Property2Name.json @@ -1,21 +1,21 @@ { - "12102": ["AttackAddedRatio", "攻击力百分比"], - "12103": ["AttackDelta", "攻击力"], - "11102": ["HPAddedRatio", "生命值百分比"], - "11103": ["HPDelta", "生命值"], - "13102": ["DefenceAddedRatio", "防御力百分比"], - "13103": ["DefenceDelta", "防御力"], - "20103": ["CriticalChanceBase", "暴击率"], - "21103": ["CriticalDamageBase", "暴击伤害"], - "12202": ["ImpactRatio", "冲击力"], - "31203": ["ElementMystery", "异常精通"], - "31403": ["ElementAbnormalPower", "异常掌控"], - "23203": ["PenDelta", "穿透值"], - "30502": ["SpGetRatio", "能量恢复"], - "23103": ["PenRatioBase", "穿透率"], - "31703": ["IceAddedRatio", "冰属性伤害提高"], - "31603": ["FireAddedRatio", "火属性伤害提高"], - "31503": ["PhysicalAddedRatio", "物理属性伤害提高"], - "31803": ["ElectricAddedRatio", "电属性伤害提高"], - "31903": ["EtherAddedRatio", "以太属性伤害提高"] + "11102": ["HPRatio", "生命值百分比"], + "11103": ["HP", "生命值"], + "12102": ["ATKRatio", "攻击力百分比"], + "12103": ["ATK", "攻击力"], + "12202": ["Impact", "冲击力"], + "13102": ["DEFRatio", "防御力百分比"], + "13103": ["DEF", "防御力"], + "20103": ["CRITRate", "暴击率"], + "21103": ["CRITDMG", "暴击伤害"], + "23203": ["Pen", "穿透值"], + "23103": ["PenRatio", "穿透率"], + "30502": ["EnergyRegen", "能量恢复"], + "31203": ["AnomalyProficiency", "异常精通"], + "31403": ["AnomalyMastery", "异常掌控"], + "31503": ["PhysicalDMGBonus", "物理属性伤害提高"], + "31603": ["FireDMGBonus", "火属性伤害提高"], + "31703": ["IceDMGBonus", "冰属性伤害提高"], + "31803": ["ElectricDMGBonus", "电属性伤害提高"], + "31903": ["EtherDMGBonus", "以太属性伤害提高"] } diff --git a/resources/map/WeaponId2Data.json b/resources/map/WeaponId2Data.json new file mode 100644 index 0000000..99d4230 --- /dev/null +++ b/resources/map/WeaponId2Data.json @@ -0,0 +1,338 @@ +{ + "12001": { + "id": 12001, + "name": "「月相」-望", + "rarity": "B", + "profession": 1 + }, + "12002": { + "id": 12002, + "name": "「月相」-晦", + "rarity": "B", + "profession": 1 + }, + "12003": { + "id": 12003, + "name": "「月相」-朔", + "rarity": "B", + "profession": 1 + }, + "12004": { + "id": 12004, + "name": "「残响」-Ⅰ型", + "rarity": "B", + "profession": 4 + }, + "12005": { + "id": 12005, + "name": "「残响」-Ⅱ型", + "rarity": "B", + "profession": 4 + }, + "12006": { + "id": 12006, + "name": "「残响」-Ⅲ型", + "rarity": "B", + "profession": 4 + }, + "12007": { + "id": 12007, + "name": "「湍流」-铳型", + "rarity": "B", + "profession": 2 + }, + "12008": { + "id": 12008, + "name": "「湍流」-矢型", + "rarity": "B", + "profession": 2 + }, + "12009": { + "id": 12009, + "name": "「湍流」-斧型", + "rarity": "B", + "profession": 2 + }, + "12010": { + "id": 12010, + "name": "「电磁暴」-壹式", + "rarity": "B", + "profession": 3 + }, + "12011": { + "id": 12011, + "name": "「电磁暴」-贰式", + "rarity": "B", + "profession": 3 + }, + "12012": { + "id": 12012, + "name": "「电磁暴」-叁式", + "rarity": "B", + "profession": 3 + }, + "12013": { + "id": 12013, + "name": "「恒等式」-本格", + "rarity": "B", + "profession": 5 + }, + "12014": { + "id": 12014, + "name": "「恒等式」-变格", + "rarity": "B", + "profession": 5 + }, + "13001": { + "id": 13001, + "name": "街头巨星", + "rarity": "A", + "profession": 1 + }, + "13002": { + "id": 13002, + "name": "时光切片", + "rarity": "A", + "profession": 4 + }, + "13003": { + "id": 13003, + "name": "雨林饕客", + "rarity": "A", + "profession": 3 + }, + "13004": { + "id": 13004, + "name": "星徽引擎", + "rarity": "A", + "profession": 1 + }, + "13005": { + "id": 13005, + "name": "人为刀俎", + "rarity": "A", + "profession": 2 + }, + "13006": { + "id": 13006, + "name": "贵重骨核", + "rarity": "A", + "profession": 2 + }, + "13007": { + "id": 13007, + "name": "正版变身器", + "rarity": "A", + "profession": 5 + }, + "13008": { + "id": 13008, + "name": "双生泣星", + "rarity": "A", + "profession": 3 + }, + "13009": { + "id": 13009, + "name": "触电唇彩", + "rarity": "A", + "profession": 3 + }, + "13010": { + "id": 13010, + "name": "兔能环", + "rarity": "A", + "profession": 5 + }, + "13011": { + "id": 13011, + "name": "春日融融", + "rarity": "A", + "profession": 5 + }, + "13013": { + "id": 13013, + "name": "鎏金花信", + "rarity": "A", + "profession": 1 + }, + "13015": { + "id": 13015, + "name": "强音热望", + "rarity": "A", + "profession": 1 + }, + "13101": { + "id": 13101, + "name": "德玛拉电池Ⅱ型", + "rarity": "A", + "profession": 2 + }, + "13103": { + "id": 13103, + "name": "聚宝箱", + "rarity": "A", + "profession": 4 + }, + "13106": { + "id": 13106, + "name": "家政员", + "rarity": "A", + "profession": 1 + }, + "13108": { + "id": 13108, + "name": "仿制星徽引擎", + "rarity": "A", + "profession": 1 + }, + "13111": { + "id": 13111, + "name": "旋钻机-赤轴", + "rarity": "A", + "profession": 1 + }, + "13112": { + "id": 13112, + "name": "比格气缸", + "rarity": "A", + "profession": 5 + }, + "13113": { + "id": 13113, + "name": "含羞恶面", + "rarity": "A", + "profession": 4 + }, + "13115": { + "id": 13115, + "name": "好斗的阿炮", + "rarity": "A", + "profession": 4 + }, + "13127": { + "id": 13127, + "name": "维序者-特化型", + "rarity": "A", + "profession": 5 + }, + "13128": { + "id": 13128, + "name": "轰鸣座驾", + "rarity": "A", + "profession": 3 + }, + "14001": { + "id": 14001, + "name": "加农转子", + "rarity": "A", + "profession": 1 + }, + "14002": { + "id": 14002, + "name": "逍遥游球", + "rarity": "A", + "profession": 4 + }, + "14003": { + "id": 14003, + "name": "左轮转子", + "rarity": "A", + "profession": 2 + }, + "14102": { + "id": 14102, + "name": "钢铁肉垫", + "rarity": "S", + "profession": 1 + }, + "14104": { + "id": 14104, + "name": "硫磺石", + "rarity": "S", + "profession": 1 + }, + "14107": { + "id": 14107, + "name": "奔袭獠牙", + "rarity": "S", + "profession": 5 + }, + "14109": { + "id": 14109, + "name": "霰落星殿", + "rarity": "S", + "profession": 3 + }, + "14110": { + "id": 14110, + "name": "燃狱齿轮", + "rarity": "S", + "profession": 2 + }, + "14114": { + "id": 14114, + "name": "拘缚者", + "rarity": "S", + "profession": 2 + }, + "14116": { + "id": 14116, + "name": "焰心桂冠", + "rarity": "S", + "profession": 2 + }, + "14117": { + "id": 14117, + "name": "灼心摇壶", + "rarity": "S", + "profession": 3 + }, + "14118": { + "id": 14118, + "name": "嵌合编译器", + "rarity": "S", + "profession": 3 + }, + "14119": { + "id": 14119, + "name": "深海访客", + "rarity": "S", + "profession": 1 + }, + "14120": { + "id": 14120, + "name": "残心青囊", + "rarity": "S", + "profession": 1 + }, + "14121": { + "id": 14121, + "name": "啜泣摇篮", + "rarity": "S", + "profession": 4 + }, + "14122": { + "id": 14122, + "name": "时流贤者", + "rarity": "S", + "profession": 3 + }, + "14124": { + "id": 14124, + "name": "防暴者Ⅵ型", + "rarity": "S", + "profession": 1 + }, + "14125": { + "id": 14125, + "name": "玉壶青冰", + "rarity": "S", + "profession": 2 + }, + "14126": { + "id": 14126, + "name": "淬锋钳刺", + "rarity": "S", + "profession": 3 + } +} \ No newline at end of file diff --git a/resources/map/weapon_effect.json b/resources/map/weapon_effect.json deleted file mode 100644 index f0c995f..0000000 --- a/resources/map/weapon_effect.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "14109": { - "Param": { - "CriticalDamageBase": [ - 0.5, - 0.57, - 0.65, - 0.72, - 0.8 - ], - "IceDmgAdd": [ - 0.2, - 0.23, - 0.26, - 0.29, - 0.32 - ] - } - }, - "14119": { - "Param": { - "IceDmgAdd": [ - 0.25, - 0.315, - 0.38, - 0.445, - 0.5 - ], - "CriticalChanceBase": [ - 0.2, - 0.25, - 0.3, - 0.35, - 0.4 - ] - } - }, - "14102": { - "Param": { - "All_DmgAdd": [ - 0.25, - 0.315, - 0.38, - 0.445, - 0.5 - ], - "Physical_DmgAdd": [ - 0.2, - 0.25, - 0.3, - 0.35, - 0.4 - ] - } - }, - "14104": { - "Param": { - "AttackAddedRatio": [ - 0.035, - 0.044, - 0.052, - 0.06, - 0.07 - ] - } - }, - "14124": { - "Param": { - "A_DmgAdd": [ - 0.35, - 0.435, - 0.52, - 0.605, - 0.7 - ] - } - }, - "13001": { - "Param": { - "R_DmgAdd": [ - 0.15, - 0.172, - 0.195, - 0.217, - 0.24 - ] - } - }, - "13004": { - "Param": { - "AttackAddedRatio": [ - 0.12, - 0.138, - 0.156, - 0.174, - 0.192 - ] - } - }, - "13013": { - "Param": { - "EUP_DmgAdd": [ - 0.15, - 0.172, - 0.195, - 0.217, - 0.24 - ] - } - }, - "13106": { - "Param": { - "EUP_DmgAdd": [ - 0.03, - 0.035, - 0.04, - 0.044, - 0.048 - ] - } - }, - "13108": { - "Param": { - "Physical_DmgAdd": [ - 0.36, - 0.41, - 0.465, - 0.52, - 0.575 - ] - } - }, - "13111": { - "Param": { - "A_DmgAdd": [ - 0.50, - 0.575, - 0.65, - 0.725, - 0.80 - ] - } - }, - "14121": { - "Param": { - "All_DmgAdd": [ - 0.1, - 0.125, - 0.15, - 0.175, - 0.20 - ], - "All_DmgAdd_Max": [ - 0.102, - 0.12, - 0.15, - 0.18, - 0.198 - ] - } - } -} diff --git a/resources/panel/card.html b/resources/panel/card.html index c169542..14537a8 100644 --- a/resources/panel/card.html +++ b/resources/panel/card.html @@ -39,56 +39,57 @@
<% include(sys.specialTitle, {en: 'PROPERTY' , cn: '属性' , count: 6 }) %>
+ {{set basic_properties = charData.basic_properties}}
生命值
-
{{charData.basic_properties.hpmax.final}}
+
{{basic_properties.hpmax.final}}
攻击力
-
{{charData.basic_properties.attack.final}}
+
{{basic_properties.attack.final}}
防御力
-
{{charData.basic_properties.def.final}}
+
{{basic_properties.def.final}}
冲击力
-
{{charData.basic_properties.breakstun.final}}
+
{{basic_properties.breakstun.final}}
暴击率
-
{{charData.basic_properties.crit.final}}
+
{{basic_properties.crit.final}}
暴击伤害
-
{{charData.basic_properties.critdam.final}}
+
{{basic_properties.critdam.final}}
异常掌控
-
{{charData.basic_properties.elementabnormalpower.final}}
+
{{basic_properties.elementabnormalpower.final}}
异常精通
-
{{charData.basic_properties.elementmystery.final}}
+
{{basic_properties.elementmystery.final}}
穿透率
-
{{charData.basic_properties.penratio.final}}
+
{{basic_properties.penratio.final}}
能量恢复
-
{{charData.basic_properties.sprecover.final}}
+
{{basic_properties.sprecover.final}}
@@ -223,9 +224,9 @@ {{each damages damage}}
-
{{damage.title}}
-
{{damage.value.cd.toFixed(0)}}
-
{{damage.value.qw.toFixed(0)}}
+
{{damage.skill.name}}
+
{{damage.result.critDMG.toFixed(0)}}
+
{{damage.result.expectDMG.toFixed(0)}}
{{/each}}