ZZZ-Plugin/model/damage/Score.js

354 lines
12 KiB
JavaScript

import { baseValueData, formatScoreWeight } from '../../lib/score.js';
import { idToName } from '../../lib/convert/property.js';
import { aliasToID } from '../../lib/convert/char.js';
import { getMapData } from '../../utils/file.js';
import { scoreFnc } from './avatar.js';
var rarity;
(function (rarity) {
rarity[rarity["S"] = 0] = "S";
rarity[rarity["A"] = 1] = "A";
rarity[rarity["B"] = 2] = "B";
})(rarity || (rarity = {}));
const equipScore = getMapData('EquipScore');
for (const charName in equipScore) {
const charID = +charName || aliasToID(charName);
if (!charID) {
logger.warn(`驱动盘评分:未找到角色${charName}的角色ID`);
delete equipScore[charName];
continue;
}
equipScore[charID] = equipScore[charName];
delete equipScore[charName];
}
const mainStats = getMapData('EquipMainStats');
const subStats = Object.keys(baseValueData).map(Number);
export default class Score {
equip;
weight;
partition;
userMainStat;
constructor(equip, weight) {
this.equip = equip;
this.weight = weight;
this.partition = this.equip.equipment_type;
this.userMainStat = this.equip.main_properties[0].property_id;
}
get_level_multiplier() {
return (0.25 + +this.equip.level * 0.05) || 1;
}
get_rarity_multiplier() {
switch (rarity[this.equip.rarity]) {
case rarity.S:
return 1;
case rarity.A:
return 2 / 3;
case rarity.B:
return 1 / 3;
default:
return 1;
}
}
get_max_count() {
const subMaxStats = subStats
.filter(p => p !== this.userMainStat && this.weight[p])
.sort((a, b) => this.weight[b] - this.weight[a]).slice(0, 4);
if (!subMaxStats.length)
return 0;
logger.debug(`[${this.partition}号位]理论副词条:` + subMaxStats.map(idToName).reduce((a, p, i) => a + `${p}*${this.weight[subMaxStats[i]].toFixed(2)} `, ''));
let count = this.weight[subMaxStats[0]] * 6;
subMaxStats.slice(1).forEach(p => count += this.weight[p] || 0);
logger.debug(`[${this.partition}号位]理论词条数:${logger.blue(count)}`);
return count;
}
get_actual_count() {
let count = 0;
for (const prop of this.equip.properties) {
const propID = prop.property_id;
const weight = this.weight[propID];
if (weight) {
logger.debug(`[${this.partition}号位]实际副词条:${idToName(propID)} ${logger.green(prop.count + 1)}*${weight}`);
count += weight * (prop.count + 1);
}
}
logger.debug(`[${this.partition}号位]实际词条数:${logger.blue(count)}`);
return count;
}
get_score() {
const rarity_multiplier = this.get_rarity_multiplier();
const actual_count = this.get_actual_count();
if (actual_count === 0 && this.partition <= 3) {
return 12 * this.get_level_multiplier() * rarity_multiplier;
}
const max_count = this.get_max_count();
if (max_count === 0)
return 0;
if (this.partition <= 3) {
const score = actual_count / max_count * rarity_multiplier * 55;
logger.debug(`[${this.partition}号位] ${logger.magenta(`${actual_count} / ${max_count} * ${rarity_multiplier} * 55 = ${score}`)}`);
return score;
}
const mainMaxStat = mainStats[this.partition]
.filter(p => this.weight[p])
.sort((a, b) => this.weight[b] - this.weight[a])[0];
const mainScore = (mainMaxStat ? 12 * (this.weight[this.userMainStat] || 0) / this.weight[mainMaxStat] : 12) * this.get_level_multiplier();
const subScore = actual_count / max_count * 43;
const score = (mainScore + subScore) * rarity_multiplier;
logger.debug(`[${this.partition}号位] ${logger.magenta(`(${mainScore} + ${subScore}) * ${rarity_multiplier} = ${score}`)}`);
return score;
}
static main(equip, weight) {
try {
return new Score(equip, weight).get_score();
}
catch (err) {
logger.error('角色驱动盘评分计算错误:', err);
return 0;
}
}
static getFinalWeight(avatar) {
let def_weight = equipScore[avatar.id];
if (!def_weight && !scoreFnc[avatar.id]) {
switch (avatar.avatar_profession) {
case 1:
def_weight = ['主C·双爆'];
break;
case 2:
def_weight = ['冲击·双爆', '冲击·攻击'];
break;
case 3:
def_weight = ['主C·异常', '辅助·异常'];
break;
case 4:
case 5:
def_weight = ['辅助·双爆', '辅助·异常'];
break;
case 6:
def_weight = ['命破·双爆'];
break;
}
}
const delRules = (rules) => {
if (rules.length === 1) {
rule_name = rules[0];
final_weight = predefinedWeights[rules[0]]?.value;
}
else {
for (const name of rules) {
if (predefinedWeights[name]?.rule(avatar)) {
rule_name = name;
final_weight = predefinedWeights[name].value;
break;
}
}
if (!final_weight) {
for (const name of rules) {
if (predefinedWeights[name]) {
rule_name = name;
final_weight = predefinedWeights[name].value;
break;
}
}
}
}
final_weight = { ...final_weight };
};
let rule_name = '默认', final_weight;
if (Array.isArray(def_weight)) {
delRules(def_weight);
}
else if (def_weight.rules) {
const { rules, ...rest } = def_weight;
delRules(rules);
if (Object.keys(rest).length) {
rule_name += '·改';
Object.assign(final_weight, rest);
}
}
else {
final_weight = def_weight;
}
final_weight = formatScoreWeight(final_weight, avatar.id);
const calc_weight = scoreFnc[avatar.id] && scoreFnc[avatar.id](avatar);
if (calc_weight) {
rule_name = calc_weight[0];
final_weight = { ...final_weight, ...formatScoreWeight(calc_weight[1], avatar.id) };
}
for (const [small, big, name] of [[11103, 11102, 'HP'], [12103, 12102, 'ATK'], [13103, 13102, 'DEF']]) {
if (final_weight[big]) {
final_weight[small] ??= +(baseValueData[small] * 100 / (baseValueData[big] * avatar.base_properties[name]) * final_weight[big]).toFixed(2);
}
}
return [rule_name, final_weight];
}
}
const predefinedWeights = {
主C·双爆: {
rule: (avatar) => {
const { ATK, CRITRate, CRITDMG, AnomalyMastery, AnomalyProficiency } = avatar.initial_properties;
return ATK > 2400 && CRITRate * 2 + CRITDMG >= 2.2 && AnomalyMastery < 150 && AnomalyProficiency < 200;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 0.75,
"防御力百分比": 0,
"冲击力": 0,
"暴击率": 1,
"暴击伤害": 1,
"穿透率": 1,
"穿透值": 0.25,
"能量自动回复": 0,
"异常精通": 0,
"异常掌控": 0,
"属性伤害加成": 1
}
},
主C·异常: {
rule: (avatar) => {
const { ATK, CRITRate, CRITDMG, AnomalyMastery, AnomalyProficiency } = avatar.initial_properties;
if (CRITRate * 2 + CRITDMG >= 2)
return false;
if (ATK < 2400)
return false;
if (AnomalyMastery >= 180 && AnomalyProficiency >= 200)
return true;
if (AnomalyMastery >= 120 && AnomalyProficiency >= 300)
return true;
if (AnomalyMastery >= 150 && AnomalyProficiency >= 250)
return true;
return false;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 0.75,
"防御力百分比": 0,
"冲击力": 0,
"暴击率": 0,
"暴击伤害": 0,
"穿透率": 1,
"穿透值": 0.25,
"能量自动回复": 0,
"异常精通": 1,
"异常掌控": 1,
"属性伤害加成": 1
}
},
命破·双爆: {
rule: (avatar) => {
return true;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 0.25,
"防御力百分比": 0,
"冲击力": 0,
"暴击率": 1,
"暴击伤害": 1,
"穿透率": 0,
"穿透值": 0,
"能量自动回复": 0,
"异常精通": 0,
"异常掌控": 0,
"属性伤害加成": 1
}
},
辅助·双爆: {
rule: (avatar) => {
const { CRITRate, CRITDMG, AnomalyProficiency } = avatar.initial_properties;
return CRITRate * 2 + CRITDMG >= 1.5 && AnomalyProficiency < 200;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 0.75,
"防御力百分比": 0,
"冲击力": 0,
"暴击率": 1,
"暴击伤害": 1,
"穿透率": 0.75,
"穿透值": 0.25,
"能量自动回复": 1,
"异常精通": 0,
"异常掌控": 0,
"属性伤害加成": 1
}
},
辅助·攻击: {
rule: (avatar) => {
const { CRITRate, CRITDMG } = avatar.initial_properties;
return CRITRate * 2 + CRITDMG >= 1.5;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 1,
"防御力百分比": 0,
"冲击力": 0,
"暴击率": 1,
"暴击伤害": 0.75,
"穿透率": 0.75,
"穿透值": 0.25,
"能量自动回复": 1,
"异常精通": 0,
"异常掌控": 0,
"属性伤害加成": 1
}
},
辅助·异常: {
rule: (avatar) => {
const { CRITRate, CRITDMG, AnomalyProficiency } = avatar.initial_properties;
return CRITRate * 2 + CRITDMG < 2 && AnomalyProficiency >= 200;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 0.75,
"防御力百分比": 0,
"冲击力": 0,
"暴击率": 0,
"暴击伤害": 0,
"穿透率": 0.75,
"穿透值": 0.25,
"能量自动回复": 1,
"异常精通": 1,
"异常掌控": 1,
"属性伤害加成": 1
}
},
冲击·双爆: {
rule: (avatar) => {
const { CRITRate, CRITDMG } = avatar.initial_properties;
return CRITRate * 2 + CRITDMG >= 1.5;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 0.75,
"防御力百分比": 0,
"冲击力": 1,
"暴击率": 1,
"暴击伤害": 1,
"穿透率": 0.75,
"穿透值": 0.25,
"能量自动回复": 0,
"异常精通": 0,
"异常掌控": 0,
"属性伤害加成": 1
}
},
冲击·攻击: {
rule: (avatar) => {
const { ATK, CRITRate, CRITDMG } = avatar.initial_properties;
return ATK > 2000 && CRITRate * 2 + CRITDMG >= 1;
},
value: {
"生命值百分比": 0,
"攻击力百分比": 1,
"防御力百分比": 0,
"冲击力": 1,
"暴击率": 1,
"暴击伤害": 0.75,
"穿透率": 0.75,
"穿透值": 0.25,
"能量自动回复": 0,
"异常精通": 0,
"异常掌控": 0,
"属性伤害加成": 1
}
},
};