mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-14 12:17:48 +00:00
完全重构伤害计算;支持异常伤害计算;支持所有武器、套装计算;新增悠真计算等
This commit is contained in:
parent
f7e1066773
commit
3f8e64af66
99 changed files with 4243 additions and 1615 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -9,4 +9,5 @@ data/**/*.*
|
|||
|
||||
resources/images/**/*.*
|
||||
resources/data/**/*.*
|
||||
resources/map/*.js
|
||||
model/damage/map/
|
||||
model/damage/character/**/*_user.*
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
> 插件依靠社区维护,发起者随缘更新,但是ZZZure组织成员会对PR进行合并,你可以在PR页面@协助者进行合并。
|
||||
>
|
||||
> 在你使用之前请**务必**完整阅读 `README` 内容,如因无视 `README` 遇到的问题,在提问时难免遭受嘲笑。
|
||||
>
|
||||
> 如需自定义伤害计算请查看[此说明](./model/damage/README.md)
|
||||
|
||||
# 安装
|
||||
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
160
model/avatar.js
160
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<string, number>;
|
||||
* set_detail: Record<string, number>;
|
||||
* }} */
|
||||
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} */
|
||||
|
|
|
|||
223
model/damage/BuffManager.js
Normal file
223
model/damage/BuffManager.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
296
model/damage/BuffManager.ts
Normal file
296
model/damage/BuffManager.ts
Normal file
|
|
@ -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, boolean>()
|
||||
/**
|
||||
* 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<T extends keyof buff>(buffs: buff[], type: T, value: buff[T]): buff[]
|
||||
_filter(buffs: buff[], obj: { [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: buff[key] } & { element: element }, calc?: Calculator): buff[]
|
||||
_filter(buffs: buff[], fnc: (buff: buff, index: number) => boolean): buff[]
|
||||
_filter<T extends keyof buff>(
|
||||
buffs: buff[],
|
||||
param:
|
||||
| T
|
||||
| ({ [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: 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<T extends keyof buff>(type: T, value: buff[T]): buff[]
|
||||
/**
|
||||
* 根据多个指定属性筛选 **启用状态** 的buff
|
||||
*/
|
||||
filter(obj: { [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: buff[key] } & { element: element }, calc?: Calculator): buff[]
|
||||
/**
|
||||
* 根据指定函数筛选buff
|
||||
*/
|
||||
filter(fnc: (buff: buff, index: number) => boolean): buff[]
|
||||
filter<T extends keyof buff>(
|
||||
param:
|
||||
| T
|
||||
| ({ [key in Exclude<keyof buff, 'status' | 'check' | 'element'>]?: buff[key] } & { element: element })
|
||||
| ((buff: buff, index: number) => boolean),
|
||||
valueOcalc?: buff[T] | Calculator
|
||||
) {
|
||||
// @ts-ignore
|
||||
return this._filter(this.buffs, param, valueOcalc)
|
||||
}
|
||||
|
||||
operator<T extends keyof buff>(type: T, value: buff[T], fnc: (buff: buff) => void) {
|
||||
this.forEach(buff => {
|
||||
if (buff[type] === value) {
|
||||
fnc(buff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭符合条件的所有buff
|
||||
*/
|
||||
close<T extends keyof buff>(type: T, value: buff[T]) {
|
||||
this.operator(type, value, buff => buff.status = false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启符合条件的所有buff
|
||||
*/
|
||||
open<T extends keyof buff>(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<T extends keyof buff>(type: T, value?: buff[T]): void
|
||||
default<T extends keyof buff>(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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
406
model/damage/Calculator.js
Normal file
406
model/damage/Calculator.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
545
model/damage/Calculator.ts
Normal file
545
model/damage/Calculator.ts
Normal file
|
|
@ -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<T extends keyof enemy>(key: T, value: enemy[T]): void
|
||||
defEnemy(enemy: enemy): void
|
||||
defEnemy<T extends keyof enemy>(param: T | enemy, value?: enemy[T]) {
|
||||
if (typeof param === 'string' && value !== undefined) {
|
||||
this.enemy[param] = value
|
||||
} else if (typeof param === 'object') {
|
||||
_.merge(this.enemy, param)
|
||||
}
|
||||
}
|
||||
|
||||
/** 注册skill */
|
||||
new(skill: skill): skill[]
|
||||
/** 注册skills */
|
||||
new(skills: skill[]): skill[]
|
||||
new(skill: skill | skill[]) {
|
||||
if (Array.isArray(skill)) {
|
||||
skill.forEach(s => this.new(s))
|
||||
return this.skills
|
||||
}
|
||||
skill = _.merge({
|
||||
...this.defaultSkill
|
||||
}, skill)
|
||||
if (!skill.element) skill.element = elementType2element(this.avatar.element_type)
|
||||
if (!skill.name || !skill.type) return logger.warn('无效skill:', skill)
|
||||
this.skills.push(skill)
|
||||
return this.skills
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算已注册的技能伤害
|
||||
* @param type 技能类型名
|
||||
*/
|
||||
calc_skill(type: skill['type']): damage
|
||||
/**
|
||||
* 计算技能伤害
|
||||
* @param skill 技能
|
||||
*/
|
||||
calc_skill(skill: skill): damage
|
||||
calc_skill(skill: skill['type'] | skill) {
|
||||
if (typeof skill === 'string') {
|
||||
const MySkill = this.skills.find(s => s.type === skill)
|
||||
if (!MySkill) return
|
||||
return this.calc_skill(MySkill)
|
||||
}
|
||||
if (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<T extends keyof skill>(type: T, value?: skill[T]): void
|
||||
default<T extends keyof skill>(param: T | { [key in keyof skill]?: skill[key] }, value?: skill[T]): void {
|
||||
if (typeof param === 'object') {
|
||||
this.defaultSkill = param
|
||||
} else {
|
||||
if (value === undefined) delete this.defaultSkill[param]
|
||||
else this.defaultSkill[param] = value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取技能等级
|
||||
* @param baseType 技能基类 'A', 'E', 'C', 'R', 'T', 'L'
|
||||
*/
|
||||
get_SkillLevel(baseType: string) {
|
||||
const id = ['A', 'E', 'C', 'R', , 'T', 'L'].indexOf(baseType)
|
||||
if (id === -1) return 1
|
||||
return Number(this.avatar.skills.find(({ skill_type }) => skill_type === id)?.level || 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取技能倍率
|
||||
* @param type 参见技能命名标准
|
||||
*/
|
||||
get_Multiplier(type: string) {
|
||||
const skillLevel = this.get_SkillLevel(type[0])
|
||||
logger.debug(`等级:${skillLevel}`)
|
||||
const Multiplier = charData[this.avatar.id].skill[type]?.[skillLevel - 1]
|
||||
logger.debug(`倍率:${Multiplier}`)
|
||||
return Multiplier
|
||||
}
|
||||
|
||||
get_AnomalyData(skill: skill) {
|
||||
let a = AnomalyData.filter(({ element_type }) => element_type === this.avatar.element_type)
|
||||
if (skill.type === '紊乱') a = a.filter(({ discover }) => discover)
|
||||
else a = a.filter(({ name, multiplier }) => name === skill.type && multiplier)
|
||||
if (a.length === 1) return a[0]
|
||||
a = a.filter(({ sub_element_type }) => sub_element_type === this.avatar.sub_element_type)
|
||||
return a[0]
|
||||
}
|
||||
|
||||
/** 获取属性异常倍率 */
|
||||
get_AnomalyMultiplier(skill: skill, usefulBuffs: buff[], 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
|
||||
}
|
||||
|
||||
}
|
||||
314
model/damage/README.md
Normal file
314
model/damage/README.md
Normal file
|
|
@ -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的精确管控,插件的计算结果将与游戏实机十分吻合
|
||||
|
||||
<p align="center">
|
||||
<img width="251" src="https://s2.loli.net/2025/01/14/o6mi3LKdgGtT2RP.jpg" title="她真好看">
|
||||
<img width="251" src="https://s2.loli.net/2025/01/14/Ue5kLpha7N621Px.jpg" title="她真好看">
|
||||
</p>
|
||||
|
||||
## 技能属性
|
||||
|
||||
技能属性,即在代理人技能详情界面对该技能的描述:技能名、伤害倍率、伤害类型等
|
||||
|
||||
### 认识技能
|
||||
|
||||
技能为泛指,任何可以造成伤害的输出手段,此处都可称为技能,包括属性异常、紊乱等
|
||||
|
||||
每个技能由各项[技能参数](./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
|
||||
|
|
@ -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({});
|
||||
}
|
||||
|
|
|
|||
172
model/damage/avatar.ts
Normal file
172
model/damage/avatar.ts
Normal file
|
|
@ -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({})
|
||||
}
|
||||
37
model/damage/character/11号/calc.js
Normal file
37
model/damage/character/11号/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
22
model/damage/character/11号/data.json
Normal file
22
model/damage/character/11号/data.json
Normal file
|
|
@ -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]
|
||||
}
|
||||
}
|
||||
35
model/damage/character/安东/calc.js
Normal file
35
model/damage/character/安东/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
27
model/damage/character/安东/data.json
Normal file
27
model/damage/character/安东/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
42
model/damage/character/悠真/calc.js
Normal file
42
model/damage/character/悠真/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
30
model/damage/character/悠真/data.json
Normal file
30
model/damage/character/悠真/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
173
model/damage/character/星见雅/calc.js
Normal file
173
model/damage/character/星见雅/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
34
model/damage/character/星见雅/data.json
Normal file
34
model/damage/character/星见雅/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
50
model/damage/character/朱鸢/calc.js
Normal file
50
model/damage/character/朱鸢/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
24
model/damage/character/朱鸢/data.json
Normal file
24
model/damage/character/朱鸢/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
123
model/damage/character/模板/calc.js
Normal file
123
model/damage/character/模板/calc.js
Normal file
|
|
@ -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: '' },
|
||||
]
|
||||
27
model/damage/character/模板/data.json
Normal file
27
model/damage/character/模板/data.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"skill": {
|
||||
"AP": [
|
||||
|
||||
],
|
||||
"CC": [
|
||||
|
||||
],
|
||||
"CF": [
|
||||
|
||||
],
|
||||
"EQ": [
|
||||
|
||||
],
|
||||
"RL": [
|
||||
|
||||
],
|
||||
"RZ": [
|
||||
|
||||
]
|
||||
},
|
||||
"buff": {
|
||||
"T": [
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
40
model/damage/character/猫又/calc.js
Normal file
40
model/damage/character/猫又/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
24
model/damage/character/猫又/data.json
Normal file
24
model/damage/character/猫又/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
64
model/damage/character/艾莲/calc.js
Normal file
64
model/damage/character/艾莲/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
39
model/damage/character/艾莲/data.json
Normal file
39
model/damage/character/艾莲/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
53
model/damage/character/青衣/calc.js
Normal file
53
model/damage/character/青衣/calc.js
Normal file
|
|
@ -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' }
|
||||
]
|
||||
19
model/damage/character/青衣/data.json
Normal file
19
model/damage/character/青衣/data.json
Normal file
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
8
model/damage/set/原始朋克.js
Normal file
8
model/damage/set/原始朋克.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '增伤',
|
||||
value: 0.15,
|
||||
check: 4
|
||||
}
|
||||
]
|
||||
8
model/damage/set/啄木鸟电音.js
Normal file
8
model/damage/set/啄木鸟电音.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '攻击力',
|
||||
value: 0.09 * 3,
|
||||
check: 4
|
||||
}
|
||||
]
|
||||
42
model/damage/set/折枝剑歌.js
Normal file
42
model/damage/set/折枝剑歌.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
8
model/damage/set/摇摆爵士.js
Normal file
8
model/damage/set/摇摆爵士.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '增伤',
|
||||
value: 0.15,
|
||||
check: 4
|
||||
}
|
||||
]
|
||||
23
model/damage/set/极地重金属.js
Normal file
23
model/damage/set/极地重金属.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
43
model/damage/set/模板.js
Normal file
43
model/damage/set/模板.js
Normal file
|
|
@ -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: ['']
|
||||
}
|
||||
]
|
||||
15
model/damage/set/河豚电音.js
Normal file
15
model/damage/set/河豚电音.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
16
model/damage/set/混沌爵士.js
Normal file
16
model/damage/set/混沌爵士.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
21
model/damage/set/混沌重金属.js
Normal file
21
model/damage/set/混沌重金属.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
8
model/damage/set/激素朋克.js
Normal file
8
model/damage/set/激素朋克.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '攻击力',
|
||||
value: 0.25,
|
||||
check: 4
|
||||
}
|
||||
]
|
||||
15
model/damage/set/炎狱重金属.js
Normal file
15
model/damage/set/炎狱重金属.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
15
model/damage/set/獠牙重金属.js
Normal file
15
model/damage/set/獠牙重金属.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
15
model/damage/set/雷暴重金属.js
Normal file
15
model/damage/set/雷暴重金属.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
8
model/damage/set/静听嘉音.js
Normal file
8
model/damage/set/静听嘉音.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '增伤',
|
||||
value: 0.08 * 3,
|
||||
check: 4
|
||||
}
|
||||
]
|
||||
|
|
@ -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;
|
||||
};
|
||||
7
model/damage/weapon/「恒等式」-本格.js
Normal file
7
model/damage/weapon/「恒等式」-本格.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/「月相」-晦.js
Normal file
7
model/damage/weapon/「月相」-晦.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
9
model/damage/weapon/「月相」-望.js
Normal file
9
model/damage/weapon/「月相」-望.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/「残响」-Ⅰ型.js
Normal file
7
model/damage/weapon/「残响」-Ⅰ型.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
11
model/damage/weapon/「残响」-Ⅱ型.js
Normal file
11
model/damage/weapon/「残响」-Ⅱ型.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/「残响」-Ⅲ型.js
Normal file
7
model/damage/weapon/「残响」-Ⅲ型.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/「湍流」-斧型.js
Normal file
7
model/damage/weapon/「湍流」-斧型.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/「电磁暴」-壹式.js
Normal file
7
model/damage/weapon/「电磁暴」-壹式.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '异常掌控',
|
||||
value: [25, 28, 32, 36, 40]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/「电磁暴」-贰式.js
Normal file
7
model/damage/weapon/「电磁暴」-贰式.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '异常精通',
|
||||
value: [25, 28, 32, 36, 40]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/人为刀俎.js
Normal file
7
model/damage/weapon/人为刀俎.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
8
model/damage/weapon/仿制星徽引擎.js
Normal file
8
model/damage/weapon/仿制星徽引擎.js
Normal file
|
|
@ -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'
|
||||
}
|
||||
]
|
||||
12
model/damage/weapon/兔能环.js
Normal file
12
model/damage/weapon/兔能环.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
8
model/damage/weapon/加农转子.js
Normal file
8
model/damage/weapon/加农转子.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/双生泣星.js
Normal file
7
model/damage/weapon/双生泣星.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/** @type {import('../BuffManager.ts').BuffManager['buffs']} */
|
||||
export const buffs = [
|
||||
{
|
||||
type: '异常精通',
|
||||
value: [30, 34, 38, 42, 48].map(v => v * 4)
|
||||
}
|
||||
]
|
||||
13
model/damage/weapon/含羞恶面.js
Normal file
13
model/damage/weapon/含羞恶面.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
11
model/damage/weapon/啜泣摇篮.js
Normal file
11
model/damage/weapon/啜泣摇篮.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/奔袭獠牙.js
Normal file
7
model/damage/weapon/奔袭獠牙.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/好斗的阿炮.js
Normal file
7
model/damage/weapon/好斗的阿炮.js
Normal file
|
|
@ -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),
|
||||
}
|
||||
]
|
||||
9
model/damage/weapon/家政员.js
Normal file
9
model/damage/weapon/家政员.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
12
model/damage/weapon/嵌合编译器.js
Normal file
12
model/damage/weapon/嵌合编译器.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/强音热望.js
Normal file
7
model/damage/weapon/强音热望.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
9
model/damage/weapon/德玛拉电池Ⅱ型.js
Normal file
9
model/damage/weapon/德玛拉电池Ⅱ型.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
8
model/damage/weapon/拘缚者.js
Normal file
8
model/damage/weapon/拘缚者.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
9
model/damage/weapon/旋钻机-赤轴.js
Normal file
9
model/damage/weapon/旋钻机-赤轴.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
13
model/damage/weapon/时流贤者.js
Normal file
13
model/damage/weapon/时流贤者.js
Normal file
|
|
@ -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: ['紊乱']
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/星徽引擎.js
Normal file
7
model/damage/weapon/星徽引擎.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
28
model/damage/weapon/模板.js
Normal file
28
model/damage/weapon/模板.js
Normal file
|
|
@ -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: []
|
||||
}
|
||||
]
|
||||
12
model/damage/weapon/正版变身器.js
Normal file
12
model/damage/weapon/正版变身器.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
19
model/damage/weapon/残心青囊.js
Normal file
19
model/damage/weapon/残心青囊.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
8
model/damage/weapon/淬锋钳刺.js
Normal file
8
model/damage/weapon/淬锋钳刺.js
Normal file
|
|
@ -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'
|
||||
}
|
||||
]
|
||||
13
model/damage/weapon/深海访客.js
Normal file
13
model/damage/weapon/深海访客.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
11
model/damage/weapon/灼心摇壶.js
Normal file
11
model/damage/weapon/灼心摇壶.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
12
model/damage/weapon/焰心桂冠.js
Normal file
12
model/damage/weapon/焰心桂冠.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/燃狱齿轮.js
Normal file
7
model/damage/weapon/燃狱齿轮.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
11
model/damage/weapon/玉壶青冰.js
Normal file
11
model/damage/weapon/玉壶青冰.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/硫磺石.js
Normal file
7
model/damage/weapon/硫磺石.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
8
model/damage/weapon/聚宝箱.js
Normal file
8
model/damage/weapon/聚宝箱.js
Normal file
|
|
@ -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
|
||||
}
|
||||
]
|
||||
8
model/damage/weapon/街头巨星.js
Normal file
8
model/damage/weapon/街头巨星.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
11
model/damage/weapon/触电唇彩.js
Normal file
11
model/damage/weapon/触电唇彩.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
11
model/damage/weapon/轰鸣座驾.js
Normal file
11
model/damage/weapon/轰鸣座驾.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/逍遥游球.js
Normal file
7
model/damage/weapon/逍遥游球.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
13
model/damage/weapon/鎏金花信.js
Normal file
13
model/damage/weapon/鎏金花信.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
13
model/damage/weapon/钢铁肉垫.js
Normal file
13
model/damage/weapon/钢铁肉垫.js
Normal file
|
|
@ -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]
|
||||
}
|
||||
]
|
||||
14
model/damage/weapon/防暴者Ⅵ型.js
Normal file
14
model/damage/weapon/防暴者Ⅵ型.js
Normal file
|
|
@ -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']
|
||||
}
|
||||
]
|
||||
7
model/damage/weapon/雨林饕客.js
Normal file
7
model/damage/weapon/雨林饕客.js
Normal file
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
34
model/damage/weapon/霰落星殿.js
Normal file
34
model/damage/weapon/霰落星殿.js
Normal file
|
|
@ -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'
|
||||
}
|
||||
]
|
||||
|
|
@ -206,7 +206,7 @@ export class Equip {
|
|||
|
||||
/**
|
||||
* @param {number} id
|
||||
* @returns {EquipProperty}
|
||||
* @returns {number}
|
||||
*/
|
||||
get_property(id) {
|
||||
const result =
|
||||
|
|
|
|||
98
resources/map/AnomalyData.json
Normal file
98
resources/map/AnomalyData.json
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -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", "以太属性伤害提高"]
|
||||
}
|
||||
|
|
|
|||
338
resources/map/WeaponId2Data.json
Normal file
338
resources/map/WeaponId2Data.json
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -39,56 +39,57 @@
|
|||
<div class="title">
|
||||
<% include(sys.specialTitle, {en: 'PROPERTY' , cn: '属性' , count: 6 }) %>
|
||||
</div>
|
||||
{{set basic_properties = charData.basic_properties}}
|
||||
<div class="list">
|
||||
<div class="properties">
|
||||
<div class="prop-icon hpmax"></div>
|
||||
<div class="label yellow">生命值</div>
|
||||
<div class="value">{{charData.basic_properties.hpmax.final}}</div>
|
||||
<div class="value">{{basic_properties.hpmax.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon attack"></div>
|
||||
<div class="label yellow">攻击力</div>
|
||||
<div class="value">{{charData.basic_properties.attack.final}}</div>
|
||||
<div class="value">{{basic_properties.attack.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon def"></div>
|
||||
<div class="label yellow">防御力</div>
|
||||
<div class="value">{{charData.basic_properties.def.final}}</div>
|
||||
<div class="value">{{basic_properties.def.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon breakstun"></div>
|
||||
<div class="label">冲击力</div>
|
||||
<div class="value">{{charData.basic_properties.breakstun.final}}</div>
|
||||
<div class="value">{{basic_properties.breakstun.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon crit"></div>
|
||||
<div class="label blue">暴击率</div>
|
||||
<div class="value">{{charData.basic_properties.crit.final}}</div>
|
||||
<div class="value">{{basic_properties.crit.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon critdam"></div>
|
||||
<div class="label blue">暴击伤害</div>
|
||||
<div class="value">{{charData.basic_properties.critdam.final}}</div>
|
||||
<div class="value">{{basic_properties.critdam.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon elementabnormalpower"></div>
|
||||
<div class="label">异常掌控</div>
|
||||
<div class="value">{{charData.basic_properties.elementabnormalpower.final}}</div>
|
||||
<div class="value">{{basic_properties.elementabnormalpower.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon elementmystery"></div>
|
||||
<div class="label">异常精通</div>
|
||||
<div class="value">{{charData.basic_properties.elementmystery.final}}</div>
|
||||
<div class="value">{{basic_properties.elementmystery.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon penratio"></div>
|
||||
<div class="label">穿透率</div>
|
||||
<div class="value">{{charData.basic_properties.penratio.final}}</div>
|
||||
<div class="value">{{basic_properties.penratio.final}}</div>
|
||||
</div>
|
||||
<div class="properties">
|
||||
<div class="prop-icon sprecover"></div>
|
||||
<div class="label">能量恢复</div>
|
||||
<div class="value">{{charData.basic_properties.sprecover.final}}</div>
|
||||
<div class="value">{{basic_properties.sprecover.final}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -223,9 +224,9 @@
|
|||
</div>
|
||||
{{each damages damage}}
|
||||
<div class="dmg-tr">
|
||||
<div class="dmg-td no-zzz-font">{{damage.title}}</div>
|
||||
<div class="dmg-td">{{damage.value.cd.toFixed(0)}}</div>
|
||||
<div class="dmg-td">{{damage.value.qw.toFixed(0)}}</div>
|
||||
<div class="dmg-td no-zzz-font">{{damage.skill.name}}</div>
|
||||
<div class="dmg-td">{{damage.result.critDMG.toFixed(0)}}</div>
|
||||
<div class="dmg-td">{{damage.result.expectDMG.toFixed(0)}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue