From 9b7e609f6a31d5ea20bec8745037d09536026ba0 Mon Sep 17 00:00:00 2001 From: UCPr <2032385471@qq.com> Date: Fri, 2 May 2025 18:01:04 +0800 Subject: [PATCH] =?UTF-8?q?feature=EF=BC=9A%XX=E4=BC=A4=E5=AE=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/damage.js | 71 +++ apps/help.js | 2 +- lib/convert/property.js | 33 +- model/avatar.js | 11 +- model/damage/Calculator.js | 123 ++++- model/damage/Calculator.ts | 166 ++++++- model/damage/avatar.js | 6 +- model/damage/avatar.ts | 8 +- model/damage/character/11号/calc.js | 6 +- model/damage/character/「扳机」/calc.js | 1 + model/damage/character/伊芙琳/calc.js | 2 +- model/damage/character/可琳/calc.js | 7 +- model/damage/character/悠真/calc.js | 2 +- model/damage/character/星见雅/calc.js | 2 + model/damage/character/月城柳/calc.js | 1 + model/damage/character/朱鸢/calc.js | 1 + model/damage/character/柏妮思/calc.js | 3 +- model/damage/character/格莉丝/calc.js | 2 +- model/damage/character/波可娜/calc.js | 1 + model/damage/character/派派/calc.js | 2 +- model/damage/character/猫又/calc.js | 2 +- model/damage/character/简/calc.js | 2 +- model/damage/character/耀嘉音/calc.js | 2 +- model/damage/character/艾莲/calc.js | 7 +- model/damage/character/莱卡恩/calc.js | 2 +- model/damage/character/莱特/calc.js | 7 +- model/damage/character/零号·安比/calc.js | 1 + model/damage/character/露西/calc.js | 2 +- resources/map/Property2Name.json | 38 +- resources/panel/card.css | 49 +- resources/panel/card.html | 10 +- resources/panel/card.scss | 30 +- resources/panel/damage.css | 557 +++++++++++++++++++++++ resources/panel/damage.html | 257 +++++++++++ resources/panel/damage.scss | 547 ++++++++++++++++++++++ 35 files changed, 1849 insertions(+), 114 deletions(-) create mode 100644 apps/damage.js create mode 100644 resources/panel/damage.css create mode 100644 resources/panel/damage.html create mode 100644 resources/panel/damage.scss diff --git a/apps/damage.js b/apps/damage.js new file mode 100644 index 0000000..e6700c6 --- /dev/null +++ b/apps/damage.js @@ -0,0 +1,71 @@ +import { getPanelOrigin, formatPanelData } from '../lib/avatar.js' +import { avatar_calc } from '../model/damage/avatar.js' +import { rulePrefix } from '../lib/common.js' +import { ZZZPlugin } from '../lib/plugin.js' +import settings from '../lib/settings.js' + +export class Damage extends ZZZPlugin { + constructor() { + super({ + name: '[ZZZ-Plugin]Damage', + dsc: 'zzzdamage', + event: 'message', + priority: settings.getConfig('priority')?.panel ?? 70, + rule: [ + { + reg: `${rulePrefix}(.+)伤害\\d*$`, + fnc: 'charDamagePanel' + } + ] + }) + } + + async charDamagePanel() { + const uid = await this.getUID() + if (!uid) { + return this.reply('UID为空') + } + const reg = new RegExp(`${rulePrefix}(.+)伤害(\\d*)$`) + const match = this.e.msg.match(reg) + if (!match) return false + const name = match[4] + const data = getPanelOrigin(uid, name) + if (!data) { + return this.reply(`未找到角色${name}的面板信息,请先刷新面板`) + } + const parsedData = formatPanelData(data) + const calc = avatar_calc(parsedData) + const damages = parsedData._damages = calc?.calc() + if (!calc || !damages?.length) return this.reply(`暂无角色${name}的伤害计算`) + let skillIndex = match[5] && match[5] - 1 + if (skillIndex && skillIndex > damages.length) + skillIndex = damages.length - 1 + else if (skillIndex < 0) + skillIndex = 0 + const differences = calc.calc_differences(damages[skillIndex]?.skill) + if (skillIndex === '') { + const _s_ = differences[0]?.[0].damage.skill + skillIndex = ((_s_ && damages.findIndex(({ skill }) => skill.name === _s_.name && skill.type === _s_.type) + 1) || damages.length) - 1 + } + const damage = damages[skillIndex] + const skill = damage.skill + await parsedData.get_detail_assets() + const finalData = { + uid, + charData: parsedData, + command: `%${parsedData.name_mi18n}伤害${skillIndex + 1}`, + damage, + damages, + differences, + skill: { + ...skill, + index: skillIndex + } + } + const image = await this.render('panel/damage.html', finalData, { retType: 'base64' }) + const res = await this.reply(image) + if (res?.message_id && parsedData.role_icon) + await redis.set(`ZZZ:PANEL:IMAGE:${res.message_id}`, parsedData.role_icon, { EX: 3600 * 3 }) + } + +} diff --git a/apps/help.js b/apps/help.js index 102a6d2..a7ee45a 100644 --- a/apps/help.js +++ b/apps/help.js @@ -31,7 +31,7 @@ const helpData = [ desc: '查看玩家的角色和邦布列表', needCK: true, needSK: false, - commands: ['card', '卡片', '个人信息'], + commands: ['card', '卡片', '角色','个人信息'], }, { title: '便签', diff --git a/lib/convert/property.js b/lib/convert/property.js index 53a27fc..6f58804 100644 --- a/lib/convert/property.js +++ b/lib/convert/property.js @@ -65,16 +65,39 @@ export const idToName = id => { }; /** - * 获取属性二字简称 + * 获取属性2字简称 * @param {string | number} id 属性id - * @returns {string | null} + * @returns {string} */ -export const idToShortName = id => { +export const idToShortName2 = id => { const result = propertyData[id]; - if (!result) return null; + if (!result) return ''; return result[2]; }; +/** + * 获取属性2~3字简称 + * @param {string | number} id 属性id + * @returns {string} + */ +export const idToShortName3 = id => { + const result = propertyData[id]; + if (!result) return ''; + return result[3]; +}; + +/** + * 获取属性2~3字简称 + * @param {string | number} id 属性id + * @returns {string} + */ +export const nameToShortName3 = propName => { + for (const id in propertyData) { + if (propertyData[id]?.[1] === propName) return propertyData[id][3]; + }; + return propName; +}; + /** * 属性名转id * @param {string} propName 属性名 @@ -82,6 +105,6 @@ export const idToShortName = id => { export const nameToId = (propName) => { for (const id in propertyData) { if (propertyData[id]?.[1] === propName) return Number(id); - } + }; return null; }; diff --git a/model/avatar.js b/model/avatar.js index 8b8ba44..a33b675 100644 --- a/model/avatar.js +++ b/model/avatar.js @@ -5,8 +5,8 @@ import { getSquareAvatar, } from '../lib/download.js'; import { baseValueData, formatScoreWeight, scoreWeight } from '../lib/score.js'; -import { avatar_ability, scoreFnc } from './damage/avatar.js'; -import { idToShortName } from '../lib/convert/property.js'; +import { avatar_calc, scoreFnc } from './damage/avatar.js'; +import { idToShortName2 } from '../lib/convert/property.js'; import { imageResourcesPath } from '../lib/path.js'; import { Equip, Weapon } from './equip.js'; import { Property } from './property.js'; @@ -352,9 +352,10 @@ export class ZZZAvatarInfo { } } - /** @type {import("./damage/Calculator.ts").damage} */ + /** @type {import("./damage/Calculator.ts").damage[]} */ get damages() { - return avatar_ability(this); + if (this._damages) return this._damages; + return this._damages = avatar_calc(this)?.calc(); } /** @type {number|boolean} */ @@ -449,7 +450,7 @@ export class ZZZAvatarInfo { const propID = property.property_id stats[propID] ??= { id: propID, - name: idToShortName(propID), + name: idToShortName2(propID), weight: this.scoreWeight[propID] || 0, value: '0', count: 0 diff --git a/model/damage/Calculator.js b/model/damage/Calculator.js index 7c7ab64..db4f782 100644 --- a/model/damage/Calculator.js +++ b/model/damage/Calculator.js @@ -1,13 +1,27 @@ -import { getMapData } from '../../utils/file.js'; import { elementEnum, anomalyEnum } from './BuffManager.js'; +import * as prop from '../../lib/convert/property.js'; +import { getMapData } from '../../utils/file.js'; import { charData } from './avatar.js'; import _ from 'lodash'; const elementType2element = (elementType) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]]; +const baseValueData = { + "生命值百分比": [0.03, '3.0%'], + "生命值": [112, '112'], + "攻击力百分比": [0.03, '3.0%'], + "攻击力": [19, '19'], + "防御力百分比": [0.048, '4.8%'], + "防御力": [15, '15'], + "暴击率": [0.024, '2.4%'], + "暴击伤害": [0.048, '4.8%'], + "穿透值": [9, '9'], + "异常精通": [9, '9'] +}; const AnomalyData = getMapData('AnomalyData'); export class Calculator { buffM; avatar; skills = []; + usefulBuffs = []; cache = Object.create(null); props = {}; skill; @@ -68,6 +82,7 @@ export class Calculator { logger.debug(`${logger.green(skill.type)}${skill.name}伤害计算:`); if (skill.dmg) { const dmg = skill.dmg(this); + dmg.skill ||= skill; logger.debug('自定义计算最终伤害:', dmg.result); return dmg; } @@ -114,15 +129,18 @@ export class Calculator { areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs); areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs); areas.LevelArea ??= this.get_LevelArea(); - props.异常暴击率 = this.get_AnomalyCRITRate(skill, usefulBuffs); - props.异常暴击伤害 = this.get_AnomalyCRITDMG(skill, usefulBuffs); - areas.CriticalArea ??= 1 + props.异常暴击率 * (props.异常暴击伤害 - 1); + if (skill.type !== '紊乱') { + props.异常暴击率 ??= this.get_AnomalyCRITRate(skill, usefulBuffs); + props.异常暴击伤害 ??= this.get_AnomalyCRITDMG(skill, usefulBuffs); + areas.CriticalArea ??= 1 + props.异常暴击率 * (props.异常暴击伤害 - 1); + } } else { - props.暴击率 = this.get_CRITRate(skill, usefulBuffs); - props.暴击伤害 = this.get_CRITDMG(skill, usefulBuffs); + props.暴击率 ??= this.get_CRITRate(skill, usefulBuffs); + props.暴击伤害 ??= this.get_CRITDMG(skill, usefulBuffs); areas.CriticalArea ??= 1 + props.暴击率 * (props.暴击伤害 - 1); } + areas.CriticalArea ??= 1; logger.debug(`暴击期望:${areas.CriticalArea}`); areas.BoostArea ??= this.get_BoostArea(skill, usefulBuffs); areas.VulnerabilityArea ??= this.get_VulnerabilityArea(skill, usefulBuffs); @@ -138,7 +156,8 @@ export class Calculator { critDMG: BasicArea * 暴击伤害 * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, expectDMG: BasicArea * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea }; - const damage = { skill, props, areas, result }; + const damage = { skill, usefulBuffs: _.sortBy(this.usefulBuffs, ['type', 'value']).reverse(), props, areas, result }; + this.usefulBuffs = []; if (skill.after) { damage.add = (d) => { if (typeof d === 'string') @@ -158,6 +177,7 @@ export class Calculator { damage.fnc(v => v * n); }; skill.after({ avatar: this.avatar, calc: this, usefulBuffs, skill, damage }); + delete damage.add, delete damage.fnc, delete damage.x; } logger.debug('最终伤害:', result); if (!skill.banCache) @@ -175,6 +195,86 @@ export class Calculator { } }).filter(v => v && v.result?.expectDMG && !v.skill?.isHide); } + calc_differences(skill, types) { + if (!skill) { + skill = this.skills.find((skill) => skill.isMain) + || this.calc().sort((a, b) => b.result.expectDMG - a.result.expectDMG)[0]?.skill; + } + if (!types || !types.length) { + types = Object.entries(this.avatar.scoreWeight) + .reduce((acc, [id, weight]) => { + if (weight > 0) { + const type = prop.idToName(id); + if (type && baseValueData[type]) { + acc.push({ type, weight }); + } + } + return acc; + }, []) + .slice(0, 7) + .sort((a, b) => b.weight - a.weight) + .map(({ type }) => type); + } + const base = {}; + types.forEach(type => base[type] = type.includes('百分比') ? this.avatar.base_properties[type.includes('攻击力') ? 'ATK' : type.includes('生命值') ? 'HP' : 'DEF'] * baseValueData[type][0] : baseValueData[type][0]); + logger.debug(logger.red('词条变化值:'), base); + const buffs = types.map(t => ({ + name: t, + shortName: prop.nameToShortName3(t), + type: t.replace('百分比', ''), + value: base[t], + valueBase: baseValueData[t][1] + })); + return this._calc_differences(skill, buffs); + } + _calc_differences(skill, buffs) { + if (typeof skill === 'string') { + const MySkill = this.skills.find(s => s.type === skill); + if (!MySkill) + return []; + return this._calc_differences(MySkill, buffs); + } + const oriDamage = this.calc_skill(skill); + const result = []; + for (const i_del in buffs) { + result[i_del] = []; + const data_del = buffs[i_del]; + const { type: type_del, name: name_del = type_del, value: value_del } = data_del; + logger.debug(logger.blue(`差异计算:${name_del}`)); + this.buffM.buffs.push({ + name: logger.green(`差异计算:${name_del}`), + type: type_del, + value: ({ calc }) => -calc.calc_value(value_del) + }); + for (const i_add in buffs) { + const data_add = buffs[i_add]; + data_add.name ??= data_add.type; + const { type: type_add, name: name_add = type_add, value: value_add } = data_add; + const data = result[i_del][i_add] = { + add: data_add, + del: data_del, + damage: oriDamage, + difference: 0 + }; + if (name_del === name_add) + continue; + logger.debug(logger.yellow(`差异计算:${name_del}->${name_add}`)); + this.cache = Object.create(null); + this.buffM.buffs.push({ + name: logger.green(`差异计算:${name_del}->${name_add}`), + type: type_add, + value: value_add + }); + const newDamage = this.calc_skill(skill); + this.buffM.buffs.pop(); + data.damage = newDamage; + data.difference = newDamage.result.expectDMG - oriDamage.result.expectDMG; + logger.debug(logger.magenta(`差异计算:${name_del}->${name_add} 伤害变化:${data.difference}`)); + } + this.buffM.buffs.pop(); + } + return result; + } default(param, value) { if (typeof param === 'object') { this.defaultSkill = param; @@ -271,14 +371,16 @@ export class Calculator { }, this).reduce((previousValue, buff) => { const { value } = buff; let add = 0; - if (isRatio && typeof value === 'number' && value < 1) { + if (isRatio && typeof value === 'number' && Math.abs(value) < 1) { add = value * initial; } else { add = this.calc_value(value, buff); - if (add < 1 && isRatio && (typeof value === 'string' || Array.isArray(value))) + if (Math.abs(add) < 1 && isRatio && (typeof value === 'string' || Array.isArray(value))) add *= initial; } + if (!this.usefulBuffs.find(b => b.name === buff.name && b.type === buff.type && add === buff.value)) + this.usefulBuffs.push({ ...buff, value: add }); logger.debug(`\tBuff:${buff.name}对${buff.range || '全类型'}增加${add}${buff.element || ''}${type}`); return previousValue + add; }, initial); @@ -317,7 +419,8 @@ export class Calculator { return VulnerabilityArea; } get_ResistanceArea(skill, usefulBuffs) { - const ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs); + let ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs); + ResistanceArea = Math.min(2, ResistanceArea); logger.debug(`抗性区:${ResistanceArea}`); return ResistanceArea; } diff --git a/model/damage/Calculator.ts b/model/damage/Calculator.ts index d3bb1ab..f3b6203 100644 --- a/model/damage/Calculator.ts +++ b/model/damage/Calculator.ts @@ -1,7 +1,8 @@ import type { BuffManager, anomaly, buff, buffType, element } from './BuffManager.ts' import type { ZZZAvatarInfo } from '../avatar.js' -import { getMapData } from '../../utils/file.js' import { elementEnum, anomalyEnum } from './BuffManager.js' +import * as prop from '../../lib/convert/property.js' +import { getMapData } from '../../utils/file.js' import { charData } from './avatar.js' import _ from 'lodash' @@ -29,6 +30,8 @@ export interface skill { * 当为数组类型时(多类型共存),满足数组内其一类型即可,判断规则同上 */ redirect?: string | string[] | anomaly[] | "追加攻击"[] + /** 是否为主要技能。`true`时%XX伤害 默认计算该技能 */ + isMain?: boolean /** 角色面板伤害统计中是否隐藏显示 */ isHide?: boolean /** 禁用伤害计算cache */ @@ -41,14 +44,19 @@ export interface skill { }) => boolean) /** 自定义计算逻辑 */ dmg?: (calc: Calculator) => damage - /** 伤害计算前调用,可自由定义各属性等 */ + /** + * 伤害计算前调用,可自由定义各属性等 + * 此操作只作用于当前技能 + */ before?: ({ avatar, calc, usefulBuffs, skill, props, areas }: { avatar: ZZZAvatarInfo calc: Calculator usefulBuffs: buff[] /** 技能自身 */ skill: skill + /** 属性数据。设置后不会更改 */ props: damage['props'] + /** 乘区数据。设置后不会更改 */ areas: damage['areas'] }) => void /** 伤害计算后调用,可对结果进行修改等 */ @@ -65,6 +73,8 @@ export interface skill { export interface damage { /** 技能类型 */ skill: skill + /** 有益Buffs */ + usefulBuffs: buff[] /** 技能属性 */ props?: skill['props'] /** 各乘区数据 */ @@ -102,6 +112,21 @@ export interface damage { const elementType2element = (elementType: number) => elementEnum[[0, 1, 2, 3, -1, 4][elementType - 200]] as element +const baseValueData = { + "生命值百分比": [0.03, '3.0%'], + "生命值": [112, '112'], + "攻击力百分比": [0.03, '3.0%'], + "攻击力": [19, '19'], + "防御力百分比": [0.048, '4.8%'], + "防御力": [15, '15'], + "暴击率": [0.024, '2.4%'], + "暴击伤害": [0.048, '4.8%'], + "穿透值": [9, '9'], + "异常精通": [9, '9'] +} as const + +type statKeys = keyof typeof baseValueData + const AnomalyData = getMapData('AnomalyData') as { name: string, element: element, @@ -129,6 +154,7 @@ export class Calculator { readonly buffM: BuffManager readonly avatar: ZZZAvatarInfo readonly skills: skill[] = [] + private usefulBuffs: buff[] = [] private cache: { [type: string]: damage } = Object.create(null) private props: Exclude = {} /** 当前正在计算的技能 */ @@ -206,6 +232,7 @@ export class Calculator { logger.debug(`${logger.green(skill.type)}${skill.name}伤害计算:`) if (skill.dmg) { const dmg = skill.dmg(this) + dmg.skill ||= skill logger.debug('自定义计算最终伤害:', dmg.result) return dmg } @@ -248,14 +275,17 @@ export class Calculator { areas.AnomalyProficiencyArea ??= this.get_AnomalyProficiencyArea(skill, usefulBuffs) areas.AnomalyBoostArea ??= this.get_AnomalyBoostArea(skill, usefulBuffs) areas.LevelArea ??= this.get_LevelArea() - props.异常暴击率 = this.get_AnomalyCRITRate(skill, usefulBuffs) - props.异常暴击伤害 = this.get_AnomalyCRITDMG(skill, usefulBuffs) - areas.CriticalArea ??= 1 + props.异常暴击率! * (props.异常暴击伤害! - 1) + if (skill.type !== '紊乱') { // 紊乱暂无异常暴击区 + props.异常暴击率 ??= this.get_AnomalyCRITRate(skill, usefulBuffs) + props.异常暴击伤害 ??= this.get_AnomalyCRITDMG(skill, usefulBuffs) + areas.CriticalArea ??= 1 + props.异常暴击率! * (props.异常暴击伤害! - 1) + } } else { - props.暴击率 = this.get_CRITRate(skill, usefulBuffs) - props.暴击伤害 = this.get_CRITDMG(skill, usefulBuffs) + props.暴击率 ??= this.get_CRITRate(skill, usefulBuffs) + props.暴击伤害 ??= this.get_CRITDMG(skill, usefulBuffs) areas.CriticalArea ??= 1 + props.暴击率! * (props.暴击伤害! - 1) } + areas.CriticalArea ??= 1 logger.debug(`暴击期望:${areas.CriticalArea}`) areas.BoostArea ??= this.get_BoostArea(skill, usefulBuffs) areas.VulnerabilityArea ??= this.get_VulnerabilityArea(skill, usefulBuffs) @@ -274,7 +304,8 @@ export class Calculator { critDMG: BasicArea * 暴击伤害! * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea, expectDMG: BasicArea * CriticalArea * BoostArea * VulnerabilityArea * ResistanceArea * DefenceArea } - const damage: damage = { skill, props, areas, result } + const damage: damage = { skill, usefulBuffs: _.sortBy(this.usefulBuffs, ['type', 'value']).reverse(), props, areas, result } + this.usefulBuffs = [] if (skill.after) { damage.add = (d) => { if (typeof d === 'string') d = this.calc_skill(d) @@ -292,6 +323,7 @@ export class Calculator { damage.fnc!(v => v * n) } skill.after({ avatar: this.avatar, calc: this, usefulBuffs, skill, damage }) + delete damage.add, delete damage.fnc, delete damage.x } logger.debug('最终伤害:', result) if (!skill.banCache) this.cache[skill.type] = damage @@ -310,6 +342,113 @@ export class Calculator { }).filter(v => v && v.result?.expectDMG && !v.skill?.isHide) as damage[] } + /** + * 计算词条伤害差异 + * @param types 需进行比较的词条数组 + */ + calc_differences(skill?: skill, types?: statKeys[]) { + if (!skill) { + skill = this.skills.find((skill) => skill.isMain) // 主技能 + || this.calc().sort((a, b) => b.result.expectDMG - a.result.expectDMG)[0]?.skill // 伤害最高技能 + } + // 未指定types时,筛选评分权重大于0的词条进行差异计算 + if (!types || !types.length) { + types = Object.entries(this.avatar.scoreWeight) + .reduce((acc: { type: statKeys, weight: number }[], [id, weight]) => { + if (weight > 0) { + const type = prop.idToName(id) as statKeys + if (type && baseValueData[type]) { + acc.push({ type, weight }) + } + } + return acc + }, []) + .slice(0, 7) // 默认最多7个 + .sort((a, b) => b.weight - a.weight) // 按权重从大到小排序 + .map(({ type }) => type) + } + const base: { [type: string]: number } = {} + types.forEach(type => base[type] = type.includes('百分比') ? this.avatar.base_properties[ + type.includes('攻击力') ? 'ATK' : type.includes('生命值') ? 'HP' : 'DEF' + ] * baseValueData[type][0] : baseValueData[type][0]) + logger.debug(logger.red('词条变化值:'), base) + const buffs = types.map(t => ({ + name: t, + shortName: prop.nameToShortName3(t), + type: t.replace('百分比', '') as buff['type'], + value: base[t], + valueBase: baseValueData[t][1] + })) + return this._calc_differences(skill, buffs) + } + + /* 计算已注册技能差异 */ + _calc_differences( + skill: string, + buffs: B[] + ): { add: B, del: B, damage: damage, difference: number }[][] + /* 计算技能差异 */ + _calc_differences( + skill: skill, + buffs: B[] + ): { add: B, del: B, damage: damage, difference: number }[][] + /** + * 以buff形式两两组合进行差异计算 + * @param buffs 需要进行组合差异计算的buffs + * @returns 差异计算结果。`buffs.length维`结果数组 + */ + _calc_differences( + skill: skill['type'] | skill, + buffs: B[] + ): { add: B, del: B, damage: damage, difference: number }[][] { + if (typeof skill === 'string') { + const MySkill = this.skills.find(s => s.type === skill) + if (!MySkill) return [] + return this._calc_differences(MySkill, buffs) + } + const oriDamage = this.calc_skill(skill) + const result: { del: B, add: B, damage: damage, difference: number }[][] = [] + for (const i_del in buffs) { + result[i_del] = [] + const data_del = buffs[i_del] + const { type: type_del, name: name_del = type_del, value: value_del } = data_del + logger.debug(logger.blue(`差异计算:${name_del}`)) + // @ts-ignore + this.buffM.buffs.push({ + name: logger.green(`差异计算:${name_del}`), + type: type_del, + value: ({ calc }) => -calc.calc_value(value_del) // 转为负值 + }) + for (const i_add in buffs) { + const data_add = buffs[i_add] + data_add.name ??= data_add.type + const { type: type_add, name: name_add = type_add, value: value_add } = data_add + const data = result[i_del][i_add] = { + add: data_add, + del: data_del, + damage: oriDamage, + difference: 0 + } + if (name_del === name_add) continue + logger.debug(logger.yellow(`差异计算:${name_del}->${name_add}`)) + this.cache = Object.create(null) + // @ts-ignore + this.buffM.buffs.push({ + name: logger.green(`差异计算:${name_del}->${name_add}`), + type: type_add, + value: value_add + }) + const newDamage = this.calc_skill(skill) + this.buffM.buffs.pop() + data.damage = newDamage + data.difference = newDamage.result.expectDMG - oriDamage.result.expectDMG + logger.debug(logger.magenta(`差异计算:${name_del}->${name_add} 伤害变化:${data.difference}`)) + } + this.buffM.buffs.pop() + } + return result + } + /** * 设置后续新增buff参数的默认值 * @param obj 直接覆盖默认值 @@ -414,7 +553,7 @@ export class Calculator { /** * 获取局内属性原始值 - * @param isRatio 是否支持buff.value为数值/字符串/数组类型且<1时按初始数值百分比提高处理 + * @param isRatio 是否支持buff.value为数值/字符串/数组类型且<1时按 **`初始数值`** 百分比提高处理 */ get(type: buff['type'], initial: number, skill: skill, usefulBuffs: buff[] = this.buffM.buffs, isRatio = false): number { return this.props[type] ??= this.buffM._filter(usefulBuffs, { @@ -425,13 +564,15 @@ export class Calculator { }, this).reduce((previousValue, buff) => { const { value } = buff let add = 0 - if (isRatio && typeof value === 'number' && value < 1) { // 值小于1时,认为是百分比 + if (isRatio && typeof value === 'number' && Math.abs(value) < 1) { // 绝对值小于1时,认为是百分比 add = value * initial } else { add = this.calc_value(value, buff) - if (add < 1 && isRatio && (typeof value === 'string' || Array.isArray(value))) + if (Math.abs(add) < 1 && isRatio && (typeof value === 'string' || Array.isArray(value))) add *= initial } + if (!this.usefulBuffs.find(b => b.name === buff.name && b.type === buff.type && add === buff.value)) + this.usefulBuffs.push({ ...buff, value: add }) logger.debug(`\tBuff:${buff.name}对${buff.range || '全类型'}增加${add}${buff.element || ''}${type}`) return previousValue + add }, initial) @@ -484,7 +625,8 @@ export class Calculator { /** 抗性区 */ get_ResistanceArea(skill: skill, usefulBuffs: buff[]) { - const ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs) + let ResistanceArea = this.get('无视抗性', 1 + this.enemy.resistance, skill, usefulBuffs) + ResistanceArea = Math.min(2, ResistanceArea) logger.debug(`抗性区:${ResistanceArea}`) return ResistanceArea } diff --git a/model/damage/avatar.js b/model/damage/avatar.js index 68aac6e..79a17b8 100644 --- a/model/damage/avatar.js +++ b/model/damage/avatar.js @@ -104,10 +104,10 @@ async function importFile(type, name, isWatch = false) { } } await init(); -export function avatar_ability(avatar) { +export function avatar_calc(avatar) { const m = calcFnc.character[avatar.id]; if (!m) - return []; + return; const buffM = new BuffManager(avatar); const calc = new Calculator(buffM); logger.debug('initial_properties', avatar.initial_properties); @@ -120,7 +120,7 @@ export function avatar_ability(avatar) { if (m.calc) m.calc(buffM, calc, avatar); logger.debug(`Buff*${buffM.buffs.length}:`, buffM.buffs); - return calc.calc(); + return calc; } export function weapon_buff(weapon, buffM) { const name = weapon?.name; diff --git a/model/damage/avatar.ts b/model/damage/avatar.ts index 53d8847..871709d 100644 --- a/model/damage/avatar.ts +++ b/model/damage/avatar.ts @@ -137,10 +137,10 @@ async function importFile(type: 'weapon' | 'set', name: string, isWatch = false) await init() -/** 角色计算 */ -export function avatar_ability(avatar: ZZZAvatarInfo) { +/** 角色计算实例 */ +export function avatar_calc(avatar: ZZZAvatarInfo) { const m = calcFnc.character[avatar.id] - if (!m) return [] + if (!m) return const buffM = new BuffManager(avatar) const calc = new Calculator(buffM) logger.debug('initial_properties', avatar.initial_properties) @@ -150,7 +150,7 @@ export function avatar_ability(avatar: ZZZAvatarInfo) { 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() + return calc } /** 武器加成 */ diff --git a/model/damage/character/11号/calc.js b/model/damage/character/11号/calc.js index 62b0210..097b1e0 100644 --- a/model/damage/character/11号/calc.js +++ b/model/damage/character/11号/calc.js @@ -30,7 +30,11 @@ export const buffs = [ /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ { name: '灼烧', type: '灼烧' }, - { name: '普攻:火力镇压四段', type: 'AQ4' }, + { + name: '普攻:火力镇压四段', + isMain: true, + type: 'AQ4' + }, { name: '闪避反击:逆火', type: 'CF' }, { name: '强化特殊技:盛燃烈火', type: 'EQ' }, { name: '连携技:昂扬烈焰', type: 'RL' }, diff --git a/model/damage/character/「扳机」/calc.js b/model/damage/character/「扳机」/calc.js index cf91f2c..5e8e56f 100644 --- a/model/damage/character/「扳机」/calc.js +++ b/model/damage/character/「扳机」/calc.js @@ -37,6 +37,7 @@ export const skills = [ name: '普攻:协奏狙杀·冥狱', type: 'AXQ', redirect: ['AXQ', '追加攻击'], + isMain: true, after: ({ damage }) => damage.add('AXQ0') }, { name: '闪避反击:极魂罚', type: 'CF' }, diff --git a/model/damage/character/伊芙琳/calc.js b/model/damage/character/伊芙琳/calc.js index 407c19a..528c31c 100644 --- a/model/damage/character/伊芙琳/calc.js +++ b/model/damage/character/伊芙琳/calc.js @@ -54,7 +54,7 @@ export const skills = [ type: 'EQ', after: ({ damage }) => damage.add('EQ0') }, - { name: '连携技:月辉丝·绊', type: 'RL' }, + { name: '连携技:月辉丝·绊', isMain: true, type: 'RL' }, { name: '终结技:月辉丝·弦音', type: 'RZ' }, { name: '6影月辉丝·弦', diff --git a/model/damage/character/可琳/calc.js b/model/damage/character/可琳/calc.js index 10845b3..fc9b211 100644 --- a/model/damage/character/可琳/calc.js +++ b/model/damage/character/可琳/calc.js @@ -35,7 +35,12 @@ export const skills = [ isHide: true, after: ({ damage }) => damage.x(2) }, - { name: '强化特殊技:小心裙角', type: 'EQ', after: ({ damage }) => damage.add('EQ0') }, + { + name: '强化特殊技:小心裙角', + type: 'EQ', + isMain: true, + after: ({ damage }) => damage.add('EQ0') + }, { name: '连携技:抱歉…', type: 'RL' }, { name: '终结技:非、非常抱歉!', type: 'RZ' } ] \ No newline at end of file diff --git a/model/damage/character/悠真/calc.js b/model/damage/character/悠真/calc.js index 205ef66..04ff0cc 100644 --- a/model/damage/character/悠真/calc.js +++ b/model/damage/character/悠真/calc.js @@ -36,7 +36,7 @@ export const skills = [ { name: '感电每次', type: '感电' }, { name: '普攻:穿云五段', type: 'AP5' }, { name: '普攻:落羽', type: 'AX' }, - { name: '冲刺攻击:飞弦·斩', type: 'CCQ3' }, + { name: '冲刺攻击:飞弦·斩', isMain: true, type: 'CCQ3' }, { name: '强化特殊技:地网', type: 'EQ' }, { name: '连携技:会·离', type: 'RL' }, { name: '终结技:残心', type: 'RZ' } diff --git a/model/damage/character/星见雅/calc.js b/model/damage/character/星见雅/calc.js index 34ebb0b..0b1626f 100644 --- a/model/damage/character/星见雅/calc.js +++ b/model/damage/character/星见雅/calc.js @@ -80,6 +80,7 @@ // calc.new({ // name: '蓄力攻击:三段蓄', // type: 'AX3', +// isMain: true, // after: ({ avatar, damage }) => avatar.rank >= 6 && damage.add('AX2') // }) // calc.new({ name: '强化特殊技:飞雪', type: 'EQ1' }) @@ -164,6 +165,7 @@ export const skills = [ { name: '蓄力攻击:三段蓄', type: 'AX3', + isMain: true, after: ({ avatar, damage }) => avatar.rank >= 6 && damage.add('AX2') }, { name: '强化特殊技:飞雪', type: 'EQ1' }, diff --git a/model/damage/character/月城柳/calc.js b/model/damage/character/月城柳/calc.js index 4a17164..a685228 100644 --- a/model/damage/character/月城柳/calc.js +++ b/model/damage/character/月城柳/calc.js @@ -76,6 +76,7 @@ export const skills = [ { name: '强化E极性紊乱', type: '紊乱', + isMain: true, banCache: true, before: ({ calc, areas }) => { const skill = { type: '紊乱' } diff --git a/model/damage/character/朱鸢/calc.js b/model/damage/character/朱鸢/calc.js index 157ce6d..5aa19e4 100644 --- a/model/damage/character/朱鸢/calc.js +++ b/model/damage/character/朱鸢/calc.js @@ -41,6 +41,7 @@ export const skills = [ { name: '强化特殊技:全弹连射', type: 'EQ', + isMain: true, after: ({ damage }) => damage.add('EQ2') }, { name: '连携技:歼灭模式', type: 'RL' }, diff --git a/model/damage/character/柏妮思/calc.js b/model/damage/character/柏妮思/calc.js index e506717..902d5d4 100644 --- a/model/damage/character/柏妮思/calc.js +++ b/model/damage/character/柏妮思/calc.js @@ -27,7 +27,7 @@ export const buffs = [ { name: '核心被动:燃油特调', type: '增伤', - value: ({ calc }) => Math.min(30, Math.floor(calc.get_AnomalyProficiency() / 10)) * 0.01, + value: ({ calc }) => Math.min(30, calc.get_AnomalyProficiency() / 10) * 0.01, isForever: true, range: ['TY', 'Y6Y'] }, @@ -46,6 +46,7 @@ export const skills = [ { name: '核心被动:余烬', type: 'TY', + isMain: true, redirect: 'L' }, // { name: '普攻:炽焰直调式五段', type: 'AP5' }, diff --git a/model/damage/character/格莉丝/calc.js b/model/damage/character/格莉丝/calc.js index a4da086..9f6c48a 100644 --- a/model/damage/character/格莉丝/calc.js +++ b/model/damage/character/格莉丝/calc.js @@ -16,7 +16,7 @@ export const buffs = [ /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ { name: '感电每次', type: '感电' }, - { name: '紊乱', type: '紊乱' }, + { name: '紊乱', isMain: true, type: '紊乱' }, { name: '闪避反击:违章处罚', type: 'CF' }, { name: '强化特殊技:超规工程清障', diff --git a/model/damage/character/波可娜/calc.js b/model/damage/character/波可娜/calc.js index c324c7b..7d55ca0 100644 --- a/model/damage/character/波可娜/calc.js +++ b/model/damage/character/波可娜/calc.js @@ -39,6 +39,7 @@ export const skills = [ { name: '特殊技:噬爪·噩梦袭影', type: 'EPLP', + isMain: true, redirect: ['EPLP', '追加攻击'] }, { diff --git a/model/damage/character/派派/calc.js b/model/damage/character/派派/calc.js index 37a160b..e9cfd71 100644 --- a/model/damage/character/派派/calc.js +++ b/model/damage/character/派派/calc.js @@ -16,7 +16,7 @@ export const buffs = [ /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ - { name: '强击', type: '强击' }, + { name: '强击', isMain: true, type: '强击' }, { name: '普攻:准备发车四段', type: 'AP4' }, { name: '闪避反击:动力漂移', type: 'CF' }, { name: '强化特殊技:引擎转(每圈)', type: 'EQZ' }, diff --git a/model/damage/character/猫又/calc.js b/model/damage/character/猫又/calc.js index 2bfc0d1..3ee6e12 100644 --- a/model/damage/character/猫又/calc.js +++ b/model/damage/character/猫又/calc.js @@ -32,7 +32,7 @@ export const buffs = [ /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ { name: '强击', type: '强击' }, - { name: '普攻:猫猫爪刺四段', type: 'AP4' }, + { name: '普攻:猫猫爪刺四段', isMain: true, type: 'AP4' }, { name: '闪避反击:虚影双刺', type: 'CF' }, { name: '强化特殊技:超~凶奇袭!', type: 'EQ' }, { name: '连携技:刃爪挥击', type: 'RL' }, diff --git a/model/damage/character/简/calc.js b/model/damage/character/简/calc.js index 2a7726d..020df28 100644 --- a/model/damage/character/简/calc.js +++ b/model/damage/character/简/calc.js @@ -70,7 +70,7 @@ export const buffs = [ /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ - { name: '强击', type: '强击' }, + { name: '强击', isMain: true, type: '强击' }, { name: '紊乱', type: '紊乱' }, { name: '普攻:跳步刃舞六段(狂热)', type: 'AP6' }, { name: '普攻:萨霍夫跳0', type: 'AX0', isHide: true }, diff --git a/model/damage/character/耀嘉音/calc.js b/model/damage/character/耀嘉音/calc.js index 5e8a670..3f2a514 100644 --- a/model/damage/character/耀嘉音/calc.js +++ b/model/damage/character/耀嘉音/calc.js @@ -56,7 +56,7 @@ export const skills = [ }, { name: '普攻:间奏/终曲每[震音]', type: 'AQ', before }, { name: '特殊技:《风铃与旧约》', type: 'EP', before }, - { name: '和弦追加[震音]', type: 'EQZ', before }, + { name: '和弦追加[震音]', isMain: true, type: 'EQZ', before }, { name: '天赋追加[震音]', type: 'EZ', before }, { name: '追加[音簇]*3', diff --git a/model/damage/character/艾莲/calc.js b/model/damage/character/艾莲/calc.js index d371a15..b104134 100644 --- a/model/damage/character/艾莲/calc.js +++ b/model/damage/character/艾莲/calc.js @@ -11,7 +11,6 @@ export const buffs = [ value: 0.6, range: ['EQ'] }, - { name: '6影', type: '穿透率', @@ -40,7 +39,11 @@ export const buffs = [ /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ { name: '碎冰', type: '碎冰' }, - { name: '普攻:急冻修剪法三段', type: 'AQ3' }, + { + name: '普攻:急冻修剪法三段', + isMain: true, + type: 'AQ3' + }, { name: '闪避反击:暗礁', type: 'CF' }, { name: '冲刺攻击:寒潮', type: 'CCP' }, { diff --git a/model/damage/character/莱卡恩/calc.js b/model/damage/character/莱卡恩/calc.js index 4f11895..dd3fc2a 100644 --- a/model/damage/character/莱卡恩/calc.js +++ b/model/damage/character/莱卡恩/calc.js @@ -18,7 +18,7 @@ export const skills = [ { name: '蓄力普攻:五段一级', type: 'AX51' }, { name: '蓄力普攻:五段二级', type: 'AX52' }, { name: '闪避反击:保持清洁', type: 'CF' }, - { name: '蓄力强化特殊技:狂猎时刻', type: 'EQX' }, + { name: '蓄力强化特殊技:狂猎时刻', isMain: true, type: 'EQX' }, { name: '连携技:遵命', type: 'RL' }, { name: '终结技:不辱使命', type: 'RZ' } ] \ No newline at end of file diff --git a/model/damage/character/莱特/calc.js b/model/damage/character/莱特/calc.js index 6db59f0..0e70d75 100644 --- a/model/damage/character/莱特/calc.js +++ b/model/damage/character/莱特/calc.js @@ -41,15 +41,18 @@ export const buffs = [ element: ['Fire', 'Ice'], value: ({ calc }) => { const Impact = calc.get_Impact() - const step = 0.0125 + Math.max(0, Math.floor((Impact - 170) / 10) * 25 / 10000) + const step = 0.0125 + Math.max(0, (Impact - 170) / 10 * 25 / 10000) return Math.min(0.75, step * 20) + }, + is: { + team: true } } ] /** @type {import('../../Calculator.ts').Calculator['skills']} */ export const skills = [ - { name: '普攻:强力终结一击', type: 'AQ5Q' }, + { name: '普攻:强力终结一击', isMain: true, type: 'AQ5Q' }, { name: '闪避反击:烈闪', type: 'CF' }, { name: '快速支援:烈闪-守', type: 'LK' }, { name: '强化E:V式日轮升拳-全冲程', type: 'EQ1' }, diff --git a/model/damage/character/零号·安比/calc.js b/model/damage/character/零号·安比/calc.js index 73781d9..cc0b7a8 100644 --- a/model/damage/character/零号·安比/calc.js +++ b/model/damage/character/零号·安比/calc.js @@ -49,6 +49,7 @@ export const skills = [ name: '特殊技:苍光', type: 'EPC', redirect: ['EPC', '追加攻击'], + isMain: true, after: ({ damage }) => damage.add('EPC0') }, { diff --git a/model/damage/character/露西/calc.js b/model/damage/character/露西/calc.js index 9dabbe3..870666d 100644 --- a/model/damage/character/露西/calc.js +++ b/model/damage/character/露西/calc.js @@ -62,7 +62,7 @@ export const skills = [ }, { name: '闪避反击:獠牙折转!', type: 'CF' }, { name: '强化特殊技:全垒打短按', type: 'EQP' }, - { name: '强化特殊技:全垒打长按', type: 'EQX' }, + { name: '强化特殊技:全垒打长按', isMain: true, type: 'EQX' }, { name: '连携技:大满贯!', type: 'RL', diff --git a/resources/map/Property2Name.json b/resources/map/Property2Name.json index 0fcfa2d..ab8a880 100644 --- a/resources/map/Property2Name.json +++ b/resources/map/Property2Name.json @@ -1,21 +1,21 @@ { - "11102": ["HPRatio", "生命值百分比", "生命"], - "11103": ["HP", "生命值", "生命"], - "12102": ["ATKRatio", "攻击力百分比", "攻击"], - "12103": ["ATK", "攻击力", "攻击"], - "12202": ["Impact", "冲击力", "冲击"], - "13102": ["DEFRatio", "防御力百分比", "防御"], - "13103": ["DEF", "防御力", "防御"], - "20103": ["CRITRate", "暴击率", "暴击"], - "21103": ["CRITDMG", "暴击伤害", "暴伤"], - "23103": ["PenRatio", "穿透率", "穿透"], - "23203": ["Pen", "穿透值", "穿透"], - "30502": ["EnergyRegen", "能量回复", "回能"], - "31203": ["AnomalyProficiency", "异常精通", "精通"], - "31402": ["AnomalyMastery", "异常掌控", "掌控"], - "31503": ["PhysicalDMGBonus", "物理属性伤害提高", "物伤"], - "31603": ["FireDMGBonus", "火属性伤害提高", "火伤"], - "31703": ["IceDMGBonus", "冰属性伤害提高", "冰伤"], - "31803": ["ElectricDMGBonus", "电属性伤害提高", "电伤"], - "31903": ["EtherDMGBonus", "以太属性伤害提高", "以伤"] + "11102": ["HPRatio", "生命值百分比", "生命", "大生命"], + "11103": ["HP", "生命值", "生命", "小生命"], + "12102": ["ATKRatio", "攻击力百分比", "攻击", "大攻击"], + "12103": ["ATK", "攻击力", "攻击", "小攻击"], + "12202": ["Impact", "冲击力", "冲击", "冲击力"], + "13102": ["DEFRatio", "防御力百分比", "防御", "大防御"], + "13103": ["DEF", "防御力", "防御", "小防御"], + "20103": ["CRITRate", "暴击率", "暴击", "暴击率"], + "21103": ["CRITDMG", "暴击伤害", "暴伤", "暴伤"], + "23103": ["PenRatio", "穿透率", "穿透", "穿透率"], + "23203": ["Pen", "穿透值", "穿透", "穿透值"], + "30502": ["EnergyRegen", "能量回复", "回能", "回能"], + "31203": ["AnomalyProficiency", "异常精通", "精通", "精通"], + "31402": ["AnomalyMastery", "异常掌控", "掌控", "掌控"], + "31503": ["PhysicalDMGBonus", "物理属性伤害提高", "物伤", "物伤"], + "31603": ["FireDMGBonus", "火属性伤害提高", "火伤", "火伤"], + "31703": ["IceDMGBonus", "冰属性伤害提高", "冰伤", "冰伤"], + "31803": ["ElectricDMGBonus", "电属性伤害提高", "电伤", "电伤"], + "31903": ["EtherDMGBonus", "以太属性伤害提高", "以伤", "以太伤"] } diff --git a/resources/panel/card.css b/resources/panel/card.css index 3f8ac5b..4564a63 100644 --- a/resources/panel/card.css +++ b/resources/panel/card.css @@ -255,30 +255,6 @@ .card .basic .info .char_info .addition .role_ranks.r6 span:nth-child(6) { opacity: 1 !important; } -.card .basic .info .char_info .addition .role_ranks span:nth-child(7) { - background-image: url("./images/ranks/7.png"); -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(1) { - opacity: 1 !important; -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(2) { - opacity: 1 !important; -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(3) { - opacity: 1 !important; -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(4) { - opacity: 1 !important; -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(5) { - opacity: 1 !important; -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(6) { - opacity: 1 !important; -} -.card .basic .info .char_info .addition .role_ranks.r7 span:nth-child(7) { - opacity: 1 !important; -} .card .basic .info .property_info { background-color: rgb(47, 47, 47); border-left: 0.1em solid rgb(0, 0, 0); @@ -854,7 +830,7 @@ padding-top: 1.2em; } .card .damagelist { - margin: 0 2em; + margin: 0 1.2em; margin-top: 1em; text-align: center; display: grid; @@ -866,12 +842,10 @@ } .card .damagelist .dmg-tr { display: grid; - grid-template-columns: 1.5fr 1fr 1fr; + grid-template-columns: 0.25fr 1.6fr 1fr 1fr; border-bottom: 0.1em solid rgba(255, 255, 255, 0.2); } .card .damagelist .dmg-tr:first-child { - border-top: 0.2em solid rgba(255, 255, 255, 0.2); - border-bottom: 0.2em solid rgba(255, 255, 255, 0.2); font-size: 1.1em; background: rgba(0, 0, 0, 0.2); text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.6); @@ -881,13 +855,28 @@ text-align: center !important; background: none !important; } +.card .damagelist .dmg-tr:nth-child(even) { + background-color: rgba(255, 255, 255, 0.12); +} +.card .damagelist .dmg-tr:last-child { + border-bottom: none; +} +.card .damagelist .info-tr { + font-size: 0.8em !important; + display: grid !important; + grid-template-columns: 1fr !important; + padding: 0.3em !important; + color: #bbb !important; +} +.card .damagelist .info-tr span { + color: #fff !important; +} .card .damagelist .dmg-td { padding: 0.5em 0.5em; font-size: 0.9em; border-right: 0.1em solid rgba(255, 255, 255, 0.2); } -.card .damagelist .dmg-td:first-child { - text-align: left; +.card .damagelist .dmg-td:nth-child(2) { background: rgba(0, 0, 0, 0.2); color: rgb(206, 190, 149); } diff --git a/resources/panel/card.html b/resources/panel/card.html index 77c0ed1..0955a94 100644 --- a/resources/panel/card.html +++ b/resources/panel/card.html @@ -243,17 +243,23 @@
-
类型
+
%
+
技能类型
暴击伤害
期望伤害
- {{each damages damage}} + {{each damages damage,index}}
+
{{index+1}}
{{damage.skill.name}}
{{damage.result.critDMG.toFixed(0)}}
{{damage.result.expectDMG.toFixed(0)}}
{{/each}} +
+
伤害计算默认取有弱点、角色等级、1级基础防御力为50的敌方数据,且不考虑失衡易伤区
+
可通过%{{charData.name_mi18n}}伤害+序号查看指定技能伤害详情 如%{{charData.name_mi18n}}伤害1
+
{{/if}} diff --git a/resources/panel/card.scss b/resources/panel/card.scss index 7d280ba..7e19985 100644 --- a/resources/panel/card.scss +++ b/resources/panel/card.scss @@ -160,7 +160,7 @@ opacity: 0.4; } - @for $i from 1 through 7 { + @for $i from 1 through 6 { span:nth-child(#{$i}) { background-image: url('./images/ranks/#{$i}.png'); } @@ -843,7 +843,7 @@ } .damagelist { - margin: 0 2em; + margin: 0 1.2em; margin-top: 1em; text-align: center; display: grid; @@ -855,12 +855,10 @@ .dmg-tr { display: grid; - grid-template-columns: 1.5fr 1fr 1fr; + grid-template-columns: 0.25fr 1.6fr 1fr 1fr; border-bottom: 0.1em solid rgba(255, 255, 255, 0.2); &:first-child { - border-top: 0.2em solid rgba(255, 255, 255, 0.2); - border-bottom: 0.2em solid rgba(255, 255, 255, 0.2); font-size: 1.1em; background: rgba(0, 0, 0, 0.2); text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.6); @@ -870,14 +868,32 @@ background: none !important; } } + + &:nth-child(even) { + background-color: rgba(255, 255, 255, 0.12); + } + + &:last-child { + border-bottom: none; + } + } + + .info-tr { + font-size: 0.8em !important; + display: grid !important; + grid-template-columns: 1fr !important; + padding: 0.3em !important; + color: #bbb !important; + span { + color: #fff !important; + } } .dmg-td { padding: 0.5em 0.5em; font-size: 0.9em; border-right: 0.1em solid rgba(255, 255, 255, 0.2); - &:first-child { - text-align: left; + &:nth-child(2) { background: rgba(0, 0, 0, 0.2); color: rgb(206, 190, 149); } diff --git a/resources/panel/damage.css b/resources/panel/damage.css new file mode 100644 index 0000000..bc0ddfb --- /dev/null +++ b/resources/panel/damage.css @@ -0,0 +1,557 @@ +@charset "UTF-8"; +.damage { + padding-top: 0.8em; + overflow: hidden; + position: relative; + /* 伤害统计 */ + /* 乘区数据 */ + /* buff统计 */ + /* 差异计算 */ +} +.damage .uid { + font-size: 0.6em; + text-align: center; + position: absolute; + border-image-source: url("./images/CurseBG08.png"); + border-image-slice: 30 60 30 60 fill; + border-image-width: 0.5em 1em 0.5em 1em; + border-image-outset: 0em 0em 0em 0em; + border-image-repeat: stretch stretch; + padding: 0.5em 1.2em; + font-size: 1em; + top: 0.5em; + left: 0.5em; + color: rgb(255, 255, 255); + stroke: 0.05em rgba(0, 0, 0, 0.6); + -webkit-text-stroke: 0.05em rgba(0, 0, 0, 0.6); + z-index: 1; +} +.damage .star { + width: 5.5em; + height: 1.5em; +} +.damage .star.star0 { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url("./images/star/0.png"); +} +.damage .star.star1 { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url("./images/star/1.png"); +} +.damage .star.star2 { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url("./images/star/2.png"); +} +.damage .star.star3 { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url("./images/star/3.png"); +} +.damage .star.star4 { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url("./images/star/4.png"); +} +.damage .star.star5 { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url("./images/star/5.png"); +} +.damage .basic { + display: flex; + align-items: stretch; + overflow: hidden; +} +.damage .basic .char { + width: 55%; + position: relative; + flex-grow: 1; +} +.damage .basic .char .avatar { + height: 100%; + padding-top: 1em; + overflow: hidden; +} +.damage .basic .char .avatar img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: top center; + position: absolute; +} +.damage .basic .char .skills { + position: absolute; + width: 120%; + height: 3.5em; + background: url("./images/skill_bg.png"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + bottom: 3em; + right: -1.6em; + display: flex; + align-items: flex-end; + padding-left: 2.5em; + padding-bottom: 0.2em; +} +.damage .basic .char .skills .skill { + width: 1.4em; + aspect-ratio: 1; + margin-right: 1.38em; + display: flex; + justify-content: center; + align-items: center; +} +.damage .basic .info { + width: 45%; + flex-grow: 0; + flex-shrink: 0; + font-size: 1.2em; + position: relative; + z-index: 2; +} +.damage .basic .info .char_info { + width: 140%; + position: relative; + right: 20%; + padding: 0.3em 0.2em; + padding-right: 20%; + padding-left: 8%; + border-image-slice: 0 30 0 40 fill; + border-image-width: 0em 1.5em 0em 2em; + border-image-outset: 0 0 0 0; + border-image-repeat: stretch stretch; + border-image-source: url("./images/CurseBG04.png"); + filter: drop-shadow(0 0 0.1em rgb(0, 0, 0)); +} +.damage .basic .info .char_info .base { + display: flex; + align-items: center; + gap: 0.2em; + overflow: hidden; +} +.damage .basic .info .char_info .base .rank { + width: 1.2em; + flex-grow: 0; + flex-shrink: 0; +} +.damage .basic .info .char_info .base .property { + width: 1em; + flex-grow: 0; + flex-shrink: 0; +} +.damage .basic .info .char_info .base .name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.damage .basic .info .char_info .addition { + display: flex; + align-items: center; + font-size: 0.7em; + gap: 0.2em; + padding-left: 1em; +} +.damage .basic .info .char_info .addition .level { + background-color: #000; + padding: 0em 0.7em; + border-radius: 1em; +} +.damage .basic .info .char_info .addition .role_ranks { + display: flex; + gap: 0.1em; +} +.damage .basic .info .char_info .addition .role_ranks span { + width: 1.2em; + aspect-ratio: 1; + border-radius: 1em; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + opacity: 0.4; +} +.damage .basic .info .char_info .addition .role_ranks span:nth-child(1) { + background-image: url("./images/ranks/1.png"); +} +.damage .basic .info .char_info .addition .role_ranks.r1 span:nth-child(1) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks span:nth-child(2) { + background-image: url("./images/ranks/2.png"); +} +.damage .basic .info .char_info .addition .role_ranks.r2 span:nth-child(1) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r2 span:nth-child(2) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks span:nth-child(3) { + background-image: url("./images/ranks/3.png"); +} +.damage .basic .info .char_info .addition .role_ranks.r3 span:nth-child(1) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r3 span:nth-child(2) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r3 span:nth-child(3) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks span:nth-child(4) { + background-image: url("./images/ranks/4.png"); +} +.damage .basic .info .char_info .addition .role_ranks.r4 span:nth-child(1) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r4 span:nth-child(2) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r4 span:nth-child(3) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r4 span:nth-child(4) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks span:nth-child(5) { + background-image: url("./images/ranks/5.png"); +} +.damage .basic .info .char_info .addition .role_ranks.r5 span:nth-child(1) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r5 span:nth-child(2) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r5 span:nth-child(3) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r5 span:nth-child(4) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r5 span:nth-child(5) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks span:nth-child(6) { + background-image: url("./images/ranks/6.png"); +} +.damage .basic .info .char_info .addition .role_ranks.r6 span:nth-child(1) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r6 span:nth-child(2) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r6 span:nth-child(3) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r6 span:nth-child(4) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r6 span:nth-child(5) { + opacity: 1 !important; +} +.damage .basic .info .char_info .addition .role_ranks.r6 span:nth-child(6) { + opacity: 1 !important; +} +.damage .basic .info .property_info { + background-color: rgb(47, 47, 47); + border-left: 0.1em solid rgb(0, 0, 0); + padding-top: 0.2em; + background: url("./images/BgFrame01.png") center/150% no-repeat; +} +.damage .basic .info .property_info .title { + font-size: 0.6em; +} +.damage .basic .info .property_info .title .special-title { + margin-bottom: 0; + padding-bottom: 0; +} +.damage .basic .info .property_info .list { + display: flex; + flex-direction: column; + gap: 0.2em; + padding-bottom: 0.2em; +} +.damage .basic .info .property_info .list .properties { + display: flex; + align-items: center; + gap: 0.4em; + padding: 0.05em 1em 0em 0.05em; +} +.damage .basic .info .property_info .list .properties:nth-child(odd) { + background-color: rgba(221, 224, 221, 0.25); +} +.damage .basic .info .property_info .list .properties .prop-icon { + width: 1em; + flex-grow: 0; + flex-shrink: 0; + margin: 0 0.4em; +} +.damage .basic .info .property_info .list .properties .label { + flex-grow: 1; + flex-shrink: 1; + font-size: 0.65em; + color: rgb(166, 166, 166); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.damage .basic .info .property_info .list .properties .label.yellow { + color: rgb(247, 199, 54); +} +.damage .basic .info .property_info .list .properties .label.blue { + color: rgb(65, 147, 237); +} +.damage .basic .info .property_info .list .properties .label.white { + color: rgb(233, 233, 233); +} +.damage .basic .info .property_info .list .properties .value { + flex-grow: 0; + flex-shrink: 0; + font-size: 0.8em; +} +.damage .basic .info .weapon_info { + border-image-slice: 118 0 68 43 fill; + border-image-width: 4.5em 0em 2.7em 1.7em; + border-image-outset: 0em 0em 0em 0em; + border-image-repeat: stretch stretch; + border-image-source: url("./images/weapon_bg.png"); + margin-top: -0.8em; + width: 115%; + margin-left: -15%; + font-size: 0.8em; + padding: 2em 1.2em; + padding-bottom: 3em; + position: relative; + overflow: hidden; +} +.damage .basic .info .weapon_info .info { + width: 100%; + position: relative; + z-index: 2; +} +.damage .basic .info .weapon_info .info .base { + width: 100%; + display: flex; + align-items: center; + gap: 0.5em; +} +.damage .basic .info .weapon_info .info .base .rarity-icon { + width: 2em; +} +.damage .basic .info .weapon_info .info .base .name { + text-shadow: 0 0 0.2em rgb(0, 0, 0); +} +.damage .basic .info .weapon_info .info .main { + display: flex; + flex-direction: column; + justify-content: stretch; + width: max-content; +} +.damage .basic .info .weapon_info .info .main .addition { + font-size: 0.8em; + display: flex; + align-items: center; + gap: 0.4em; +} +.damage .basic .info .weapon_info .info .main .addition .level { + -webkit-clip-path: polygon(0.2em 0%, calc(100% - 0.2em) 0%, 100% 0.2em, 100% calc(100% - 0.2em), calc(100% - 0.2em) 100%, 0.2em 100%, 0% calc(100% - 0.2em), 0% 0.2em); + clip-path: polygon(0.2em 0%, calc(100% - 0.2em) 0%, 100% 0.2em, 100% calc(100% - 0.2em), calc(100% - 0.2em) 100%, 0.2em 100%, 0% calc(100% - 0.2em), 0% 0.2em); + padding: 0 0.4em; + font-size: 0.9em; + display: flex; + justify-content: center; + align-items: center; + color: rgb(43, 38, 40); + margin: 0.1em 0; + background-color: rgb(243, 203, 69); +} +.damage .basic .info .weapon_info .info .main .properties { + display: flex; + align-items: center; + background-color: rgb(65, 147, 237); + gap: 0.2em; + padding: 0 0.5em; + border-radius: 1em; + margin: 0.2em 0; +} +.damage .basic .info .weapon_info .info .main .properties.sub { + background-color: rgb(0, 0, 0); +} +.damage .basic .info .weapon_info .info .main .properties .prop-icon { + width: 1em; + flex-grow: 0; + flex-shrink: 0; +} +.damage .basic .info .weapon_info .info .main .properties .label { + flex-grow: 1; + flex-shrink: 1; + font-size: 0.7em; + color: rgb(222, 222, 222); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.damage .basic .info .weapon_info .icon { + position: absolute; + top: 1.2em; + right: 0; + z-index: 1; + height: 70%; + margin-right: -1em; +} +.damage .basic .info .weapon_info .icon img { + height: 100%; +} +.damage .damage-data { + border-image-source: url("./images/BgFrame01.png"); + border-image-slice: 200 100 70 280 fill; + border-image-width: 2em 1em 0.7em 2.8em; + border-image-outset: 2em 1em 0.7em 2.8em; + border-image-repeat: stretch stretch; + padding-bottom: 3.3em; + margin-top: -1.4em; + position: relative; + z-index: 5; +} +.damage .title:nth-child(n+2) { + padding-top: 0.5em; +} +.damage .data-list { + margin: 0 1.2em; + text-align: center; + backdrop-filter: blur(0.2em); + border-radius: 0.5em; + overflow: hidden; + box-shadow: 0 0 3em rgba(0, 0, 0, 0.6); + border: 0.1em solid rgba(255, 255, 255, 0.3); +} +.damage .data-list .tr { + display: grid; + border-bottom: 0.1em solid rgba(255, 255, 255, 0.3); +} +.damage .data-list .tr:first-child { + font-size: 1.1em; + background: rgba(0, 0, 0, 0.2); + text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.6); + color: rgb(206, 190, 149); +} +.damage .data-list .tr:last-child { + border-bottom: none; +} +.damage .data-list .tr:nth-child(even) { + background-color: rgba(255, 255, 255, 0.12); +} +.damage .data-list .td { + padding: 0.5em 0.5em; + font-size: 0.9em; + border-right: 0.1em solid rgba(255, 255, 255, 0.3); +} +.damage .data-list .td:last-child { + border-right: none; +} +.damage .data-list .info-tr { + font-size: 0.8em !important; + display: grid !important; + grid-template-columns: 1fr !important; + padding: 0.3em !important; + color: #bbb !important; +} +.damage .data-list .info-tr span { + color: #fff !important; +} +.damage .damage-list .dmg-tr { + grid-template-columns: 0.25fr 1.6fr 1fr 1fr; +} +.damage .damage-list .dmg-tr.current { + background: rgb(120, 104, 73); +} +.damage .damage-list .dmg-tr.current .dmg-td:nth-child(2) { + color: rgb(251, 198, 65) !important; + font-weight: bold !important; +} +.damage .damage-list .dmg-td:nth-child(2) { + background: rgba(0, 0, 0, 0.2); + color: rgb(206, 190, 149); +} +.damage .area-list .area-tr { + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; +} +.damage .area-list .area-tr:nth-child(odd) { + font-size: 1.1em; + background: rgba(0, 0, 0, 0.2); + text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.6); + color: rgb(206, 190, 149); +} +.damage .area-list .area-tr.anomaly { + grid-template-columns: 1fr 1fr 1fr; +} +.damage .buff-list .buff-tr { + grid-template-columns: 0.7fr 5fr 3fr 2fr; +} +.damage .buff-list .buff-td:nth-child(2) { + background: rgba(0, 0, 0, 0.2); + color: rgb(206, 190, 149); +} +.damage .difference-list .difference-tr.d2 { + grid-template-columns: repeat(3, 1fr); +} +.damage .difference-list .difference-tr.d3 { + grid-template-columns: repeat(4, 1fr); +} +.damage .difference-list .difference-tr.d4 { + grid-template-columns: repeat(5, 1fr); +} +.damage .difference-list .difference-tr.d5 { + grid-template-columns: repeat(6, 1fr); +} +.damage .difference-list .difference-tr.d6 { + grid-template-columns: repeat(7, 1fr); +} +.damage .difference-list .difference-tr.d7 { + grid-template-columns: repeat(8, 1fr); +} +.damage .difference-list .difference-tr.d8 { + grid-template-columns: repeat(9, 1fr); +} +.damage .difference-list .difference-tr.d9 { + grid-template-columns: repeat(10, 1fr); +} +.damage .difference-list .difference-td { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +.damage .difference-list .difference-td span { + font-size: 0.8em; + color: #bbb; +} +.damage .difference-list .difference-td.zero { + background-color: #222; +} +.damage .difference-list .difference-td:first-child { + background: rgba(0, 0, 0, 0.2); + color: rgb(206, 190, 149); +} +.damage .difference-list .difference-td.positive { + background-color: rgb(82, 38, 32); +} +.damage .difference-list .difference-td.negative { + background-color: rgb(22, 65, 33); +} + +.copyright { + margin-top: -3.3em; + position: relative; + z-index: 5; +} + +/*# sourceMappingURL=damage.css.map */ diff --git a/resources/panel/damage.html b/resources/panel/damage.html new file mode 100644 index 0000000..99d8f26 --- /dev/null +++ b/resources/panel/damage.html @@ -0,0 +1,257 @@ +{{extend defaultLayout}} + +{{block 'css'}} + +{{/block}} + +{{block 'main'}} +
+
UID {{uid}}
+
+
+
+ +
+
+
{{charData.skills[0].level}}
+
{{charData.skills[2].level}}
+
{{charData.skills[5].level}}
+
{{charData.skills[1].level}}
+
{{charData.skills[3].level}}
+
{{charData.skills[4].level}}
+
+
+
+
+
+
+
+
{{charData.full_name_mi18n}}
+
+
+
Lv.{{charData.level}}
+
+ +
+
+
+
+
+ <% include(sys.specialTitle, {en: 'PROPERTY' , cn: '属性' , count: 6 }) %> +
+ {{set basic_properties = charData.basic_properties}} +
+
+
+
生命值
+
{{basic_properties.hpmax.final}}
+
+
+
+
攻击力
+
{{basic_properties.attack.final}}
+
+
+
+
防御力
+
{{basic_properties.def.final}}
+
+
+
+
冲击力
+
{{basic_properties.breakstun.final}}
+
+
+
+
暴击率
+
{{basic_properties.crit.final}}
+
+
+
+
暴击伤害
+
{{basic_properties.critdam.final}}
+
+
+
+
异常掌控
+
{{basic_properties.elementabnormalpower.final}}
+
+
+
+
异常精通
+
{{basic_properties.elementmystery.final}}
+
+
+
+
穿透率
+
{{basic_properties.penratio.final}}
+
+
+
+
能量回复
+
{{basic_properties.sprecover.final}}
+
+
+
+
+ {{if charData.weapon}} +
+
+
+
{{charData.weapon.name}}
+
+
+
+
+
Lv.{{charData.weapon.level}}
+
+ {{each charData.weapon.main_properties prop}} +
+
+
{{prop.property_name}}
+
{{prop.base}}
+
+ {{/each}} + {{each charData.weapon.properties prop}} +
+
+
{{prop.property_name}}
+
{{prop.base}}
+
+ {{/each}} +
+
+
+ +
+ {{else}} +
+
+
+
+
+
+ {{/if}} +
+
+
+
+
+ <% include(sys.specialTitle, {en: 'DAMAGE' , cn: '伤害统计' }) %> +
+
+
+
%
+
技能类型
+
暴击伤害
+
期望伤害
+
+ {{each damages d,index}} + {{if index == skill.index}} +
+ {{else}} +
+ {{/if}} +
{{index+1}}
+
{{d.skill.name}}
+
{{d.result.critDMG.toFixed(0)}}
+
{{d.result.expectDMG.toFixed(0)}}
+
+ {{/each}} +
+
伤害计算默认取有弱点、角色等级、1级基础防御力为50的敌方数据,且不考虑失衡易伤区
+
当前指令:{{command}}
+
下列计算以 {{damage.skill.name}} 为计算目标
+
可通过 %{{charData.name_mi18n}}伤害123 切换需要查看的伤害计算
+
+
+
+ <% include(sys.specialTitle, {en: 'AREA' , cn: '乘区数据' }) %> +
+
+ {{set areas = damage.areas}} +
+
基础区
+
暴击区
+
增伤区
+
易伤区
+
抗性区
+
防御区
+
+
+
{{(areas.BasicArea || 1).toFixed(0)}}
+
{{(areas.CriticalArea || 1).toFixed(2)}}
+
{{(areas.BoostArea || 1).toFixed(2)}}
+
{{(areas.VulnerabilityArea || 1).toFixed(2)}}
+
{{(areas.ResistanceArea || 1).toFixed(2)}}
+
{{(areas.DefenceArea || 1).toFixed(4)}}
+
+ {{if areas.AnomalyProficiencyArea}} +
+
异常精通区
+
异常增伤区
+
等级区
+
+
+
{{(areas.AnomalyProficiencyArea || 1).toFixed(2)}}
+
{{(areas.AnomalyBoostArea || 1).toFixed(2)}}
+
{{(areas.LevelArea || 1).toFixed(2)}}
+
+ {{/if}} +
+
注:一个技能可能分为多个出伤部分分别计算累加,此时该数据仅为一部分的乘区,下同
+
+
+
+ <% include(sys.specialTitle, {en: 'BUFF' , cn: 'Buff统计' }) %> +
+
+
+
%
+
名称
+
增益类型
+
增益值
+
+ {{each damage.usefulBuffs buff,index}} +
+
{{index+1}}
+
{{buff.name}}
+
{{buff.type}}
+
{{buff.value % 1 == 0 ? buff.value < 2 ? buff.value.toFixed(2) : buff.value : buff.value.toFixed(2)}}
+
+ {{/each}} +
+ {{if differences.length > 1}} +
+ <% include(sys.specialTitle, {en: 'STAT' , cn: '词条差异计算' }) %> +
+
+
+
词条变化
+ {{each differences[0] d}} +
+ {{d.add.shortName}} + +{{d.add.valueBase}} +
+ {{/each}} +
+ {{each differences diff}} +
+
+ {{diff[0].del.shortName}} + -{{diff[0].del.valueBase}} +
+ {{each diff d}} +
{{d.difference > 0 ? '+' + d.difference.toFixed(0) : d.difference.toFixed(0)}}
+ {{/each}} +
+ {{/each}} +
+
反映在上述buff作用下置换单位词条后的{{skill.name}}期望伤害变化
+
横轴表示增加的单位词条,纵轴表示减少的单位词条,对应坐标即为期望伤害变化
+
+
+ {{/if}} +
+
+{{/block}} \ No newline at end of file diff --git a/resources/panel/damage.scss b/resources/panel/damage.scss new file mode 100644 index 0000000..1abec04 --- /dev/null +++ b/resources/panel/damage.scss @@ -0,0 +1,547 @@ +.damage { + padding-top: 0.8em; + overflow: hidden; + position: relative; + + .uid { + font-size: 0.6em; + text-align: center; + position: absolute; + border-image-source: url('./images/CurseBG08.png'); + border-image-slice: 30 60 30 60 fill; + border-image-width: 0.5em 1em 0.5em 1em; + border-image-outset: 0em 0em 0em 0em; + border-image-repeat: stretch stretch; + padding: 0.5em 1.2em; + font-size: 1em; + top: 0.5em; + left: 0.5em; + color: rgb(255, 255, 255); + stroke: 0.05em rgba(0, 0, 0, 0.6); + -webkit-text-stroke: 0.05em rgba(0, 0, 0, 0.6); + z-index: 1; + } + + .star { + width: 5.5em; + height: 1.5em; + + @for $i from 0 through 5 { + &.star#{$i} { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + background-image: url('./images/star/#{$i}.png'); + } + } + } + + .basic { + display: flex; + align-items: stretch; + overflow: hidden; + + .char { + width: 55%; + position: relative; + flex-grow: 1; + + .avatar { + height: 100%; + padding-top: 1em; + overflow: hidden; + + img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: top center; + position: absolute; + } + } + + .skills { + position: absolute; + width: 120%; + height: 3.5em; + background: url('./images/skill_bg.png'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + bottom: 3em; + right: -1.6em; + display: flex; + align-items: flex-end; + padding-left: 2.5em; + padding-bottom: 0.2em; + + .skill { + width: 1.4em; + aspect-ratio: 1; + margin-right: 1.38em; + display: flex; + justify-content: center; + align-items: center; + } + } + } + + .info { + width: 45%; + flex-grow: 0; + flex-shrink: 0; + font-size: 1.2em; + position: relative; + z-index: 2; + + .char_info { + width: 140%; + position: relative; + right: 20%; + padding: 0.3em 0.2em; + padding-right: 20%; + padding-left: 8%; + border-image-slice: 0 30 0 40 fill; + border-image-width: 0em 1.5em 0em 2em; + border-image-outset: 0 0 0 0; + border-image-repeat: stretch stretch; + border-image-source: url('./images/CurseBG04.png'); + filter: drop-shadow(0 0 0.1em rgb(0, 0, 0)); + + .base { + display: flex; + align-items: center; + gap: 0.2em; + overflow: hidden; + + .rank { + width: 1.2em; + flex-grow: 0; + flex-shrink: 0; + } + + .property { + width: 1em; + flex-grow: 0; + flex-shrink: 0; + } + + .name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .addition { + display: flex; + align-items: center; + font-size: 0.7em; + gap: 0.2em; + padding-left: 1em; + + .level { + background-color: #000; + padding: 0em 0.7em; + border-radius: 1em; + } + + .role_ranks { + display: flex; + gap: 0.1em; + + span { + width: 1.2em; + aspect-ratio: 1; + border-radius: 1em; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + opacity: 0.4; + } + + @for $i from 1 through 6 { + span:nth-child(#{$i}) { + background-image: url('./images/ranks/#{$i}.png'); + } + + &.r#{$i} { + @for $j from 1 through $i { + span:nth-child(#{$j}) { + opacity: 1 !important; + } + } + } + } + } + } + } + + .property_info { + background-color: rgb(47, 47, 47); + border-left: 0.1em solid rgb(0, 0, 0); + padding-top: 0.2em; + background: url('./images/BgFrame01.png') center / 150% no-repeat; + + .title { + font-size: 0.6em; + + .special-title { + margin-bottom: 0; + padding-bottom: 0; + } + } + + .list { + display: flex; + flex-direction: column; + gap: 0.2em; + padding-bottom: 0.2em; + + .properties { + display: flex; + align-items: center; + gap: 0.4em; + padding: 0.05em 1em 0em 0.05em; + + &:nth-child(odd) { + background-color: rgba(221, 224, 221, 0.25); + } + + .prop-icon { + width: 1em; + flex-grow: 0; + flex-shrink: 0; + margin: 0 0.4em; + } + + .label { + flex-grow: 1; + flex-shrink: 1; + font-size: 0.65em; + color: rgb(166, 166, 166); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + &.yellow { + color: rgb(247, 199, 54); + } + + &.blue { + color: rgb(65, 147, 237); + } + + &.white { + color: rgb(233, 233, 233); + } + } + + .value { + flex-grow: 0; + flex-shrink: 0; + font-size: 0.8em; + } + } + } + } + + .weapon_info { + border-image-slice: 118 0 68 43 fill; + border-image-width: 4.5em 0em 2.7em 1.7em; + border-image-outset: 0em 0em 0em 0em; + border-image-repeat: stretch stretch; + border-image-source: url('./images/weapon_bg.png'); + margin-top: -0.8em; + width: 115%; + margin-left: -15%; + font-size: 0.8em; + padding: 2em 1.2em; + padding-bottom: 3em; + position: relative; + overflow: hidden; + + .info { + width: 100%; + position: relative; + z-index: 2; + + .base { + width: 100%; + display: flex; + align-items: center; + gap: 0.5em; + + .rarity-icon { + width: 2em; + } + + .name { + text-shadow: 0 0 0.2em rgb(0, 0, 0); + } + } + + .main { + display: flex; + flex-direction: column; + justify-content: stretch; + width: max-content; + + .addition { + font-size: 0.8em; + display: flex; + align-items: center; + gap: 0.4em; + + .level { + $label-width: 0.2em; + -webkit-clip-path: polygon( + $label-width 0%, + calc(100% - $label-width) 0%, + 100% $label-width, + 100% calc(100% - $label-width), + calc(100% - $label-width) 100%, + $label-width 100%, + 0% calc(100% - $label-width), + 0% $label-width + ); + clip-path: polygon( + $label-width 0%, + calc(100% - $label-width) 0%, + 100% $label-width, + 100% calc(100% - $label-width), + calc(100% - $label-width) 100%, + $label-width 100%, + 0% calc(100% - $label-width), + 0% $label-width + ); + padding: 0 0.4em; + font-size: 0.9em; + display: flex; + justify-content: center; + align-items: center; + color: rgb(43, 38, 40); + margin: 0.1em 0; + background-color: rgb(243, 203, 69); + } + } + + .properties { + display: flex; + align-items: center; + background-color: rgb(65, 147, 237); + gap: 0.2em; + padding: 0 0.5em; + border-radius: 1em; + margin: 0.2em 0; + + &.sub { + background-color: rgb(0, 0, 0); + } + + .prop-icon { + width: 1em; + flex-grow: 0; + flex-shrink: 0; + } + + .label { + flex-grow: 1; + flex-shrink: 1; + font-size: 0.7em; + color: rgb(222, 222, 222); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } + } + + .icon { + position: absolute; + top: 1.2em; + right: 0; + z-index: 1; + height: 70%; + margin-right: -1em; + + img { + height: 100%; + } + } + } + } + } + + .damage-data { + border-image-source: url('./images/BgFrame01.png'); + border-image-slice: 200 100 70 280 fill; + border-image-width: 2em 1em 0.7em 2.8em; + border-image-outset: 2em 1em 0.7em 2.8em; + border-image-repeat: stretch stretch; + padding-bottom: 3.3em; + margin-top: -1.4em; + position: relative; + z-index: 5; + } + + .title:nth-child(n+2) { + padding-top: 0.5em; + } + + .data-list { + margin: 0 1.2em; + text-align: center; + backdrop-filter: blur(0.2em); + border-radius: 0.5em; + overflow: hidden; + box-shadow: 0 0 3em rgba(0, 0, 0, 0.6); + border: 0.1em solid rgba(255, 255, 255, 0.3); + + .tr { + display: grid; + border-bottom: 0.1em solid rgba(255, 255, 255, 0.3); + + &:first-child { + font-size: 1.1em; + background: rgba(0, 0, 0, 0.2); + text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.6); + color: rgb(206, 190, 149); + } + + &:last-child { + border-bottom: none; + } + + &:nth-child(even) { + background-color: rgba(255, 255, 255, 0.12); + } + } + + .td { + padding: 0.5em 0.5em; + font-size: 0.9em; + border-right: 0.1em solid rgba(255, 255, 255, 0.3); + &:last-child { + border-right: none; + } + } + + .info-tr { + font-size: 0.8em !important; + display: grid !important; + grid-template-columns: 1fr !important; + padding: 0.3em !important; + color: #bbb !important; + + span { + color: #fff !important; + } + } + } + + /* 伤害统计 */ + .damage-list { + .dmg-tr { + grid-template-columns: 0.25fr 1.6fr 1fr 1fr; + + &.current { + background: rgb(120, 104, 73); + + .dmg-td:nth-child(2) { + color: rgb(251, 198, 65) !important; + font-weight: bold !important; + } + } + } + + .dmg-td { + &:nth-child(2) { + background: rgba(0, 0, 0, 0.2); + color: rgb(206, 190, 149); + } + } + } + + /* 乘区数据 */ + .area-list { + .area-tr { + grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr; + + &:nth-child(odd) { + font-size: 1.1em; + background: rgba(0, 0, 0, 0.2); + text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.6); + color: rgb(206, 190, 149); + } + &.anomaly { + grid-template-columns: 1fr 1fr 1fr; + } + } + + .area-td { + } + } + + /* buff统计 */ + .buff-list { + .buff-tr { + grid-template-columns: 0.7fr 5fr 3fr 2fr; + } + + .buff-td { + &:nth-child(2) { + background: rgba(0, 0, 0, 0.2); + color: rgb(206, 190, 149); + } + } + } + + /* 差异计算 */ + .difference-list { + .difference-tr { + @for $i from 2 through 9 { + &.d#{$i} { + grid-template-columns: repeat($i + 1, 1fr); + } + } + } + + .difference-td { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + span { + font-size: 0.8em; + color: #bbb; + } + + &.zero { + background-color: #222; + } + + &:first-child { + background: rgba(0, 0, 0, 0.2); + color: rgb(206, 190, 149); + } + + &.positive { + background-color: rgb(82, 38, 32); + } + + &.negative { + background-color: rgb(22, 65, 33); + } + + } + } +} + +.copyright { + margin-top: -3.3em; + position: relative; + z-index: 5; +}