Upd:角色面板属性颜色根据词条权重切换

This commit is contained in:
UCPr 2025-03-26 13:41:08 +08:00
parent 289a0a9a6d
commit 06ec5152a0
9 changed files with 74 additions and 53 deletions

View file

@ -6,7 +6,7 @@ import { nameToId } from './convert/property.js';
export const baseValueData = getMapData('EquipBaseValue'); export const baseValueData = getMapData('EquipBaseValue');
const equipScore = getMapData('EquipScore'); const equipScore = getMapData('EquipScore');
/** @type {{ [charID: string]: { [propID: string]: number } }} */ /** @type {{ [charID: string]: { [propID: string]: number } }} */
export const scoreData = {}; export const scoreData = Object.create(null);
for (const charName in equipScore) { for (const charName in equipScore) {
// 兼容原ID格式 // 兼容原ID格式
if (+charName) { if (+charName) {
@ -16,17 +16,17 @@ for (const charName in equipScore) {
const charID = charNameToID(charName); const charID = charNameToID(charName);
if (!charID) if (!charID)
continue; continue;
scoreData[charID] = {}; scoreData[charID] = Object.create(null);
for (const propName in equipScore[charName]) { for (const propName in equipScore[charName]) {
const propID = nameToId(propName); const propID = nameToId(propName);
if (!propID) if (!propID || !equipScore[charName][propName])
continue; continue;
scoreData[charID][propID] = equipScore[charName][propName]; scoreData[charID][propID] = equipScore[charName][propName];
}; };
/** 小生命、小攻击、小防御映射为大生命、大攻击、大防御的1/3 */ /** 小生命、小攻击、小防御映射为大生命、大攻击、大防御的1/3 */
for (const [small, big] of [[11103, 11102], [12103, 12102], [13103, 13102]]) { for (const [small, big] of [[11103, 11102], [12103, 12102], [13103, 13102]]) {
if (!scoreData[charID][small] && scoreData[charID][big]) { if (scoreData[charID][big]) {
scoreData[charID][small] = scoreData[charID][big] / 3; scoreData[charID][small] ??= scoreData[charID][big] / 3;
}; };
}; };
}; };
@ -38,7 +38,7 @@ for (const charName in equipScore) {
*/ */
export const hasScoreData = charID => { export const hasScoreData = charID => {
return ( return (
scoreData.hasOwnProperty(charID) && scoreData[charID] &&
Object.keys(scoreData[charID]).length > 0 Object.keys(scoreData[charID]).length > 0
); );
}; };

View file

@ -9,7 +9,7 @@ import { Equip, Weapon } from './equip.js';
import { Property } from './property.js'; import { Property } from './property.js';
import { Skill } from './skill.js'; import { Skill } from './skill.js';
import { avatar_ability } from './damage/avatar.js'; import { avatar_ability } from './damage/avatar.js';
import { hasScoreData } from '../lib/score.js'; import { hasScoreData, scoreData } from '../lib/score.js';
import _ from 'lodash'; import _ from 'lodash';
import fs from 'fs'; import fs from 'fs';
@ -433,6 +433,15 @@ export class ZZZAvatarInfo {
return result; return result;
} }
/** 面板属性label效果 */
get_label(propID) {
const base = scoreData[this.id][propID];
if (!base) return '';
return base === 1 ? 'yellow' :
base >= 0.75 ? 'blue' :
base >= 0.5 ? 'white' : '';
}
/** /**
* 获取基础资源 * 获取基础资源
* @returns {Promise<void>} * @returns {Promise<void>}

View file

@ -44,8 +44,8 @@ let depth = 0, weakMapCheck = new WeakMap();
export class BuffManager { export class BuffManager {
avatar; avatar;
buffs = []; buffs = [];
setCount = {}; setCount = Object.create(null);
defaultBuff = {}; defaultBuff = Object.create(null);
constructor(avatar) { constructor(avatar) {
this.avatar = avatar; this.avatar = avatar;
} }
@ -56,25 +56,26 @@ export class BuffManager {
} }
if (!buff.name && (buff.source || this.defaultBuff.source) === 'Set' && this.defaultBuff.name && typeof buff.check === 'number') if (!buff.name && (buff.source || this.defaultBuff.source) === 'Set' && this.defaultBuff.name && typeof buff.check === 'number')
buff.name = this.defaultBuff.name + buff.check; buff.name = this.defaultBuff.name + buff.check;
const oriBuff = buff;
buff = _.merge({ buff = _.merge({
status: true, status: true,
isForever: false, isForever: false,
is: {}, is: Object.create(null),
...this.defaultBuff ...this.defaultBuff
}, buff); }, buff);
if (buff.isForever) if (buff.isForever)
buff.is.forever = true; buff.is.forever = true;
if (buff.range && !Array.isArray(buff.range)) if (buff.range && !Array.isArray(buff.range))
buff.range = [buff.range]; buff.range = oriBuff.range = [buff.range];
if (!buff.source) { if (!buff.source) {
if (buff.name.includes('核心') || buff.name.includes('天赋')) if (buff.name.includes('核心') || buff.name.includes('天赋'))
buff.source = 'Talent'; buff.source = oriBuff.source = 'Talent';
else if (buff.name.includes('额外能力')) else if (buff.name.includes('额外能力'))
buff.source = 'Addition'; buff.source = oriBuff.source = 'Addition';
else if (buff.name.includes('影')) else if (buff.name.includes('影'))
buff.source = 'Rank'; buff.source = oriBuff.source = 'Rank';
else if (buff.name.includes('技')) else if (buff.name.includes('技'))
buff.source = 'Skill'; buff.source = oriBuff.source = 'Skill';
} }
if (!buff.name || !buff.value || !buff.source || !buffTypeEnum[buffTypeEnum[buff.type]]) if (!buff.name || !buff.value || !buff.source || !buffTypeEnum[buffTypeEnum[buff.type]])
return logger.warn('无效buff', buff); return logger.warn('无效buff', buff);
@ -89,7 +90,7 @@ export class BuffManager {
buff.check = ({ avatar, buffM, calc }) => professionCheck(avatar) && (!oriCheck || oriCheck({ avatar, buffM, calc })); buff.check = ({ avatar, buffM, calc }) => professionCheck(avatar) && (!oriCheck || oriCheck({ avatar, buffM, calc }));
} }
else if (buff.source === 'Rank') { else if (buff.source === 'Rank') {
buff.check ??= +buff.name.match(/\d/)?.[0]; buff.check ??= oriBuff.check = +buff.name.match(/\d/)?.[0];
} }
this.buffs.push(buff); this.buffs.push(buff);
return this.buffs; return this.buffs;

View file

@ -131,16 +131,16 @@ export class BuffManager {
readonly avatar: ZZZAvatarInfo readonly avatar: ZZZAvatarInfo
readonly buffs: buff[] = [] readonly buffs: buff[] = []
/** 套装计数 */ /** 套装计数 */
setCount: { [name: string]: number } = {} setCount: { [name: string]: number } = Object.create(null)
defaultBuff: { [key in keyof buff]?: buff[key] } = {} defaultBuff: { [key in keyof buff]?: buff[key] } = Object.create(null)
constructor(avatar: ZZZAvatarInfo) { constructor(avatar: ZZZAvatarInfo) {
this.avatar = avatar this.avatar = avatar
} }
/** 注册buff */ /** 注册并格式化buff */
new(buff: buff): buff[] new(buff: buff): buff[]
/** 注册buffs */ /** 注册并格式化buffs */
new(buffs: buff[]): buff[] new(buffs: buff[]): buff[]
new(buff: buff | buff[]) { new(buff: buff | buff[]) {
if (Array.isArray(buff)) { if (Array.isArray(buff)) {
@ -150,21 +150,22 @@ export class BuffManager {
// 简化参数 // 简化参数
if (!buff.name && (buff.source || this.defaultBuff.source) === 'Set' && this.defaultBuff.name && typeof buff.check === 'number') if (!buff.name && (buff.source || this.defaultBuff.source) === 'Set' && this.defaultBuff.name && typeof buff.check === 'number')
buff.name = this.defaultBuff.name + buff.check buff.name = this.defaultBuff.name + buff.check
const oriBuff = buff
buff = _.merge({ buff = _.merge({
status: true, status: true,
isForever: false, isForever: false,
is: {}, is: Object.create(null),
...this.defaultBuff ...this.defaultBuff
}, buff) }, buff)
if (buff.isForever) if (buff.isForever)
buff.is.forever = true buff.is.forever = true
if (buff.range && !Array.isArray(buff.range)) if (buff.range && !Array.isArray(buff.range))
buff.range = [buff.range] buff.range = oriBuff.range = [buff.range]
if (!buff.source) { if (!buff.source) {
if (buff.name.includes('核心') || buff.name.includes('天赋')) buff.source = 'Talent' if (buff.name.includes('核心') || buff.name.includes('天赋')) buff.source = oriBuff.source = 'Talent'
else if (buff.name.includes('额外能力')) buff.source = 'Addition' else if (buff.name.includes('额外能力')) buff.source = oriBuff.source = 'Addition'
else if (buff.name.includes('影')) buff.source = 'Rank' else if (buff.name.includes('影')) buff.source = oriBuff.source = 'Rank'
else if (buff.name.includes('技')) buff.source = 'Skill' else if (buff.name.includes('技')) buff.source = oriBuff.source = 'Skill'
} }
if (!buff.name || !buff.value || !buff.source || !buffTypeEnum[buffTypeEnum[buff.type]]) if (!buff.name || !buff.value || !buff.source || !buffTypeEnum[buffTypeEnum[buff.type]])
return logger.warn('无效buff', buff) return logger.warn('无效buff', buff)
@ -177,8 +178,9 @@ export class BuffManager {
} }
const oriCheck = typeof buff.check === 'function' && buff.check const oriCheck = typeof buff.check === 'function' && buff.check
buff.check = ({ avatar, buffM, calc }) => professionCheck(avatar) && (!oriCheck || oriCheck({ avatar, buffM, calc })) buff.check = ({ avatar, buffM, calc }) => professionCheck(avatar) && (!oriCheck || oriCheck({ avatar, buffM, calc }))
// 影画buff影画数检查
} else if (buff.source === 'Rank') { } else if (buff.source === 'Rank') {
buff.check ??= +buff.name.match(/\d/)!?.[0] buff.check ??= oriBuff.check = +buff.name.match(/\d/)!?.[0]
} }
this.buffs.push(buff) this.buffs.push(buff)
return this.buffs return this.buffs

View file

@ -8,10 +8,10 @@ export class Calculator {
buffM; buffM;
avatar; avatar;
skills = []; skills = [];
cache = {}; cache = Object.create(null);
props = {}; props = Object.create(null);
skill; skill;
defaultSkill = {}; defaultSkill = Object.create(null);
enemy; enemy;
constructor(buffM) { constructor(buffM) {
this.buffM = buffM; this.buffM = buffM;
@ -38,16 +38,17 @@ export class Calculator {
skill.forEach(s => this.new(s)); skill.forEach(s => this.new(s));
return this.skills; return this.skills;
} }
const oriSkill = skill;
skill = _.merge({ skill = _.merge({
...this.defaultSkill ...this.defaultSkill
}, skill); }, skill);
if (!skill.element) if (!skill.element)
skill.element = elementType2element(this.avatar.element_type); skill.element = oriSkill.element = elementType2element(this.avatar.element_type);
if (!skill.name || !skill.type) if (!skill.name || !skill.type)
return logger.warn('无效skill', skill); return logger.warn('无效skill', skill);
if (skill.check && +skill.check) { if (skill.check && +skill.check) {
const num = skill.check; const num = skill.check;
skill.check = ({ avatar }) => avatar.rank >= num; skill.check = oriSkill.check = ({ avatar }) => avatar.rank >= num;
} }
this.skills.push(skill); this.skills.push(skill);
return this.skills; return this.skills;
@ -70,13 +71,13 @@ export class Calculator {
logger.debug('自定义计算最终伤害:', dmg.result); logger.debug('自定义计算最终伤害:', dmg.result);
return dmg; return dmg;
} }
const props = this.props = skill.props || {}; const props = this.props = skill.props || Object.create(null);
const usefulBuffs = this.buffM.filter({ const usefulBuffs = this.buffM.filter({
element: skill.element, element: skill.element,
range: [skill.type], range: [skill.type],
redirect: skill.redirect redirect: skill.redirect
}, this); }, this);
const areas = {}; const areas = Object.create(null);
if (skill.before) if (skill.before)
skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas }); skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas });
const isAnomaly = typeof anomalyEnum[skill.type] === 'number'; const isAnomaly = typeof anomalyEnum[skill.type] === 'number';

View file

@ -129,11 +129,11 @@ export class Calculator {
readonly buffM: BuffManager readonly buffM: BuffManager
readonly avatar: ZZZAvatarInfo readonly avatar: ZZZAvatarInfo
readonly skills: skill[] = [] readonly skills: skill[] = []
private cache: { [type: string]: damage } = {} private cache: { [type: string]: damage } = Object.create(null)
private props: Exclude<damage['props'], undefined> = {} private props: Exclude<damage['props'], undefined> = Object.create(null)
/** 当前正在计算的技能 */ /** 当前正在计算的技能 */
skill: skill skill: skill
defaultSkill: { [key in keyof skill]?: skill[key] } = {} defaultSkill: { [key in keyof skill]?: skill[key] } = Object.create(null)
enemy: enemy enemy: enemy
constructor(buffM: BuffManager) { constructor(buffM: BuffManager) {
@ -161,23 +161,24 @@ export class Calculator {
} }
} }
/** 注册skill */ /** 注册并格式化skill */
new(skill: skill): skill[] new(skill: skill): skill[]
/** 注册skills */ /** 注册并格式化skills */
new(skills: skill[]): skill[] new(skills: skill[]): skill[]
new(skill: skill | skill[]) { new(skill: skill | skill[]) {
if (Array.isArray(skill)) { if (Array.isArray(skill)) {
skill.forEach(s => this.new(s)) skill.forEach(s => this.new(s))
return this.skills return this.skills
} }
const oriSkill = skill
skill = _.merge({ skill = _.merge({
...this.defaultSkill ...this.defaultSkill
}, skill) }, skill)
if (!skill.element) skill.element = elementType2element(this.avatar.element_type) if (!skill.element) skill.element = oriSkill.element = elementType2element(this.avatar.element_type)
if (!skill.name || !skill.type) return logger.warn('无效skill', skill) if (!skill.name || !skill.type) return logger.warn('无效skill', skill)
if (skill.check && +skill.check) { if (skill.check && +skill.check) {
const num = skill.check as unknown as number const num = skill.check as unknown as number
skill.check = ({ avatar }) => avatar.rank >= num skill.check = oriSkill.check = ({ avatar }) => avatar.rank >= num
} }
this.skills.push(skill) this.skills.push(skill)
return this.skills return this.skills
@ -208,14 +209,14 @@ export class Calculator {
logger.debug('自定义计算最终伤害:', dmg.result) logger.debug('自定义计算最终伤害:', dmg.result)
return dmg return dmg
} }
const props = this.props = skill.props || {} const props = this.props = skill.props || Object.create(null)
/** 缩小筛选范围 */ /** 缩小筛选范围 */
const usefulBuffs = this.buffM.filter({ const usefulBuffs = this.buffM.filter({
element: skill.element, element: skill.element,
range: [skill.type], range: [skill.type],
redirect: skill.redirect redirect: skill.redirect
}, this) }, this)
const areas = {} as damage['areas'] const areas = Object.create(null) as damage['areas']
if (skill.before) skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas }) if (skill.before) skill.before({ avatar: this.avatar, calc: this, usefulBuffs, skill, props, areas })
const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number' const isAnomaly = typeof anomalyEnum[skill.type as anomaly] === 'number'
if (!areas.BasicArea) { if (!areas.BasicArea) {

View file

@ -328,6 +328,9 @@
.card .basic .info .property_info .list .properties .label.blue { .card .basic .info .property_info .list .properties .label.blue {
color: rgb(65, 147, 237); color: rgb(65, 147, 237);
} }
.card .basic .info .property_info .list .properties .label.white {
color: rgb(233, 233, 233);
}
.card .basic .info .property_info .list .properties .value { .card .basic .info .property_info .list .properties .value {
flex-grow: 0; flex-grow: 0;
flex-shrink: 0; flex-shrink: 0;

View file

@ -43,52 +43,52 @@
<div class="list"> <div class="list">
<div class="properties"> <div class="properties">
<div class="prop-icon hpmax"></div> <div class="prop-icon hpmax"></div>
<div class="label yellow">生命值</div> <div class="label {{charData.get_label(11102)}}">生命值</div>
<div class="value">{{basic_properties.hpmax.final}}</div> <div class="value">{{basic_properties.hpmax.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon attack"></div> <div class="prop-icon attack"></div>
<div class="label yellow">攻击力</div> <div class="label {{charData.get_label(12102)}}">攻击力</div>
<div class="value">{{basic_properties.attack.final}}</div> <div class="value">{{basic_properties.attack.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon def"></div> <div class="prop-icon def"></div>
<div class="label yellow">防御力</div> <div class="label {{charData.get_label(13102)}}">防御力</div>
<div class="value">{{basic_properties.def.final}}</div> <div class="value">{{basic_properties.def.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon breakstun"></div> <div class="prop-icon breakstun"></div>
<div class="label">冲击力</div> <div class="label {{charData.get_label(12202)}}">冲击力</div>
<div class="value">{{basic_properties.breakstun.final}}</div> <div class="value">{{basic_properties.breakstun.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon crit"></div> <div class="prop-icon crit"></div>
<div class="label blue">暴击率</div> <div class="label {{charData.get_label(20103)}}">暴击率</div>
<div class="value">{{basic_properties.crit.final}}</div> <div class="value">{{basic_properties.crit.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon critdam"></div> <div class="prop-icon critdam"></div>
<div class="label blue">暴击伤害</div> <div class="label {{charData.get_label(21103)}}">暴击伤害</div>
<div class="value">{{basic_properties.critdam.final}}</div> <div class="value">{{basic_properties.critdam.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon elementabnormalpower"></div> <div class="prop-icon elementabnormalpower"></div>
<div class="label">异常掌控</div> <div class="label {{charData.get_label(31403)}}">异常掌控</div>
<div class="value">{{basic_properties.elementabnormalpower.final}}</div> <div class="value">{{basic_properties.elementabnormalpower.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon elementmystery"></div> <div class="prop-icon elementmystery"></div>
<div class="label">异常精通</div> <div class="label {{charData.get_label(31203)}}">异常精通</div>
<div class="value">{{basic_properties.elementmystery.final}}</div> <div class="value">{{basic_properties.elementmystery.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon penratio"></div> <div class="prop-icon penratio"></div>
<div class="label">穿透率</div> <div class="label {{charData.get_label(23103)}}">穿透率</div>
<div class="value">{{basic_properties.penratio.final}}</div> <div class="value">{{basic_properties.penratio.final}}</div>
</div> </div>
<div class="properties"> <div class="properties">
<div class="prop-icon sprecover"></div> <div class="prop-icon sprecover"></div>
<div class="label">能量回复</div> <div class="label {{charData.get_label(30502)}}">能量回复</div>
<div class="value">{{basic_properties.sprecover.final}}</div> <div class="value">{{basic_properties.sprecover.final}}</div>
</div> </div>
</div> </div>

View file

@ -231,6 +231,10 @@
&.blue { &.blue {
color: rgb(65, 147, 237); color: rgb(65, 147, 237);
} }
&.white {
color: rgb(233, 233, 233);
}
} }
.value { .value {