mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-16 13:17:32 +00:00
feature:支持Enka面板更新:%更新展柜面板
This commit is contained in:
parent
0fea67a389
commit
21b8915418
25 changed files with 36048 additions and 382 deletions
|
|
@ -38,7 +38,12 @@ git clone --depth=1 https://gitee.com/bietiaop/ZZZ-Plugin.git ./plugins/ZZZ-Plug
|
||||||
|
|
||||||
以下所有功能前缀为:`#zzz`、`%`、`#ZZZ`、`#绝区零` 任选其一
|
以下所有功能前缀为:`#zzz`、`%`、`#ZZZ`、`#绝区零` 任选其一
|
||||||
|
|
||||||

|
<details>
|
||||||
|
<summary>点击展开帮助图</summary>
|
||||||
|
<p align="center">
|
||||||
|
<img width="800" src="https://s2.loli.net/2025/06/03/8jfKHbOkMXgxVu7.jpg" title="帮助图片">
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
## 攻略、图鉴
|
## 攻略、图鉴
|
||||||
|
|
||||||
|
|
@ -81,6 +86,8 @@ git clone --depth=1 https://gitee.com/bietiaop/ZZZ-Plugin.git ./plugins/ZZZ-Plug
|
||||||
|
|
||||||
绑定设备**无法100%解决**账号异常问题。
|
绑定设备**无法100%解决**账号异常问题。
|
||||||
|
|
||||||
|
若更新面板遇见账号异常问题,可尝试 **%更新展柜面板**,这将调用[Enka](https://enka.network/?zzz)接口更新游戏内展示的角色的数据。如若通过此方法更新的角色数据与实际不一致,请[提出issue](https://github.com/ZZZure/ZZZ-Plugin/issues/new)
|
||||||
|
|
||||||
## 角色图缺失
|
## 角色图缺失
|
||||||
|
|
||||||
由于历史代码缘故,以前在游戏资源未更新就进行资源下载的可能导致角色图片缺失,你可以到插件资源目录手动删除对应文件,或者执行命令 `%删除全部资源` 进行删除。删除全部资源指令目前**不会**删除自定义面板图,仅会删除下载的图片资源,再次使用时需重新下载图片(自动下载)。
|
由于历史代码缘故,以前在游戏资源未更新就进行资源下载的可能导致角色图片缺失,你可以到插件资源目录手动删除对应文件,或者执行命令 `%删除全部资源` 进行删除。删除全部资源指令目前**不会**删除自定义面板图,仅会删除下载的图片资源,再次使用时需重新下载图片(自动下载)。
|
||||||
|
|
|
||||||
|
|
@ -101,10 +101,10 @@ const helpData = [
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: '刷新角色面板',
|
title: '刷新角色面板',
|
||||||
desc: '刷新角色面板',
|
desc: '%更新展柜面板 通过Enka接口获取游戏内展柜角色,无需CK',
|
||||||
needCK: true,
|
needCK: true,
|
||||||
needSK: false,
|
needSK: false,
|
||||||
commands: ['刷新面板', '更新面板', '面板刷新', '面板更新'],
|
commands: ['刷新面板', '更新面板', '刷新/更新展柜面板'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '查看角色面板列表',
|
title: '查看角色面板列表',
|
||||||
|
|
@ -115,10 +115,10 @@ const helpData = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '查看角色面板',
|
title: '查看角色面板',
|
||||||
desc: '查看角色详细面板信息',
|
desc: '查看角色详细面板/技能伤害信息',
|
||||||
needCK: false,
|
needCK: false,
|
||||||
needSK: false,
|
needSK: false,
|
||||||
commands: ['角色名+面板'],
|
commands: ['角色名+面板', '角色名+伤害'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '查看角色面板图',
|
title: '查看角色面板图',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import { ZZZPlugin } from '../lib/plugin.js';
|
import { ZZZPlugin } from '../lib/plugin.js';
|
||||||
|
import { parsePlayerInfo, refreshPanelFromEnka } from '../model/Enka/enkaApi.js';
|
||||||
|
import { getCk } from '../lib/common.js';
|
||||||
import {
|
import {
|
||||||
|
mergePanel,
|
||||||
getPanelList,
|
getPanelList,
|
||||||
refreshPanel as refreshPanelFunction,
|
refreshPanel as refreshPanelFunction,
|
||||||
getPanelOrigin,
|
getPanelOrigin,
|
||||||
|
|
@ -20,7 +23,7 @@ export class Panel extends ZZZPlugin {
|
||||||
priority: _.get(settings.getConfig('priority'), 'panel', 70),
|
priority: _.get(settings.getConfig('priority'), 'panel', 70),
|
||||||
rule: [
|
rule: [
|
||||||
{
|
{
|
||||||
reg: `${rulePrefix}(.*)面板(刷新|更新|列表)?$`,
|
reg: `${rulePrefix}(.*)面板(展柜)?(刷新|更新|列表)?$`,
|
||||||
fnc: 'handleRule',
|
fnc: 'handleRule',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -40,7 +43,7 @@ export class Panel extends ZZZPlugin {
|
||||||
}
|
}
|
||||||
async handleRule() {
|
async handleRule() {
|
||||||
if (!this.e.msg) return;
|
if (!this.e.msg) return;
|
||||||
const reg = new RegExp(`${rulePrefix}(.*)面板(刷新|更新|列表)?$`);
|
const reg = new RegExp(`${rulePrefix}(.*?)(?:展柜)?面板(?:展柜)?(刷新|更新|列表)?$`);
|
||||||
const pre = this.e.msg.match(reg)[4]?.trim();
|
const pre = this.e.msg.match(reg)[4]?.trim();
|
||||||
const suf = this.e.msg.match(reg)[5]?.trim();
|
const suf = this.e.msg.match(reg)[5]?.trim();
|
||||||
if (['刷新', '更新'].includes(pre) || ['刷新', '更新'].includes(suf))
|
if (['刷新', '更新'].includes(pre) || ['刷新', '更新'].includes(suf))
|
||||||
|
|
@ -57,19 +60,36 @@ export class Panel extends ZZZPlugin {
|
||||||
const panelSettings = settings.getConfig('panel');
|
const panelSettings = settings.getConfig('panel');
|
||||||
const coldTime = _.get(panelSettings, 'interval', 300);
|
const coldTime = _.get(panelSettings, 'interval', 300);
|
||||||
if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) {
|
if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) {
|
||||||
await this.reply(`${coldTime}秒内只能刷新一次,请稍后再试`);
|
await this.reply(`${coldTime}秒内只能更新一次,请稍后再试`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const { api, deviceFp } = await this.getAPI();
|
const isEnka = this.e.msg.includes('展柜') || !(await getCk(this.e))
|
||||||
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
|
let result
|
||||||
await this.reply('正在刷新面板列表,请稍候...');
|
if (isEnka) {
|
||||||
await this.getPlayerInfo();
|
const data = await refreshPanelFromEnka(uid)
|
||||||
const result = await refreshPanelFunction(api, deviceFp).catch(e => {
|
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
|
||||||
this.reply(e.message);
|
if (typeof data === 'object') {
|
||||||
throw e;
|
const { playerInfo, panelList } = data
|
||||||
});
|
if (!panelList.length) {
|
||||||
|
return this.reply('面板列表为空,请确保已在游戏中展示了对应角色');
|
||||||
|
}
|
||||||
|
result = await mergePanel(uid, panelList)
|
||||||
|
await this.getPlayerInfo(playerInfo)
|
||||||
|
} else if (typeof data === 'number'){
|
||||||
|
return this.reply(`Enka服务调用失败,状态码:${data}${data === 424 ? '\n版本更新后,须等待一段时间才可正常使用enka服务' : ''}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const { api, deviceFp } = await this.getAPI();
|
||||||
|
await this.reply('正在更新面板列表,请稍候...');
|
||||||
|
await this.getPlayerInfo();
|
||||||
|
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
|
||||||
|
result = await refreshPanelFunction(api, deviceFp).catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
await this.reply('面板列表刷新失败,请稍后再试');
|
await this.reply('面板列表更新失败,请稍后再试\n账号异常时,可尝试%更新展柜面板(所更新角色数据与实际不一致时,请提issue)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const newChar = result.filter(item => item.isNew);
|
const newChar = result.filter(item => item.isNew);
|
||||||
|
|
@ -79,14 +99,16 @@ export class Panel extends ZZZPlugin {
|
||||||
};
|
};
|
||||||
await this.render('panel/refresh.html', finalData);
|
await this.render('panel/refresh.html', finalData);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCharPanelList() {
|
async getCharPanelList() {
|
||||||
const uid = await this.getUID();
|
const uid = await this.getUID();
|
||||||
const result = getPanelList(uid);
|
const result = getPanelList(uid);
|
||||||
if (!result) {
|
if (!result.length) {
|
||||||
await this.reply('未找到面板数据,请先%刷新面板');
|
await this.reply(`UID:${uid}无本地面板数据,请先%更新面板 或 %更新展柜面板`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await this.getPlayerInfo();
|
const hasCk = !!(await getCk(this.e));
|
||||||
|
await this.getPlayerInfo(hasCk ? undefined : parsePlayerInfo({ uid }));
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
if (this?.reply) {
|
if (this?.reply) {
|
||||||
this.reply('查询成功,正在下载图片资源,请稍候。');
|
this.reply('查询成功,正在下载图片资源,请稍候。');
|
||||||
|
|
@ -123,7 +145,7 @@ export class Panel extends ZZZPlugin {
|
||||||
const name = match[4];
|
const name = match[4];
|
||||||
const data = getPanelOrigin(uid, name);
|
const data = getPanelOrigin(uid, name);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
await this.reply(`未找到角色${name}的面板信息,请先刷新面板`);
|
await this.reply(`未找到角色${name}的面板信息,请确保角色名称/别称存在且已更新面板`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let handler = this.e.runtime.handler || {};
|
let handler = this.e.runtime.handler || {};
|
||||||
|
|
@ -201,7 +223,7 @@ export class Panel extends ZZZPlugin {
|
||||||
const uid = await this.getUID();
|
const uid = await this.getUID();
|
||||||
const result = getPanelList(uid);
|
const result = getPanelList(uid);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
await this.reply('未找到面板数据,请先%刷新面板');
|
await this.reply('未找到面板数据,请先%更新面板 或 %更新展柜面板');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await this.getPlayerInfo();
|
await this.getPlayerInfo();
|
||||||
|
|
|
||||||
|
|
@ -102,17 +102,24 @@ export const refreshPanel = async (api, deviceFp) => {
|
||||||
// 获取新数据
|
// 获取新数据
|
||||||
const newData = await getAvatarInfoList(api, deviceFp, true);
|
const newData = await getAvatarInfoList(api, deviceFp, true);
|
||||||
if (!newData) return null;
|
if (!newData) return null;
|
||||||
|
return mergePanel(api.uid, newData);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并保存新面板
|
||||||
|
* @param {string|number} uid UID
|
||||||
|
* @param {import('../model/Enka/interface').Mys.Avatar[]} newData
|
||||||
|
*/
|
||||||
|
export const mergePanel = async (uid, newData) => {
|
||||||
// 合并新旧数据
|
// 合并新旧数据
|
||||||
const finalData = updatePanelData(api.uid, newData);
|
const finalData = updatePanelData(uid, newData);
|
||||||
|
|
||||||
const formattedData = finalData.map(item => new ZZZAvatarInfo(item));
|
const formattedData = finalData.map(item => new ZZZAvatarInfo(item));
|
||||||
for (const item of formattedData) {
|
for (const item of formattedData) {
|
||||||
// 下载图片资源
|
// 下载图片资源
|
||||||
await item.get_basic_assets();
|
await item.get_basic_assets();
|
||||||
}
|
}
|
||||||
return formattedData;
|
return formattedData;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*获取面板数据
|
*获取面板数据
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import settings from '../settings.js';
|
import settings from '../settings.js'
|
||||||
// import PartnerId2SpriteId from '../../resources/map/PartnerId2Data.json' assert { type: 'json' };
|
// import PartnerId2SpriteId from '../../resources/map/PartnerId2Data.json' assert { type: 'json' };
|
||||||
import { getMapData } from '../../utils/file.js';
|
import { getMapData } from '../../utils/file.js'
|
||||||
|
|
||||||
const PartnerId2SpriteId = getMapData('PartnerId2Data');
|
/** @type {import('../../model/Enka/interface.js').Map.PartnerId2Data} */
|
||||||
|
const PartnerId2Data = getMapData('PartnerId2Data')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -12,12 +13,12 @@ const PartnerId2SpriteId = getMapData('PartnerId2Data');
|
||||||
* @returns {string | null}
|
* @returns {string | null}
|
||||||
*/
|
*/
|
||||||
export const IDToCharName = (id, full = true, en = false) => {
|
export const IDToCharName = (id, full = true, en = false) => {
|
||||||
const data = PartnerId2SpriteId?.[id];
|
const data = PartnerId2Data?.[id]
|
||||||
if (!data) return null;
|
if (!data) return null
|
||||||
if (en) return data?.['en_name'];
|
if (en) return data?.['en_name']
|
||||||
if (full) return data?.['full_name'];
|
if (full) return data?.['full_name']
|
||||||
return data?.['name'];
|
return data?.['name']
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -25,73 +26,73 @@ export const IDToCharName = (id, full = true, en = false) => {
|
||||||
* @returns {string | null}
|
* @returns {string | null}
|
||||||
*/
|
*/
|
||||||
export const IDToCharSprite = id => {
|
export const IDToCharSprite = id => {
|
||||||
const data = PartnerId2SpriteId?.[id];
|
const data = PartnerId2Data?.[id]
|
||||||
if (!data) return null;
|
if (!data) return null
|
||||||
return data?.['sprite_id'];
|
return data?.['sprite_id']
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {number | null}
|
* @returns {number | null}
|
||||||
*/
|
*/
|
||||||
export const charNameToID = name => {
|
export const charNameToID = name => {
|
||||||
for (const [id, data] of Object.entries(PartnerId2SpriteId)) {
|
for (const [id, data] of Object.entries(PartnerId2Data)) {
|
||||||
if (data['name'] === name) return Number(id);
|
if (data['name'] === name) return Number(id)
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {string | null}
|
* @returns {string | null}
|
||||||
*/
|
*/
|
||||||
export const charNameToSprite = name => {
|
export const charNameToSprite = name => {
|
||||||
for (const [_id, data] of Object.entries(PartnerId2SpriteId)) {
|
for (const [_id, data] of Object.entries(PartnerId2Data)) {
|
||||||
if (data['name'] === name) return data['sprite'];
|
if (data['name'] === name) return data['sprite_id']
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} _alias
|
* @param {string} _alias
|
||||||
* @returns {string | null}
|
* @returns {string | null}
|
||||||
*/
|
*/
|
||||||
export const aliasToName = _alias => {
|
export const aliasToName = _alias => {
|
||||||
const alias = settings.getConfig('alias');
|
const alias = settings.getConfig('alias')
|
||||||
for (const [id, data] of Object.entries(alias)) {
|
for (const [id, data] of Object.entries(alias)) {
|
||||||
if (id === _alias) return id;
|
if (id === _alias) return id
|
||||||
if (data.includes(_alias)) return id;
|
if (data.includes(_alias)) return id
|
||||||
}
|
}
|
||||||
// 判断PartnerId2SpriteId是否有对应的name
|
// 判断PartnerId2SpriteId是否有对应的name
|
||||||
for (const [_, data] of Object.entries(PartnerId2SpriteId)) {
|
for (const [_, data] of Object.entries(PartnerId2Data)) {
|
||||||
if (data['name'] === _alias) return data['name'];
|
if (data['name'] === _alias) return data['name']
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} _alias
|
* @param {string} _alias
|
||||||
* @returns {string | null}
|
* @returns {string | null}
|
||||||
*/
|
*/
|
||||||
export const aliasToSprite = _alias => {
|
export const aliasToSprite = _alias => {
|
||||||
const name = aliasToName(_alias);
|
const name = aliasToName(_alias)
|
||||||
return charNameToSprite(name);
|
return charNameToSprite(name)
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {number | null}
|
* @returns {number | null}
|
||||||
*/
|
*/
|
||||||
export const aliasToID = name => {
|
export const aliasToID = name => {
|
||||||
const _name = aliasToName(name);
|
const _name = aliasToName(name)
|
||||||
const id = charNameToID(_name);
|
const id = charNameToID(_name)
|
||||||
return id;
|
return id
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有角色ID
|
* 获取所有角色ID
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
export const getAllCharactersID = () => {
|
export const getAllCharactersID = () => {
|
||||||
return Object.keys(PartnerId2SpriteId);
|
return Object.keys(PartnerId2Data)
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { getMapData } from '../../utils/file.js';
|
import { getMapData } from '../../utils/file.js'
|
||||||
|
|
||||||
const equipData = getMapData('EquipId2Data');
|
/** @type {import('../../model/Enka/interface.js').Map.EquipId2Data} */
|
||||||
|
const EquipId2Data = getMapData('EquipId2Data')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取驱动盘装备的图片
|
* 获取驱动盘装备的图片
|
||||||
|
|
@ -8,14 +9,14 @@ const equipData = getMapData('EquipId2Data');
|
||||||
* @returns {string | null}
|
* @returns {string | null}
|
||||||
*/
|
*/
|
||||||
export function equipIdToSprite(equipId) {
|
export function equipIdToSprite(equipId) {
|
||||||
equipId = equipId.toString();
|
equipId = equipId.toString()
|
||||||
if (equipId.length === 5) {
|
if (equipId.length === 5) {
|
||||||
const suitId = equipId.slice(0, 3) + '00';
|
const suitId = equipId.slice(0, 3) + '00'
|
||||||
if (equipData.hasOwnProperty(suitId)) {
|
if (EquipId2Data.hasOwnProperty(suitId)) {
|
||||||
return equipData[suitId]['sprite_file'].replace('3D', '');
|
return EquipId2Data[suitId]['sprite_file'].replace('3D', '')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,5 +24,5 @@ export function equipIdToSprite(equipId) {
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
export function getAllEquipID() {
|
export function getAllEquipID() {
|
||||||
return Object.keys(equipData);
|
return Object.keys(EquipId2Data)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ export const idToShortName3 = id => {
|
||||||
*/
|
*/
|
||||||
export const nameToShortName3 = propName => {
|
export const nameToShortName3 = propName => {
|
||||||
for (const id in propertyData) {
|
for (const id in propertyData) {
|
||||||
if (propertyData[id]?.[1] === propName) return propertyData[id][3];
|
if (propertyData[id][1] === propName) return propertyData[id][3];
|
||||||
};
|
};
|
||||||
return propName;
|
return propName;
|
||||||
};
|
};
|
||||||
|
|
@ -101,13 +101,15 @@ export const nameToShortName3 = propName => {
|
||||||
/**
|
/**
|
||||||
* 属性名转id
|
* 属性名转id
|
||||||
* @param {string} propName 属性名
|
* @param {string} propName 属性名
|
||||||
* @returns {string}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
export const nameToId = (propName) => {
|
export const nameToId = (propName) => {
|
||||||
for (const id in propertyData) {
|
for (const id in propertyData) {
|
||||||
if (propertyData[id]?.[1] === propName) return Number(id);
|
if (propertyData[id][1] === propName ||
|
||||||
|
propertyData[id][1].replace('属性', '') === propName
|
||||||
|
) return Number(id);
|
||||||
};
|
};
|
||||||
return '';
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,33 @@
|
||||||
// import WeaponId2Sprite from '../../resources/map/WeaponId2Sprite.json' assert { type: 'json' };
|
// import WeaponId2Sprite from '../../resources/map/WeaponId2Sprite.json' assert { type: 'json' };
|
||||||
import { getMapData } from '../../utils/file.js';
|
import { getMapData } from '../../utils/file.js'
|
||||||
|
|
||||||
const WeaponId2Sprite = getMapData('WeaponId2Sprite');
|
/** @type {import('../../model/Enka/interface.js').Map.WeaponId2Data} */
|
||||||
|
const WeaponId2Data = getMapData('WeaponId2Data')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export const IDToWeaponFileName = id => {
|
export const IDToWeaponFileName = id => {
|
||||||
const data = WeaponId2Sprite?.[id];
|
const data = WeaponId2Data?.[id]?.CodeName
|
||||||
return data;
|
return data
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export const weaponFileNameToID = name => {
|
export const weaponFileNameToID = name => {
|
||||||
for (const [id, data] of Object.entries(WeaponId2Sprite)) {
|
for (const [id, data] of Object.entries(WeaponId2Data)) {
|
||||||
if (data === name) return id;
|
if (data.CodeName === name) return id
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有武器的ID
|
* 获取所有武器的ID
|
||||||
* @returns string[]
|
* @returns string[]
|
||||||
*/
|
*/
|
||||||
export const getAllWeaponID = () => {
|
export const getAllWeaponID = () => {
|
||||||
return Object.keys(WeaponId2Sprite);
|
return Object.keys(WeaponId2Data)
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export function saveGachaLog(uid, data) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} uid
|
* @param {string} uid
|
||||||
* @returns {Array<object>}
|
* @returns {import('./../model/Enka/interface').Mys.Avatar[]}
|
||||||
*/
|
*/
|
||||||
export function getPanelData(uid) {
|
export function getPanelData(uid) {
|
||||||
return getDB('panel', uid) || [];
|
return getDB('panel', uid) || [];
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export default class MysZZZApi extends MysApi {
|
||||||
return item.ck && item.uid === uid;
|
return item.ck && item.uid === uid;
|
||||||
});
|
});
|
||||||
if (!ck) {
|
if (!ck) {
|
||||||
throw new Error(`[ZZZ]要查询的UID:${uid}未绑定Cookie`);
|
throw new Error(`[ZZZ]UID:${uid}未绑定Cookie。若无法更新面板可尝试%更新展柜面板(所更新角色数据与实际不一致时,请提issue)`);
|
||||||
}
|
}
|
||||||
this._device = ck?.device_id || ck?.device;
|
this._device = ck?.device_id || ck?.device;
|
||||||
this.cookie = ck?.ck;
|
this.cookie = ck?.ck;
|
||||||
|
|
|
||||||
|
|
@ -158,19 +158,21 @@ export class ZZZPlugin extends plugin {
|
||||||
* 获取玩家信息(当调用此方法时,会获取用户的玩家信息,并将其保存到`e.playerCard`中,方便渲染用户信息(此部分请查阅`lib/render.js`中两个模块的作用))
|
* 获取玩家信息(当调用此方法时,会获取用户的玩家信息,并将其保存到`e.playerCard`中,方便渲染用户信息(此部分请查阅`lib/render.js`中两个模块的作用))
|
||||||
* @returns {Promise<boolean | object>}
|
* @returns {Promise<boolean | object>}
|
||||||
*/
|
*/
|
||||||
async getPlayerInfo() {
|
async getPlayerInfo(playerData = null) {
|
||||||
// 获取 米游社 API
|
if (!playerData) {
|
||||||
const { api, uid } = await this.getAPI();
|
// 获取 米游社 API
|
||||||
// 获取用户信息
|
const { api, uid } = await this.getAPI();
|
||||||
let userData = await api.getFinalData('zzzUser').catch(e => {
|
// 获取用户信息
|
||||||
this.reply(e.message);
|
playerData = await api.getFinalData('zzzUser').catch(e => {
|
||||||
throw e;
|
this.reply(e.message);
|
||||||
});
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
if (!userData) throw new Error('获取用户数据失败');
|
if (!playerData) throw new Error('获取用户数据失败');
|
||||||
// 取第一个用户信息
|
// 取第一个用户信息
|
||||||
userData =
|
playerData =
|
||||||
userData?.list?.find(item => item.game_uid == uid) || userData?.list?.[0];
|
playerData?.list?.find(item => item.game_uid == uid) || playerData?.list?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
// 获取用户头像
|
// 获取用户头像
|
||||||
let avatar = '';
|
let avatar = '';
|
||||||
|
|
@ -184,10 +186,10 @@ export class ZZZPlugin extends plugin {
|
||||||
// 写入数据
|
// 写入数据
|
||||||
this.e.playerCard = {
|
this.e.playerCard = {
|
||||||
avatar: avatar,
|
avatar: avatar,
|
||||||
player: userData,
|
player: playerData,
|
||||||
};
|
};
|
||||||
// 返回数据
|
// 返回数据
|
||||||
return userData;
|
return playerData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
64
model/Enka/enkaApi.js
Normal file
64
model/Enka/enkaApi.js
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { Enka2Mys } from './formater.js'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
|
||||||
|
const EnkaApi = 'https://enka.network/api/zzz/uid/'
|
||||||
|
|
||||||
|
export function parsePlayerInfo(SocialDetail = {}) {
|
||||||
|
const ProfileDetail = SocialDetail.ProfileDetail || {}
|
||||||
|
return {
|
||||||
|
game_biz: 'nap_cn',
|
||||||
|
region: 'prod_gf_cn',
|
||||||
|
game_uid: ProfileDetail.Uid || SocialDetail.uid || '114514',
|
||||||
|
nickname: ProfileDetail.Nickname || 'Fairy',
|
||||||
|
level: ProfileDetail.Level || 60,
|
||||||
|
is_chosen: true,
|
||||||
|
region_name: '新艾利都',
|
||||||
|
is_official: true,
|
||||||
|
desc: SocialDetail.Desc || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enka更新面板
|
||||||
|
* @param {string|number} uid
|
||||||
|
*/
|
||||||
|
export async function refreshPanelFromEnka(uid) {
|
||||||
|
const res = await fetch(`${EnkaApi}${uid}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'ZZZ-Plugin/UCPr',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!res.ok) {
|
||||||
|
logger.warn(`Enka更新面板失败:${res.status} ${res.statusText}`)
|
||||||
|
return res.status
|
||||||
|
}
|
||||||
|
const data = await res.json()
|
||||||
|
/** @type {import('./interface.ts').Enka.Avatar[]} */
|
||||||
|
const panelList = data?.PlayerInfo?.ShowcaseDetail?.AvatarList
|
||||||
|
if (!panelList || !Array.isArray(panelList)) {
|
||||||
|
logger.warn('Enka更新面板失败:获取面板数据失败')
|
||||||
|
return res.status
|
||||||
|
}
|
||||||
|
if (!panelList.length)
|
||||||
|
console.log('面板列表为空')
|
||||||
|
return {
|
||||||
|
playerInfo: parsePlayerInfo(data.PlayerInfo.SocialDetail),
|
||||||
|
panelList: Enka2Mys(panelList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// import fs from 'fs'
|
||||||
|
// const uid = 11070609
|
||||||
|
// const res = await fetch(`${EnkaApi}${uid}`, {
|
||||||
|
// method: 'GET',
|
||||||
|
// headers: {
|
||||||
|
// 'User-Agent': 'ZZZ-Plugin/UCPr',
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// if (!res.ok) {
|
||||||
|
// console.log(`Enka更新面板失败:${res.status} ${res.statusText}`)
|
||||||
|
// }
|
||||||
|
// const data = await res.json()
|
||||||
|
// console.log(data)
|
||||||
|
// fs.writeFileSync('enkaPanel1.json', JSON.stringify(data, null, 2))
|
||||||
524
model/Enka/formater.js
Normal file
524
model/Enka/formater.js
Normal file
|
|
@ -0,0 +1,524 @@
|
||||||
|
import { getMapData } from '../../utils/file.js';
|
||||||
|
var Rarity;
|
||||||
|
(function (Rarity) {
|
||||||
|
Rarity[Rarity["S"] = 4] = "S";
|
||||||
|
Rarity[Rarity["A"] = 3] = "A";
|
||||||
|
Rarity[Rarity["B"] = 2] = "B";
|
||||||
|
})(Rarity || (Rarity = {}));
|
||||||
|
const WeaponId2Data = getMapData('WeaponId2Data');
|
||||||
|
const PartnerId2Data = getMapData('PartnerId2Data');
|
||||||
|
const EquipId2Data = getMapData('EquipId2Data');
|
||||||
|
const SuitData = getMapData('SuitData');
|
||||||
|
const id2zh = {
|
||||||
|
111: '生命值',
|
||||||
|
121: '攻击力',
|
||||||
|
122: '冲击力',
|
||||||
|
131: '防御力',
|
||||||
|
201: '暴击率',
|
||||||
|
211: '暴击伤害',
|
||||||
|
231: '穿透率',
|
||||||
|
232: '穿透值',
|
||||||
|
305: '能量自动回复',
|
||||||
|
312: '异常精通',
|
||||||
|
314: '异常掌控',
|
||||||
|
315: '物理伤害加成',
|
||||||
|
316: '火属性伤害加成',
|
||||||
|
317: '冰属性伤害加成',
|
||||||
|
318: '电属性伤害加成',
|
||||||
|
319: '以太伤害加成'
|
||||||
|
};
|
||||||
|
const zh2id = Object.fromEntries(Object.entries(id2zh).map(([key, value]) => [value, +key]));
|
||||||
|
const id2en = {
|
||||||
|
111: 'HpMax',
|
||||||
|
121: 'Attack',
|
||||||
|
122: 'BreakStun',
|
||||||
|
131: 'Defence',
|
||||||
|
201: 'Crit',
|
||||||
|
211: 'CritDamage',
|
||||||
|
231: 'PenRate',
|
||||||
|
232: 'PenDelta',
|
||||||
|
305: 'SpRecover',
|
||||||
|
312: 'ElementMystery',
|
||||||
|
314: 'ElementAbnormalPower',
|
||||||
|
315: 'PhysDmgBonus',
|
||||||
|
316: 'FireDmgBonus',
|
||||||
|
317: 'IceDmgBonus',
|
||||||
|
318: 'ThunderDmgBonus',
|
||||||
|
319: 'EtherDmgBonus'
|
||||||
|
};
|
||||||
|
const en2id = Object.fromEntries(Object.entries(id2en).map(([key, value]) => [value, +key]));
|
||||||
|
const percentPropId = [11102, 12102, 12202, 13102, 20103, 21103, 23103, 30502, 31402, 31503, 31603, 31703, 31803, 31903];
|
||||||
|
function get_base(propId, value) {
|
||||||
|
if (percentPropId.includes(propId)) {
|
||||||
|
const v = value / 100;
|
||||||
|
return v.toFixed(v % 1 === 0 ? 0 : 1) + '%';
|
||||||
|
}
|
||||||
|
return Math.trunc(value).toString();
|
||||||
|
}
|
||||||
|
export class Equip {
|
||||||
|
enkaEquip;
|
||||||
|
Equipment;
|
||||||
|
id;
|
||||||
|
data;
|
||||||
|
info;
|
||||||
|
equip;
|
||||||
|
constructor(enkaEquip) {
|
||||||
|
this.enkaEquip = enkaEquip;
|
||||||
|
this.Equipment = this.enkaEquip.Equipment;
|
||||||
|
this.id = this.Equipment.Id;
|
||||||
|
this.data = EquipId2Data[`${this.id.toString().slice(0, 3)}00`];
|
||||||
|
if (!this.data) {
|
||||||
|
throw new Error(`驱动盘数据缺失: ${this.id}`);
|
||||||
|
}
|
||||||
|
this.info = this.init();
|
||||||
|
this.equip = this.info;
|
||||||
|
}
|
||||||
|
static main(EquippedList) {
|
||||||
|
const equips = [];
|
||||||
|
for (const equip of EquippedList) {
|
||||||
|
if (equip.Equipment?.Id) {
|
||||||
|
const e = new Equip(equip);
|
||||||
|
equips.push(e.main());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cache = {};
|
||||||
|
for (const equip of equips) {
|
||||||
|
const suit_id = equip.equip_suit.suit_id;
|
||||||
|
cache[suit_id] ??= equips.reduce((acc, cur) => {
|
||||||
|
if (cur.equip_suit.suit_id === suit_id) {
|
||||||
|
return acc + 1;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
equip.equip_suit.own = cache[suit_id];
|
||||||
|
}
|
||||||
|
return equips;
|
||||||
|
}
|
||||||
|
main() {
|
||||||
|
this.equip.properties = this.properties(this.Equipment.RandomPropertyList, false);
|
||||||
|
this.equip.main_properties = this.properties(this.Equipment.MainPropertyList, true);
|
||||||
|
this.equip.equip_suit = this.equip_suit();
|
||||||
|
return this.equip;
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
level: this.Equipment.Level,
|
||||||
|
name: `${this.data.equip_name}[${this.enkaEquip.Slot}]`,
|
||||||
|
icon: '',
|
||||||
|
rarity: Rarity[+String(this.id)[3]] || 'S',
|
||||||
|
equipment_type: this.enkaEquip.Slot,
|
||||||
|
invalid_property_cnt: 0,
|
||||||
|
all_hit: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
properties(enkaProperties, isMain) {
|
||||||
|
const properties = [];
|
||||||
|
for (const p of enkaProperties) {
|
||||||
|
const property = {};
|
||||||
|
const propId = p.PropertyId;
|
||||||
|
property.property_name = id2zh[propId.toString().slice(0, 3)];
|
||||||
|
property.property_id = propId;
|
||||||
|
const value = p.PropertyValue * (isMain ? (1 + this.info.level * ({
|
||||||
|
S: 0.2,
|
||||||
|
A: 0.25,
|
||||||
|
B: 0.3
|
||||||
|
})[this.info.rarity]) : p.PropertyLevel);
|
||||||
|
property.base = get_base(propId, value);
|
||||||
|
property.level = p.PropertyLevel;
|
||||||
|
property.valid = false;
|
||||||
|
property.system_id = 0;
|
||||||
|
property.add = p.PropertyLevel - 1;
|
||||||
|
properties.push(property);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
equip_suit() {
|
||||||
|
return {
|
||||||
|
suit_id: +`${this.id.toString().slice(0, 3)}00`,
|
||||||
|
name: this.data.equip_name,
|
||||||
|
own: 0,
|
||||||
|
desc1: this.data.desc1,
|
||||||
|
desc2: this.data.desc2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Weapon {
|
||||||
|
enkaWeapon;
|
||||||
|
id;
|
||||||
|
data;
|
||||||
|
info;
|
||||||
|
weapon;
|
||||||
|
constructor(enkaWeapon) {
|
||||||
|
this.enkaWeapon = enkaWeapon;
|
||||||
|
this.id = this.enkaWeapon.Id;
|
||||||
|
this.data = WeaponId2Data[this.id];
|
||||||
|
if (!this.data) {
|
||||||
|
throw new Error(`音擎数据缺失: ${this.id}`);
|
||||||
|
}
|
||||||
|
this.info = this.init();
|
||||||
|
this.weapon = this.info;
|
||||||
|
}
|
||||||
|
static main(enkaWeapon) {
|
||||||
|
if (!enkaWeapon)
|
||||||
|
return null;
|
||||||
|
const weapon = new Weapon(enkaWeapon);
|
||||||
|
return weapon.main();
|
||||||
|
}
|
||||||
|
main() {
|
||||||
|
this.weapon.properties = this.properties(false);
|
||||||
|
this.weapon.main_properties = this.properties(true);
|
||||||
|
return this.weapon;
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
level: this.enkaWeapon.Level,
|
||||||
|
name: this.data.Name,
|
||||||
|
star: this.enkaWeapon.UpgradeLevel,
|
||||||
|
icon: '',
|
||||||
|
rarity: this.data.Rarity,
|
||||||
|
talent_title: this.data.Talents[1].Name,
|
||||||
|
talent_content: this.data.Talents[1].Desc,
|
||||||
|
profession: this.data.Profession
|
||||||
|
};
|
||||||
|
}
|
||||||
|
properties(isMain) {
|
||||||
|
const property = {};
|
||||||
|
const p = this.data[isMain ? 'BaseProperty' : 'RandProperty'];
|
||||||
|
const propId = p.Id;
|
||||||
|
property.property_name = id2zh[propId.toString().slice(0, 3)];
|
||||||
|
if (isMain && property.property_name === '攻击力') {
|
||||||
|
property.property_name = '基础攻击力';
|
||||||
|
}
|
||||||
|
property.property_id = propId;
|
||||||
|
let value;
|
||||||
|
const baseValue = p.Value;
|
||||||
|
if (isMain) {
|
||||||
|
value = baseValue * (1 +
|
||||||
|
(this.data.Level[this.info.level].Rate +
|
||||||
|
this.data.Stars[this.enkaWeapon.BreakLevel].StarRate) / 10000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = baseValue * (1 + this.data.Stars[this.enkaWeapon.BreakLevel].RandRate / 10000);
|
||||||
|
}
|
||||||
|
property.base = get_base(propId, value);
|
||||||
|
property.level = 0;
|
||||||
|
property.valid = false;
|
||||||
|
property.system_id = 0;
|
||||||
|
property.add = 0;
|
||||||
|
return [property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let isToFixed = true;
|
||||||
|
export class Property {
|
||||||
|
info;
|
||||||
|
equips;
|
||||||
|
weapon;
|
||||||
|
enkaAvatar;
|
||||||
|
data;
|
||||||
|
keepPercent = [201, 211, 231, 315, 316, 317, 318, 319];
|
||||||
|
constructor(info, equips, weapon, enkaAvatar) {
|
||||||
|
this.info = info;
|
||||||
|
this.equips = equips;
|
||||||
|
this.weapon = weapon;
|
||||||
|
this.enkaAvatar = enkaAvatar;
|
||||||
|
this.data = PartnerId2Data[this.info.id];
|
||||||
|
}
|
||||||
|
static main(info, equips, weapon, enkaAvatar) {
|
||||||
|
return new Property(info, equips, weapon, enkaAvatar).main();
|
||||||
|
}
|
||||||
|
main() {
|
||||||
|
const initial = this.initial();
|
||||||
|
const properties = initial.map(prop => {
|
||||||
|
const propId = prop.property_id;
|
||||||
|
const isPercent = this.keepPercent.includes(propId);
|
||||||
|
if (!isToFixed) {
|
||||||
|
return {
|
||||||
|
property_name: prop.property_name,
|
||||||
|
property_id: prop.property_id,
|
||||||
|
base: isPercent ? `${prop.base}%` : `${prop.base}`,
|
||||||
|
add: isPercent ? `${prop.add}%` : `${prop.add}`,
|
||||||
|
final: isPercent ? `${prop.final}%` : `${prop.final}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const isTofixed2 = propId === 305;
|
||||||
|
return {
|
||||||
|
property_name: prop.property_name,
|
||||||
|
property_id: prop.property_id,
|
||||||
|
base: isPercent ? `${prop.base.toFixed(1)}%` : `${isTofixed2 ? prop.base.toFixed(2) : prop.base}`,
|
||||||
|
add: isPercent ? `${prop.add.toFixed(1)}%` : `${isTofixed2 ? prop.add.toFixed(2) : prop.add}`,
|
||||||
|
final: isPercent ? `${prop.final.toFixed(1)}%` : `${isTofixed2 ? prop.final.toFixed(2) : prop.final}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const elementType2PropId = {
|
||||||
|
200: 315,
|
||||||
|
201: 316,
|
||||||
|
202: 317,
|
||||||
|
203: 318,
|
||||||
|
205: 319
|
||||||
|
};
|
||||||
|
const elementIds = Object.values(elementType2PropId);
|
||||||
|
const propId = elementType2PropId[this.info.element_type];
|
||||||
|
if (propId) {
|
||||||
|
return properties.filter(prop => !elementIds.includes(prop.property_id) || prop.property_id === propId);
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
base() {
|
||||||
|
const base = {};
|
||||||
|
const ids = Object.keys(id2en).map(Number);
|
||||||
|
ids.forEach((id) => {
|
||||||
|
const prop = id2en[id];
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.data, prop)) {
|
||||||
|
base[id] = +this.data[prop] || 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
base[id] = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
[111, 121, 131].forEach((id) => {
|
||||||
|
const prop = id2en[id];
|
||||||
|
if (this.info.level) {
|
||||||
|
const growth = ({
|
||||||
|
111: this.data.HpGrowth,
|
||||||
|
121: this.data.AttackGrowth,
|
||||||
|
131: this.data.DefenceGrowth
|
||||||
|
})[id] || 0;
|
||||||
|
base[id] += (this.info.level - 1) * growth / 10000;
|
||||||
|
}
|
||||||
|
const PromotionLevel = this.enkaAvatar.PromotionLevel || 0;
|
||||||
|
base[id] += this.data.Level[PromotionLevel][prop] || 0;
|
||||||
|
});
|
||||||
|
const CoreSkillEnhancement = this.enkaAvatar.CoreSkillEnhancement || 0;
|
||||||
|
if (CoreSkillEnhancement) {
|
||||||
|
const extra = this.data.ExtraLevel[CoreSkillEnhancement].Extra;
|
||||||
|
const extraIds = Object.keys(extra).map(Number);
|
||||||
|
extraIds.forEach((id) => base[id.toString().slice(0, 3)] += extra[id].Value || 0);
|
||||||
|
}
|
||||||
|
if (this.weapon) {
|
||||||
|
for (const property of this.weapon.main_properties) {
|
||||||
|
base[property.property_id.toString().slice(0, 3)] += +property.base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ids.forEach(id => base[id] = Math.trunc(base[id]));
|
||||||
|
[201, 211, 231, 305].forEach(id => base[id] /= 100);
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
en2id(data) {
|
||||||
|
const idData = {};
|
||||||
|
const ens = Object.keys(data);
|
||||||
|
ens.forEach((en) => {
|
||||||
|
const id = en2id[en];
|
||||||
|
idData[id] = data[en] || 0;
|
||||||
|
});
|
||||||
|
return idData;
|
||||||
|
}
|
||||||
|
en2zh(data) {
|
||||||
|
const idData = this.en2id(data);
|
||||||
|
return this.id2zh(idData);
|
||||||
|
}
|
||||||
|
id2zh(idData) {
|
||||||
|
const zhData = {};
|
||||||
|
const ids = Object.keys(idData).map(Number);
|
||||||
|
ids.forEach(id => {
|
||||||
|
const zh = id2zh[id];
|
||||||
|
if (zhData[zh]) {
|
||||||
|
if (!idData[id])
|
||||||
|
return;
|
||||||
|
zhData[zh] += idData[id];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
zhData[zh] = idData[id] || 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return zhData;
|
||||||
|
}
|
||||||
|
initial(base = this.id2zh(this.base())) {
|
||||||
|
const properties = Object.entries(base)
|
||||||
|
.reduce((acc, [key, value]) => {
|
||||||
|
const property_id = zh2id[key];
|
||||||
|
acc[property_id] = {
|
||||||
|
property_name: key,
|
||||||
|
property_id,
|
||||||
|
base: value,
|
||||||
|
add: 0,
|
||||||
|
final: 0
|
||||||
|
};
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
const all_properties = [];
|
||||||
|
if (this.weapon?.properties.length) {
|
||||||
|
all_properties.push(...this.weapon.properties);
|
||||||
|
}
|
||||||
|
for (const equip of this.equips) {
|
||||||
|
equip.properties.length && all_properties.push(...equip.properties);
|
||||||
|
equip.main_properties.length && all_properties.push(...equip.main_properties);
|
||||||
|
}
|
||||||
|
const suitIds = Array.from(new Set(this.equips.filter(equip => equip.equip_suit.own >= 2).map(v => v.equip_suit.suit_id)));
|
||||||
|
for (const suitId of suitIds) {
|
||||||
|
const suitData = SuitData[suitId];
|
||||||
|
if (!suitData || !suitData.properties) {
|
||||||
|
logger.warn(`驱动盘套装数据缺失: ${suitId},跳过套装效果计算`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!suitData.properties.length)
|
||||||
|
continue;
|
||||||
|
all_properties.push(...suitData.properties);
|
||||||
|
}
|
||||||
|
for (const property of all_properties) {
|
||||||
|
const propId = +property.property_id.toString().slice(0, 3);
|
||||||
|
if (property.base.includes('%')) {
|
||||||
|
if (this.keepPercent.includes(propId)) {
|
||||||
|
properties[propId].add += +property.base.replace('%', '');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
properties[propId].add += properties[propId].base * +property.base.replace('%', '') / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
properties[propId].add += +property.base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sp = special[this.info.id];
|
||||||
|
if (sp && sp.initial_before_format) {
|
||||||
|
sp.initial_before_format(properties, this);
|
||||||
|
}
|
||||||
|
if (isToFixed) {
|
||||||
|
for (const propId in properties) {
|
||||||
|
const property = properties[propId];
|
||||||
|
if (this.keepPercent.includes(+propId)) {
|
||||||
|
property.add = +property.add.toFixed(1);
|
||||||
|
property.final = +(property.base + property.add).toFixed(1);
|
||||||
|
}
|
||||||
|
else if (propId === '305') {
|
||||||
|
property.add = +(Math.trunc(property.add * 100) / 100).toFixed(2);
|
||||||
|
property.final = +(property.base + property.add).toFixed(2);
|
||||||
|
}
|
||||||
|
else if (propId === '111') {
|
||||||
|
property.add = Math.ceil(property.add);
|
||||||
|
property.final = property.base + property.add;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
property.add = Math.trunc(property.add);
|
||||||
|
property.final = property.base + property.add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (const propId in properties) {
|
||||||
|
const property = properties[propId];
|
||||||
|
property.final = property.base + property.add;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sp && sp.initial_after_format) {
|
||||||
|
sp.initial_after_format(properties, this);
|
||||||
|
}
|
||||||
|
return Object.values(properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Skill {
|
||||||
|
static main(skills, rank) {
|
||||||
|
const result = skills.map(skill => ({
|
||||||
|
level: skill.Level,
|
||||||
|
skill_type: skill.Index,
|
||||||
|
items: []
|
||||||
|
}));
|
||||||
|
const rankLevel = rank >= 3 ? (rank >= 5 ? 4 : 2) : 0;
|
||||||
|
if (rankLevel) {
|
||||||
|
result.forEach(skill => {
|
||||||
|
if (skill.skill_type === 5)
|
||||||
|
return;
|
||||||
|
skill.level += rankLevel;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function parseInfo(enkaAvatar) {
|
||||||
|
const info = {};
|
||||||
|
info.id = enkaAvatar.Id;
|
||||||
|
const data = PartnerId2Data[info.id];
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
info.name_mi18n = data.name;
|
||||||
|
info.level = enkaAvatar.Level;
|
||||||
|
info.full_name_mi18n = data.full_name;
|
||||||
|
info.element_type = +data.ElementType;
|
||||||
|
info.camp_name_mi18n = data.Camp;
|
||||||
|
info.avatar_profession = +data.WeaponType;
|
||||||
|
info.rarity = data.Rarity;
|
||||||
|
info.rank = enkaAvatar.TalentLevel;
|
||||||
|
info.us_full_name = data.en_name;
|
||||||
|
info.sub_element_type = ({
|
||||||
|
1091: 1,
|
||||||
|
1371: 2,
|
||||||
|
})[info.id] || 0;
|
||||||
|
info.group_icon_path = '';
|
||||||
|
info.hollow_icon_path = '';
|
||||||
|
info.role_vertical_painting_url = '';
|
||||||
|
info.vertical_painting_color = '';
|
||||||
|
info.role_square_url = '';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
export function Enka2Mys(enkaAvatars, __isToFixed__ = true) {
|
||||||
|
isToFixed = __isToFixed__;
|
||||||
|
const avatars = Array.isArray(enkaAvatars) ? enkaAvatars : [enkaAvatars];
|
||||||
|
const results = [];
|
||||||
|
for (const enkaAvatar of avatars) {
|
||||||
|
try {
|
||||||
|
const info = parseInfo(enkaAvatar);
|
||||||
|
if (!info) {
|
||||||
|
throw new Error(`角色数据缺失: ${enkaAvatar.Id}`);
|
||||||
|
}
|
||||||
|
const avatar = info;
|
||||||
|
avatar.ranks = [];
|
||||||
|
avatar.equip = Equip.main(enkaAvatar.EquippedList);
|
||||||
|
avatar.weapon = Weapon.main(enkaAvatar.Weapon);
|
||||||
|
avatar.properties = Property.main(info, avatar.equip, avatar.weapon, enkaAvatar);
|
||||||
|
avatar.skills = Skill.main(enkaAvatar.SkillLevelList, enkaAvatar.TalentLevel);
|
||||||
|
results.push(avatar);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
logger.warn(`Enka数据失败 ID: ${enkaAvatar.Id}\n`, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.isArray(enkaAvatars) ? results : results[0];
|
||||||
|
}
|
||||||
|
const special = {
|
||||||
|
1121: {
|
||||||
|
id: 1121,
|
||||||
|
name: '本',
|
||||||
|
initial_before_format: (properties, { enkaAvatar }) => {
|
||||||
|
const core = [0.4, 0.46, 0.52, 0.6, 0.66, 0.72, 0.8];
|
||||||
|
const { CoreSkillEnhancement } = enkaAvatar;
|
||||||
|
const value = (core[CoreSkillEnhancement] || 0) * Math.trunc(properties[131].base + properties[131].add);
|
||||||
|
properties[121].add += Math.trunc(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1371: {
|
||||||
|
id: 1371,
|
||||||
|
name: '仪玄',
|
||||||
|
initial_after_format: (properties) => {
|
||||||
|
delete properties[231], delete properties[232];
|
||||||
|
delete properties[305];
|
||||||
|
const sheerForce = Math.trunc(0.1 * properties[111].final) +
|
||||||
|
Math.trunc(0.3 * properties[121].final);
|
||||||
|
properties[19] = {
|
||||||
|
property_name: '贯穿力',
|
||||||
|
property_id: 19,
|
||||||
|
base: 0,
|
||||||
|
add: sheerForce,
|
||||||
|
final: sheerForce
|
||||||
|
};
|
||||||
|
properties[20] = {
|
||||||
|
property_name: '闪能自动累积',
|
||||||
|
property_id: 20,
|
||||||
|
base: 2,
|
||||||
|
add: 0,
|
||||||
|
final: 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
608
model/Enka/formater.ts
Normal file
608
model/Enka/formater.ts
Normal file
|
|
@ -0,0 +1,608 @@
|
||||||
|
import type { Mys, Enka, Map } from './interface.js'
|
||||||
|
import { getMapData } from '../../utils/file.js'
|
||||||
|
|
||||||
|
type FilterValueType<T, U> = {
|
||||||
|
[K in keyof T as T[K] extends U ? K : never]: T[K]
|
||||||
|
} & {}
|
||||||
|
|
||||||
|
type Values<T> = T extends Record<string, infer U> ? U : never
|
||||||
|
|
||||||
|
enum Rarity {
|
||||||
|
S = 4,
|
||||||
|
A = 3,
|
||||||
|
B = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
const WeaponId2Data = getMapData('WeaponId2Data') as Map.WeaponId2Data
|
||||||
|
const PartnerId2Data = getMapData('PartnerId2Data') as Map.PartnerId2Data
|
||||||
|
const EquipId2Data = getMapData('EquipId2Data') as Map.EquipId2Data
|
||||||
|
const SuitData = getMapData('SuitData') as Map.SuitData
|
||||||
|
|
||||||
|
const id2zh = {
|
||||||
|
111: '生命值',
|
||||||
|
121: '攻击力',
|
||||||
|
122: '冲击力',
|
||||||
|
131: '防御力',
|
||||||
|
201: '暴击率',
|
||||||
|
211: '暴击伤害',
|
||||||
|
231: '穿透率',
|
||||||
|
232: '穿透值',
|
||||||
|
305: '能量自动回复',
|
||||||
|
312: '异常精通',
|
||||||
|
314: '异常掌控',
|
||||||
|
315: '物理伤害加成',
|
||||||
|
316: '火属性伤害加成',
|
||||||
|
317: '冰属性伤害加成',
|
||||||
|
318: '电属性伤害加成',
|
||||||
|
319: '以太伤害加成'
|
||||||
|
} as const
|
||||||
|
const zh2id = Object.fromEntries(
|
||||||
|
Object.entries(id2zh).map(([key, value]) => [value, +key])
|
||||||
|
) as Record<Zhs, number>
|
||||||
|
|
||||||
|
const id2en = {
|
||||||
|
111: 'HpMax',
|
||||||
|
121: 'Attack',
|
||||||
|
122: 'BreakStun',
|
||||||
|
131: 'Defence',
|
||||||
|
201: 'Crit',
|
||||||
|
211: 'CritDamage',
|
||||||
|
231: 'PenRate',
|
||||||
|
232: 'PenDelta',
|
||||||
|
305: 'SpRecover',
|
||||||
|
312: 'ElementMystery',
|
||||||
|
314: 'ElementAbnormalPower',
|
||||||
|
315: 'PhysDmgBonus',
|
||||||
|
316: 'FireDmgBonus',
|
||||||
|
317: 'IceDmgBonus',
|
||||||
|
318: 'ThunderDmgBonus',
|
||||||
|
319: 'EtherDmgBonus'
|
||||||
|
} as const
|
||||||
|
const en2id = Object.fromEntries(
|
||||||
|
Object.entries(id2en).map(([key, value]) => [value, +key])
|
||||||
|
) as Record<Ens, number>
|
||||||
|
|
||||||
|
type Ids = keyof typeof id2zh
|
||||||
|
type IdsString = `${Ids}`
|
||||||
|
type Zhs = Values<typeof id2zh>
|
||||||
|
type Ens = Values<typeof id2en>
|
||||||
|
|
||||||
|
const percentPropId = [11102, 12102, 12202, 13102, 20103, 21103, 23103, 30502, 31402, 31503, 31603, 31703, 31803, 31903]
|
||||||
|
function get_base(propId: number, value: number) {
|
||||||
|
if (percentPropId.includes(propId)) {
|
||||||
|
const v = value / 100
|
||||||
|
return v.toFixed(v % 1 === 0 ? 0 : 1) + '%'
|
||||||
|
}
|
||||||
|
return Math.trunc(value).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Equip {
|
||||||
|
readonly enkaEquip: Enka.Equip
|
||||||
|
readonly Equipment: Enka.Equip['Equipment']
|
||||||
|
readonly id: number
|
||||||
|
readonly data: Map.EquipId2Data[string]
|
||||||
|
readonly info: FilterValueType<Mys.Equip, string | number | boolean>
|
||||||
|
readonly equip: Mys.Equip
|
||||||
|
constructor(enkaEquip: Enka.Equip) {
|
||||||
|
this.enkaEquip = enkaEquip
|
||||||
|
this.Equipment = this.enkaEquip.Equipment
|
||||||
|
this.id = this.Equipment.Id
|
||||||
|
this.data = EquipId2Data[`${this.id.toString().slice(0, 3)}00`]
|
||||||
|
if (!this.data) {
|
||||||
|
throw new Error(`驱动盘数据缺失: ${this.id}`)
|
||||||
|
}
|
||||||
|
this.info = this.init()
|
||||||
|
this.equip = this.info as Mys.Equip
|
||||||
|
}
|
||||||
|
|
||||||
|
static main(EquippedList: Enka.Avatar['EquippedList']) {
|
||||||
|
const equips: Mys.Equip[] = []
|
||||||
|
for (const equip of EquippedList) {
|
||||||
|
if (equip.Equipment?.Id) {
|
||||||
|
const e = new Equip(equip)
|
||||||
|
equips.push(e.main())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 统计own
|
||||||
|
const cache: Record<number, number> = {}
|
||||||
|
for (const equip of equips) {
|
||||||
|
const suit_id = equip.equip_suit.suit_id
|
||||||
|
cache[suit_id] ??= equips.reduce((acc, cur) => {
|
||||||
|
if (cur.equip_suit.suit_id === suit_id) {
|
||||||
|
return acc + 1
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, 0)
|
||||||
|
equip.equip_suit.own = cache[suit_id]
|
||||||
|
}
|
||||||
|
return equips
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
this.equip.properties = this.properties(this.Equipment.RandomPropertyList, false)
|
||||||
|
this.equip.main_properties = this.properties(this.Equipment.MainPropertyList, true)
|
||||||
|
this.equip.equip_suit = this.equip_suit()
|
||||||
|
return this.equip
|
||||||
|
}
|
||||||
|
|
||||||
|
init(): Equip['info'] {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
level: this.Equipment.Level,
|
||||||
|
name: `${this.data.equip_name}[${this.enkaEquip.Slot}]`,
|
||||||
|
icon: '',
|
||||||
|
rarity: Rarity[+String(this.id)[3]] || 'S',
|
||||||
|
equipment_type: this.enkaEquip.Slot,
|
||||||
|
invalid_property_cnt: 0,
|
||||||
|
all_hit: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
properties(enkaProperties: Enka.Property[], isMain: boolean) {
|
||||||
|
const properties: Mys.Property[] = []
|
||||||
|
for (const p of enkaProperties) {
|
||||||
|
const property = {} as Mys.Property
|
||||||
|
const propId = p.PropertyId
|
||||||
|
property.property_name = id2zh[propId.toString().slice(0, 3) as IdsString]
|
||||||
|
property.property_id = propId
|
||||||
|
const value = p.PropertyValue * (isMain ? (1 + this.info.level * ({
|
||||||
|
S: 0.2,
|
||||||
|
A: 0.25,
|
||||||
|
B: 0.3
|
||||||
|
})[this.info.rarity as 'S' | 'A' | 'B']) : p.PropertyLevel)
|
||||||
|
property.base = get_base(propId, value)
|
||||||
|
property.level = p.PropertyLevel
|
||||||
|
property.valid = false
|
||||||
|
property.system_id = 0
|
||||||
|
property.add = p.PropertyLevel - 1
|
||||||
|
properties.push(property)
|
||||||
|
}
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
equip_suit(): Mys.Equip['equip_suit'] {
|
||||||
|
return {
|
||||||
|
suit_id: +`${this.id.toString().slice(0, 3)}00`,
|
||||||
|
name: this.data.equip_name,
|
||||||
|
own: 0,
|
||||||
|
desc1: this.data.desc1,
|
||||||
|
desc2: this.data.desc2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Weapon {
|
||||||
|
readonly id: number
|
||||||
|
readonly data: Map.WeaponId2Data[string]
|
||||||
|
readonly info: FilterValueType<Mys.Weapon, string | number | boolean>
|
||||||
|
readonly weapon: Mys.Weapon
|
||||||
|
constructor(readonly enkaWeapon: Enka.Weapon) {
|
||||||
|
this.id = this.enkaWeapon.Id
|
||||||
|
this.data = WeaponId2Data[this.id]
|
||||||
|
if (!this.data) {
|
||||||
|
throw new Error(`音擎数据缺失: ${this.id}`)
|
||||||
|
}
|
||||||
|
this.info = this.init()
|
||||||
|
this.weapon = this.info as Mys.Weapon
|
||||||
|
}
|
||||||
|
|
||||||
|
static main(enkaWeapon: Enka.Avatar['Weapon']) {
|
||||||
|
if (!enkaWeapon) return null
|
||||||
|
const weapon = new Weapon(enkaWeapon)
|
||||||
|
return weapon.main()
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
this.weapon.properties = this.properties(false)
|
||||||
|
this.weapon.main_properties = this.properties(true)
|
||||||
|
return this.weapon
|
||||||
|
}
|
||||||
|
|
||||||
|
init(): Weapon['info'] {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
level: this.enkaWeapon.Level,
|
||||||
|
name: this.data.Name,
|
||||||
|
star: this.enkaWeapon.UpgradeLevel,
|
||||||
|
icon: '',
|
||||||
|
rarity: this.data.Rarity,
|
||||||
|
talent_title: this.data.Talents[1].Name,
|
||||||
|
talent_content: this.data.Talents[1].Desc,
|
||||||
|
profession: this.data.Profession
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
properties(isMain: boolean) {
|
||||||
|
const property = {} as Mys.Property
|
||||||
|
const p = this.data[isMain ? 'BaseProperty' : 'RandProperty']
|
||||||
|
const propId = p.Id
|
||||||
|
property.property_name = id2zh[propId.toString().slice(0, 3) as IdsString]
|
||||||
|
if (isMain && property.property_name === '攻击力') {
|
||||||
|
property.property_name = '基础攻击力'
|
||||||
|
}
|
||||||
|
property.property_id = propId
|
||||||
|
// 计算属性值
|
||||||
|
let value: number
|
||||||
|
const baseValue = p.Value
|
||||||
|
if (isMain) { // 基础属性
|
||||||
|
value = baseValue * (1 +
|
||||||
|
(this.data.Level[this.info.level].Rate +
|
||||||
|
this.data.Stars[this.enkaWeapon.BreakLevel].StarRate
|
||||||
|
) / 10000)
|
||||||
|
} else { // 高级属性
|
||||||
|
value = baseValue * (1 + this.data.Stars[this.enkaWeapon.BreakLevel].RandRate / 10000)
|
||||||
|
}
|
||||||
|
property.base = get_base(propId, value)
|
||||||
|
property.level = 0
|
||||||
|
property.valid = false
|
||||||
|
property.system_id = 0
|
||||||
|
property.add = 0
|
||||||
|
return [property]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let isToFixed = true
|
||||||
|
|
||||||
|
type initialProperty = {
|
||||||
|
property_name: string
|
||||||
|
property_id: number
|
||||||
|
base: number
|
||||||
|
add: number
|
||||||
|
final: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Property {
|
||||||
|
readonly data: Map.PartnerId2Data[string]
|
||||||
|
readonly keepPercent = [201, 211, 231, 315, 316, 317, 318, 319]
|
||||||
|
constructor(
|
||||||
|
readonly info: FilterValueType<Mys.Avatar, string | number | boolean>,
|
||||||
|
readonly equips: Mys.Avatar['equip'],
|
||||||
|
readonly weapon: Mys.Avatar['weapon'],
|
||||||
|
readonly enkaAvatar: Enka.Avatar
|
||||||
|
) {
|
||||||
|
this.data = PartnerId2Data[this.info.id]
|
||||||
|
}
|
||||||
|
|
||||||
|
static main(info: FilterValueType<Mys.Avatar, string | number | boolean>, equips: Mys.Avatar['equip'], weapon: Mys.Avatar['weapon'], enkaAvatar: Enka.Avatar) {
|
||||||
|
return new Property(info, equips, weapon, enkaAvatar).main()
|
||||||
|
}
|
||||||
|
|
||||||
|
main(): Mys.AvatarProperty[] {
|
||||||
|
const initial = this.initial()
|
||||||
|
const properties = initial.map(prop => {
|
||||||
|
const propId = prop.property_id
|
||||||
|
const isPercent = this.keepPercent.includes(propId)
|
||||||
|
if (!isToFixed) {
|
||||||
|
return {
|
||||||
|
property_name: prop.property_name,
|
||||||
|
property_id: prop.property_id,
|
||||||
|
base: isPercent ? `${prop.base}%` : `${prop.base}`,
|
||||||
|
add: isPercent ? `${prop.add}%` : `${prop.add}`,
|
||||||
|
final: isPercent ? `${prop.final}%` : `${prop.final}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const isTofixed2 = propId === 305 // 能量自动回复
|
||||||
|
return {
|
||||||
|
property_name: prop.property_name,
|
||||||
|
property_id: prop.property_id,
|
||||||
|
base: isPercent ? `${prop.base.toFixed(1)}%` : `${isTofixed2 ? prop.base.toFixed(2) : prop.base}`,
|
||||||
|
add: isPercent ? `${prop.add.toFixed(1)}%` : `${isTofixed2 ? prop.add.toFixed(2) : prop.add}`,
|
||||||
|
final: isPercent ? `${prop.final.toFixed(1)}%` : `${isTofixed2 ? prop.final.toFixed(2) : prop.final}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 筛选属伤
|
||||||
|
const elementType2PropId: Record<number, number> = {
|
||||||
|
200: 315,
|
||||||
|
201: 316,
|
||||||
|
202: 317,
|
||||||
|
203: 318,
|
||||||
|
205: 319
|
||||||
|
}
|
||||||
|
const elementIds = Object.values(elementType2PropId)
|
||||||
|
const propId = elementType2PropId[this.info.element_type]
|
||||||
|
if (propId) {
|
||||||
|
return properties.filter(prop => !elementIds.includes(prop.property_id) || prop.property_id === propId)
|
||||||
|
}
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 计算基础属性 */
|
||||||
|
base() {
|
||||||
|
// const base = new Proxy({} as Record<IdsString, number>, {
|
||||||
|
// get(target, prop: IdsString) {
|
||||||
|
// return target[prop] || 0
|
||||||
|
// },
|
||||||
|
// set(target, prop: IdsString, value: number) {
|
||||||
|
// console.log(`${prop}: ${String(target[prop]).padStart(10, ' ')} => ${String(value).padEnd(10, ' ')} ${value - target[prop]}`)
|
||||||
|
// target[prop] = value
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
const base = {} as Record<IdsString, number>
|
||||||
|
const ids = Object.keys(id2en).map(Number) as (Ids)[]
|
||||||
|
ids.forEach((id) => {
|
||||||
|
const prop = id2en[id]
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.data, prop)) {
|
||||||
|
// @ts-expect-error
|
||||||
|
base[id] = +this.data[prop] || 0
|
||||||
|
} else {
|
||||||
|
base[id] = 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 生命、攻击、防御随等级额外提升
|
||||||
|
([111, 121, 131] as const).forEach((id) => {
|
||||||
|
const prop = id2en[id]
|
||||||
|
// 等级提升
|
||||||
|
if (this.info.level) {
|
||||||
|
const growth = ({
|
||||||
|
111: this.data.HpGrowth,
|
||||||
|
121: this.data.AttackGrowth,
|
||||||
|
131: this.data.DefenceGrowth
|
||||||
|
})[id] || 0
|
||||||
|
base[id] += (this.info.level - 1) * growth / 10000
|
||||||
|
}
|
||||||
|
// 突破提升
|
||||||
|
const PromotionLevel = this.enkaAvatar.PromotionLevel || 0
|
||||||
|
base[id] += this.data.Level[PromotionLevel][prop] || 0
|
||||||
|
})
|
||||||
|
// 核心技额外提升
|
||||||
|
const CoreSkillEnhancement = this.enkaAvatar.CoreSkillEnhancement || 0
|
||||||
|
if (CoreSkillEnhancement) {
|
||||||
|
const extra = this.data.ExtraLevel[CoreSkillEnhancement].Extra
|
||||||
|
const extraIds = Object.keys(extra).map(Number)
|
||||||
|
extraIds.forEach((id) => base[id.toString().slice(0, 3) as IdsString] += extra[id].Value || 0)
|
||||||
|
}
|
||||||
|
// 处理音擎基础属性
|
||||||
|
if (this.weapon) {
|
||||||
|
for (const property of this.weapon.main_properties) {
|
||||||
|
base[property.property_id.toString().slice(0, 3) as IdsString] += +property.base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// console.log('----------------base保留整数----------------')
|
||||||
|
// 保留整数
|
||||||
|
ids.forEach(id => base[id] = Math.trunc(base[id]));
|
||||||
|
([201, 211, 231, 305] as const).forEach(id => base[id] /= 100)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
en2id(data: Record<keyof typeof en2id, number>) {
|
||||||
|
const idData = {} as Record<IdsString, number>
|
||||||
|
const ens = Object.keys(data) as (keyof typeof en2id)[]
|
||||||
|
ens.forEach((en) => {
|
||||||
|
const id = en2id[en] as Ids
|
||||||
|
idData[id] = data[en] || 0
|
||||||
|
})
|
||||||
|
return idData
|
||||||
|
}
|
||||||
|
|
||||||
|
en2zh(data: Record<keyof typeof en2id, number>) {
|
||||||
|
const idData = this.en2id(data)
|
||||||
|
return this.id2zh(idData)
|
||||||
|
}
|
||||||
|
|
||||||
|
id2zh(idData: Record<IdsString, number>) {
|
||||||
|
const zhData = {} as typeof zh2id
|
||||||
|
const ids = Object.keys(idData).map(Number) as Ids[]
|
||||||
|
ids.forEach(id => {
|
||||||
|
const zh = id2zh[id]
|
||||||
|
if (zhData[zh]) {
|
||||||
|
if (!idData[id]) return
|
||||||
|
zhData[zh] += idData[id]
|
||||||
|
} else {
|
||||||
|
zhData[zh] = idData[id] || 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return zhData
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 计算初始属性 */
|
||||||
|
initial(base = this.id2zh(this.base())) {
|
||||||
|
const properties = Object.entries(base)
|
||||||
|
.reduce((
|
||||||
|
acc: Record<number, initialProperty>,
|
||||||
|
[key, value]
|
||||||
|
) => {
|
||||||
|
const property_id = zh2id[key as keyof typeof zh2id]
|
||||||
|
acc[property_id] = {
|
||||||
|
property_name: key,
|
||||||
|
property_id,
|
||||||
|
base: value,
|
||||||
|
add: 0,
|
||||||
|
final: 0
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
const all_properties: Mys.Property[] = []
|
||||||
|
// 处理音擎高级属性
|
||||||
|
if (this.weapon?.properties.length) {
|
||||||
|
all_properties.push(...this.weapon.properties)
|
||||||
|
}
|
||||||
|
// 处理驱动盘词条
|
||||||
|
for (const equip of this.equips) {
|
||||||
|
equip.properties.length && all_properties.push(...equip.properties)
|
||||||
|
equip.main_properties.length && all_properties.push(...equip.main_properties)
|
||||||
|
}
|
||||||
|
// 处理驱动盘套装效果
|
||||||
|
const suitIds = Array.from(new Set(this.equips.filter(equip => equip.equip_suit.own >= 2).map(v => v.equip_suit.suit_id)))
|
||||||
|
for (const suitId of suitIds) {
|
||||||
|
const suitData = SuitData[suitId]
|
||||||
|
if (!suitData || !suitData.properties) {
|
||||||
|
logger.warn(`驱动盘套装数据缺失: ${suitId},跳过套装效果计算`)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!suitData.properties.length) continue
|
||||||
|
all_properties.push(...suitData.properties)
|
||||||
|
}
|
||||||
|
for (const property of all_properties) {
|
||||||
|
const propId = +property.property_id.toString().slice(0, 3) as Ids
|
||||||
|
if (property.base.includes('%')) {
|
||||||
|
if (this.keepPercent.includes(propId)) {
|
||||||
|
properties[propId].add += +property.base.replace('%', '')
|
||||||
|
} else {
|
||||||
|
properties[propId].add += properties[propId].base * +property.base.replace('%', '') / 100
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
properties[propId].add += +property.base
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 格式化前特殊处理
|
||||||
|
const sp = special[this.info.id]
|
||||||
|
if (sp && sp.initial_before_format) {
|
||||||
|
sp.initial_before_format(properties, this)
|
||||||
|
}
|
||||||
|
// 格式化、计算final
|
||||||
|
if (isToFixed) {
|
||||||
|
for (const propId in properties) {
|
||||||
|
const property = properties[propId]
|
||||||
|
if (this.keepPercent.includes(+propId)) {
|
||||||
|
property.add = +property.add.toFixed(1)
|
||||||
|
property.final = +(property.base + property.add).toFixed(1)
|
||||||
|
} else if (propId === '305') { // 能量自动回复
|
||||||
|
property.add = +(Math.trunc(property.add * 100) / 100).toFixed(2)
|
||||||
|
property.final = +(property.base + property.add).toFixed(2)
|
||||||
|
} else if (propId === '111') { // 生命值
|
||||||
|
property.add = Math.ceil(property.add)
|
||||||
|
property.final = property.base + property.add
|
||||||
|
} else {
|
||||||
|
property.add = Math.trunc(property.add)
|
||||||
|
property.final = property.base + property.add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const propId in properties) {
|
||||||
|
const property = properties[propId]
|
||||||
|
property.final = property.base + property.add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 格式化后特殊处理
|
||||||
|
if (sp && sp.initial_after_format) {
|
||||||
|
sp.initial_after_format(properties, this)
|
||||||
|
}
|
||||||
|
return Object.values(properties)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Skill {
|
||||||
|
static main(skills: Enka.Avatar['SkillLevelList'], rank: Enka.Avatar['TalentLevel']) {
|
||||||
|
const result = skills.map(skill => ({
|
||||||
|
level: skill.Level,
|
||||||
|
skill_type: skill.Index,
|
||||||
|
items: []
|
||||||
|
}))
|
||||||
|
// >=3额外提升2,>=5额外提升4
|
||||||
|
const rankLevel = rank >= 3 ? (rank >= 5 ? 4 : 2) : 0
|
||||||
|
if (rankLevel) {
|
||||||
|
result.forEach(skill => {
|
||||||
|
if (skill.skill_type === 5) return
|
||||||
|
skill.level += rankLevel
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseInfo(enkaAvatar: Enka.Avatar) {
|
||||||
|
const info = {} as FilterValueType<Mys.Avatar, string | number | boolean>
|
||||||
|
info.id = enkaAvatar.Id
|
||||||
|
const data = PartnerId2Data[info.id]
|
||||||
|
if (!data) return
|
||||||
|
info.name_mi18n = data.name
|
||||||
|
info.level = enkaAvatar.Level
|
||||||
|
info.full_name_mi18n = data.full_name
|
||||||
|
info.element_type = +data.ElementType
|
||||||
|
info.camp_name_mi18n = data.Camp
|
||||||
|
info.avatar_profession = +data.WeaponType
|
||||||
|
info.rarity = data.Rarity
|
||||||
|
info.rank = enkaAvatar.TalentLevel
|
||||||
|
info.us_full_name = data.en_name
|
||||||
|
/** 特殊元素属性 */
|
||||||
|
info.sub_element_type = ({
|
||||||
|
1091: 1, // 星见雅
|
||||||
|
1371: 2, // 仪玄
|
||||||
|
})[info.id] || 0
|
||||||
|
info.group_icon_path = ''
|
||||||
|
info.hollow_icon_path = ''
|
||||||
|
info.role_vertical_painting_url = ''
|
||||||
|
info.vertical_painting_color = ''
|
||||||
|
info.role_square_url = ''
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Enka2Mys(enkaAvatar: Enka.Avatar, __isToFixed__?: boolean): Mys.Avatar
|
||||||
|
export function Enka2Mys(enkaAvatars: Enka.Avatar[], __isToFixed__?: boolean): Mys.Avatar[]
|
||||||
|
export function Enka2Mys(enkaAvatars: Enka.Avatar | Enka.Avatar[], __isToFixed__ = true) {
|
||||||
|
isToFixed = __isToFixed__
|
||||||
|
const avatars = Array.isArray(enkaAvatars) ? enkaAvatars : [enkaAvatars]
|
||||||
|
const results: Mys.Avatar[] = []
|
||||||
|
for (const enkaAvatar of avatars) {
|
||||||
|
try {
|
||||||
|
const info = parseInfo(enkaAvatar)
|
||||||
|
if (!info) {
|
||||||
|
throw new Error(`角色数据缺失: ${enkaAvatar.Id}`)
|
||||||
|
}
|
||||||
|
const avatar = info as Mys.Avatar
|
||||||
|
avatar.ranks = []
|
||||||
|
avatar.equip = Equip.main(enkaAvatar.EquippedList)
|
||||||
|
avatar.weapon = Weapon.main(enkaAvatar.Weapon)
|
||||||
|
avatar.properties = Property.main(info, avatar.equip, avatar.weapon, enkaAvatar)
|
||||||
|
avatar.skills = Skill.main(enkaAvatar.SkillLevelList, enkaAvatar.TalentLevel)
|
||||||
|
results.push(avatar)
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`Enka数据失败 ID: ${enkaAvatar.Id}\n`, error)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.isArray(enkaAvatars) ? results : results[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 特殊处理 */
|
||||||
|
const special: Record<number, {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
initial_before_format?: (
|
||||||
|
properties: Record<number, initialProperty>,
|
||||||
|
propertyClient: Property
|
||||||
|
) => void
|
||||||
|
initial_after_format?: (
|
||||||
|
properties: Record<number, initialProperty>,
|
||||||
|
propertyClient: Property
|
||||||
|
) => void
|
||||||
|
}> = {
|
||||||
|
1121: {
|
||||||
|
id: 1121,
|
||||||
|
name: '本',
|
||||||
|
initial_before_format: (properties, { enkaAvatar }) => {
|
||||||
|
const core = [0.4, 0.46, 0.52, 0.6, 0.66, 0.72, 0.8]
|
||||||
|
const { CoreSkillEnhancement } = enkaAvatar
|
||||||
|
const value = (core[CoreSkillEnhancement] || 0) * Math.trunc(properties[131].base + properties[131].add)
|
||||||
|
properties[121].add += Math.trunc(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1371: {
|
||||||
|
id: 1371,
|
||||||
|
name: '仪玄',
|
||||||
|
initial_after_format: (properties) => {
|
||||||
|
delete properties[231], delete properties[232]
|
||||||
|
delete properties[305]
|
||||||
|
const sheerForce =
|
||||||
|
Math.trunc(0.1 * properties[111].final) +
|
||||||
|
Math.trunc(0.3 * properties[121].final)
|
||||||
|
properties[19] = {
|
||||||
|
property_name: '贯穿力',
|
||||||
|
property_id: 19,
|
||||||
|
base: 0,
|
||||||
|
add: sheerForce,
|
||||||
|
final: sheerForce
|
||||||
|
}
|
||||||
|
properties[20] = {
|
||||||
|
property_name: '闪能自动累积',
|
||||||
|
property_id: 20,
|
||||||
|
base: 2,
|
||||||
|
add: 0,
|
||||||
|
final: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
model/Enka/interface.js
Normal file
1
model/Enka/interface.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export {};
|
||||||
597
model/Enka/interface.ts
Normal file
597
model/Enka/interface.ts
Normal file
|
|
@ -0,0 +1,597 @@
|
||||||
|
import type { Client, segment as segmentSource } from 'icqq'
|
||||||
|
import type redisM from 'redis'
|
||||||
|
import type chalk from 'chalk'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var Bot: typeof Client.prototype
|
||||||
|
var redis: redisM.RedisClientType
|
||||||
|
var segment: typeof segmentSource
|
||||||
|
var logger: {
|
||||||
|
chalk: typeof chalk
|
||||||
|
red: typeof chalk
|
||||||
|
yellow: typeof chalk
|
||||||
|
blue: typeof chalk
|
||||||
|
magenta: typeof chalk
|
||||||
|
cyan: typeof chalk
|
||||||
|
green: typeof chalk
|
||||||
|
trace: (...args: any[]) => void
|
||||||
|
debug: (...args: any[]) => void
|
||||||
|
info: (...args: any[]) => void
|
||||||
|
error: (...args: any[]) => void
|
||||||
|
warn: (...args: any[]) => void
|
||||||
|
fatal: (...args: any[]) => void
|
||||||
|
mark: (...args: any[]) => void
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Mys {
|
||||||
|
|
||||||
|
export interface Avatar {
|
||||||
|
id: number
|
||||||
|
level: number
|
||||||
|
name_mi18n: string
|
||||||
|
full_name_mi18n: string
|
||||||
|
element_type: number
|
||||||
|
camp_name_mi18n: string
|
||||||
|
avatar_profession: number
|
||||||
|
rarity: string
|
||||||
|
group_icon_path: string
|
||||||
|
hollow_icon_path: string
|
||||||
|
equip: Equip[]
|
||||||
|
weapon: Weapon | null
|
||||||
|
properties: AvatarProperty[]
|
||||||
|
skills: Skill[]
|
||||||
|
rank: number
|
||||||
|
ranks: Rank[]
|
||||||
|
role_vertical_painting_url: string
|
||||||
|
equip_plan_info: EquipPlanInfo | null
|
||||||
|
us_full_name: string
|
||||||
|
vertical_painting_color: string
|
||||||
|
sub_element_type: number
|
||||||
|
skin_list: Skin[]
|
||||||
|
role_square_url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Equip {
|
||||||
|
id: number
|
||||||
|
level: number
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
rarity: string
|
||||||
|
properties: Property[]
|
||||||
|
main_properties: Property[]
|
||||||
|
equip_suit: EquipSuit
|
||||||
|
equipment_type: number
|
||||||
|
/** 未命中词条数 */
|
||||||
|
invalid_property_cnt: number
|
||||||
|
/** 词条全命中 */
|
||||||
|
all_hit: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EquipSuit {
|
||||||
|
suit_id: number
|
||||||
|
name: string
|
||||||
|
own: number
|
||||||
|
desc1: string
|
||||||
|
desc2: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Property {
|
||||||
|
property_name: string
|
||||||
|
property_id: number
|
||||||
|
base: string
|
||||||
|
level: number
|
||||||
|
/** 是否是有效词条 */
|
||||||
|
valid: boolean
|
||||||
|
system_id: number
|
||||||
|
add: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EquipPlanInfo {
|
||||||
|
type: number
|
||||||
|
game_default: CustomInfo
|
||||||
|
cultivate_info: CultivateInfo
|
||||||
|
custom_info: CustomInfo
|
||||||
|
valid_property_cnt: number
|
||||||
|
plan_only_special_property: boolean
|
||||||
|
equip_rating: string
|
||||||
|
plan_effective_property_list: PlanProperty[]
|
||||||
|
equip_rating_score: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CultivateInfo {
|
||||||
|
name: string
|
||||||
|
plan_id: string
|
||||||
|
is_delete: boolean
|
||||||
|
old_plan: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomInfo {
|
||||||
|
property_list: PlanProperty[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlanProperty {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
full_name: string
|
||||||
|
system_id: number
|
||||||
|
is_select: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AvatarProperty {
|
||||||
|
property_name: string
|
||||||
|
property_id: number
|
||||||
|
base: string
|
||||||
|
add: string
|
||||||
|
final: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Rank {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
desc: string
|
||||||
|
pos: number
|
||||||
|
is_unlocked: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Skill {
|
||||||
|
level: number
|
||||||
|
skill_type: number
|
||||||
|
items: Item[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Item {
|
||||||
|
title: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Skin {
|
||||||
|
skin_id: number
|
||||||
|
skin_name: string
|
||||||
|
skin_vertical_painting_url: string
|
||||||
|
skin_square_url: string
|
||||||
|
skin_hollow_icon_path: string
|
||||||
|
skin_vertical_painting_color: string
|
||||||
|
unlocked: boolean
|
||||||
|
rarity: string
|
||||||
|
is_original: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Weapon {
|
||||||
|
id: number
|
||||||
|
level: number
|
||||||
|
name: string
|
||||||
|
star: number
|
||||||
|
icon: string
|
||||||
|
rarity: string
|
||||||
|
properties: Property[]
|
||||||
|
main_properties: Property[]
|
||||||
|
talent_title: string
|
||||||
|
talent_content: string
|
||||||
|
profession: number
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Enka {
|
||||||
|
|
||||||
|
export interface Avatar {
|
||||||
|
TalentToggles: boolean[]
|
||||||
|
SkillLevelList: Skill[]
|
||||||
|
EquippedList: Equip[]
|
||||||
|
ClaimedRewards: number[]
|
||||||
|
WeaponEffectState: number
|
||||||
|
IsFavorite: boolean
|
||||||
|
Id: number
|
||||||
|
Level: number
|
||||||
|
/** 突破数 */
|
||||||
|
PromotionLevel: number
|
||||||
|
Exp: number
|
||||||
|
SkinId: number
|
||||||
|
/** 影画数 */
|
||||||
|
TalentLevel: number
|
||||||
|
/** 核心技等级 */
|
||||||
|
CoreSkillEnhancement: number
|
||||||
|
WeaponUid: number
|
||||||
|
ObtainmentTimestamp: number
|
||||||
|
Weapon: Weapon | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Equip {
|
||||||
|
Slot: number
|
||||||
|
Equipment: Equipment
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Equipment {
|
||||||
|
RandomPropertyList: Property[]
|
||||||
|
MainPropertyList: Property[]
|
||||||
|
IsAvailable: boolean
|
||||||
|
IsLocked: boolean
|
||||||
|
IsTrash: boolean
|
||||||
|
Id: number
|
||||||
|
Uid: number
|
||||||
|
Level: number
|
||||||
|
BreakLevel: number
|
||||||
|
Exp: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Property {
|
||||||
|
PropertyId: number
|
||||||
|
PropertyValue: number
|
||||||
|
PropertyLevel: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Skill {
|
||||||
|
Level: number
|
||||||
|
Index: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Weapon {
|
||||||
|
IsAvailable: boolean
|
||||||
|
IsLocked: boolean
|
||||||
|
Id: number
|
||||||
|
Uid: number
|
||||||
|
Level: number
|
||||||
|
BreakLevel: number
|
||||||
|
Exp: number
|
||||||
|
UpgradeLevel: number
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Map {
|
||||||
|
|
||||||
|
export interface WeaponId2Data {
|
||||||
|
[id: string]: {
|
||||||
|
Id: number
|
||||||
|
CodeName: string
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
Desc2: string
|
||||||
|
Desc3: string
|
||||||
|
Rarity: string
|
||||||
|
Icon: string
|
||||||
|
WeaponType: { [key: string]: string }
|
||||||
|
BaseProperty: {
|
||||||
|
Name: string
|
||||||
|
Name2: string
|
||||||
|
Format: string
|
||||||
|
Value: number
|
||||||
|
Id: number
|
||||||
|
}
|
||||||
|
RandProperty: {
|
||||||
|
Name: string
|
||||||
|
Name2: string
|
||||||
|
Format: string
|
||||||
|
Value: number
|
||||||
|
Id: number
|
||||||
|
}
|
||||||
|
Level: {
|
||||||
|
[key: string]: {
|
||||||
|
Exp: number
|
||||||
|
Rate: number
|
||||||
|
Rate2: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stars: {
|
||||||
|
[key: string]: {
|
||||||
|
StarRate: number
|
||||||
|
RandRate: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Materials: string
|
||||||
|
Talents: {
|
||||||
|
[key: string]: {
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Profession: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PartnerId2Data {
|
||||||
|
[id: string]: {
|
||||||
|
sprite_id: string
|
||||||
|
name: string
|
||||||
|
full_name: string
|
||||||
|
en_name: string
|
||||||
|
WeaponType: string
|
||||||
|
ElementType: string
|
||||||
|
Camp: string
|
||||||
|
HitType: string
|
||||||
|
Rarity: string
|
||||||
|
Attack: number
|
||||||
|
AttackGrowth: number
|
||||||
|
BreakStun: number
|
||||||
|
Defence: number
|
||||||
|
DefenceGrowth: number
|
||||||
|
HpMax: number
|
||||||
|
HpGrowth: number
|
||||||
|
Crit: number
|
||||||
|
CritDamage: number
|
||||||
|
ElementAbnormalPower: number
|
||||||
|
ElementMystery: number
|
||||||
|
PenDelta: number
|
||||||
|
PenRate: number
|
||||||
|
SpRecover: number
|
||||||
|
/** 角色等级提升 */
|
||||||
|
Level: {
|
||||||
|
[level: string]: {
|
||||||
|
HpMax: number
|
||||||
|
Attack: number
|
||||||
|
Defence: number
|
||||||
|
LevelMax: number
|
||||||
|
LevelMin: number
|
||||||
|
Materials: { [key: string]: number }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 核心技等级提升 */
|
||||||
|
ExtraLevel: {
|
||||||
|
[level: string]: {
|
||||||
|
MaxLevel: number
|
||||||
|
Extra: {
|
||||||
|
[key: string]: {
|
||||||
|
Prop: number
|
||||||
|
Name: string
|
||||||
|
Format: string
|
||||||
|
Value: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EquipId2Data {
|
||||||
|
[id: string]: {
|
||||||
|
equip_id_list: number[]
|
||||||
|
sprite_file: string
|
||||||
|
equip_name: string
|
||||||
|
desc1: string
|
||||||
|
desc2: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SuitData {
|
||||||
|
[suit_id: string]: {
|
||||||
|
name: string
|
||||||
|
desc2: string
|
||||||
|
desc4: string
|
||||||
|
properties: Mys.Property[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Hakush {
|
||||||
|
export interface PartnerData {
|
||||||
|
Id: number
|
||||||
|
Icon: string
|
||||||
|
Name: string
|
||||||
|
CodeName: string
|
||||||
|
Rarity: number
|
||||||
|
WeaponType: {
|
||||||
|
[profession_id: string]: string
|
||||||
|
}
|
||||||
|
ElementType: {
|
||||||
|
[element_id: string]: string
|
||||||
|
}
|
||||||
|
SpecialElementType: {
|
||||||
|
Name: string
|
||||||
|
Title: string
|
||||||
|
Desc: string
|
||||||
|
Icon: string
|
||||||
|
}
|
||||||
|
HitType: {
|
||||||
|
[hit_id: string]: string
|
||||||
|
}
|
||||||
|
Camp: {
|
||||||
|
[id: string]: string
|
||||||
|
}
|
||||||
|
Gender: number
|
||||||
|
PartnerInfo: {
|
||||||
|
Birthday: string
|
||||||
|
FullName: string
|
||||||
|
Gender: string
|
||||||
|
IconPath: string
|
||||||
|
ImpressionF: string
|
||||||
|
ImpressionM: string
|
||||||
|
Name: string
|
||||||
|
OutlookDesc: string
|
||||||
|
ProfileDesc: string
|
||||||
|
Race: string
|
||||||
|
RoleIcon: string
|
||||||
|
Stature: string
|
||||||
|
UnlockCondition: string[]
|
||||||
|
TrustLv: { [key: string]: string }
|
||||||
|
}
|
||||||
|
Skin: {
|
||||||
|
[skin_id: string]: {
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
Image: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stats: {
|
||||||
|
Armor: number
|
||||||
|
ArmorGrowth: number
|
||||||
|
Attack: number
|
||||||
|
AttackGrowth: number
|
||||||
|
AvatarPieceId: number
|
||||||
|
BreakStun: number
|
||||||
|
Crit: number
|
||||||
|
CritDamage: number
|
||||||
|
CritDmgRes: number
|
||||||
|
CritRes: number
|
||||||
|
Defence: number
|
||||||
|
DefenceGrowth: number
|
||||||
|
ElementAbnormalPower: number
|
||||||
|
ElementMystery: number
|
||||||
|
Endurance: number
|
||||||
|
HpGrowth: number
|
||||||
|
HpMax: number
|
||||||
|
PenDelta: number
|
||||||
|
PenRate: number
|
||||||
|
Rbl: number
|
||||||
|
RblCorrectionFactor: number
|
||||||
|
RblProbability: number
|
||||||
|
Shield: number
|
||||||
|
ShieldGrowth: number
|
||||||
|
SpBarPoint: number
|
||||||
|
SpRecover: number
|
||||||
|
Stun: number
|
||||||
|
Tags: string[]
|
||||||
|
RpMax: number
|
||||||
|
RpRecover: number
|
||||||
|
}
|
||||||
|
Level: {
|
||||||
|
[key: string]: {
|
||||||
|
HpMax: number
|
||||||
|
Attack: number
|
||||||
|
Defence: number
|
||||||
|
LevelMax: number
|
||||||
|
LevelMin: number
|
||||||
|
Materials: { [key: string]: number }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExtraLevel: {
|
||||||
|
[key: string]: {
|
||||||
|
MaxLevel: number
|
||||||
|
Extra: { [key: string]: Part4 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LevelEXP: number[]
|
||||||
|
Skill: {
|
||||||
|
Basic: {
|
||||||
|
Description: AssistDescription[]
|
||||||
|
Material: { [key: string]: { [key: string]: number } }
|
||||||
|
}
|
||||||
|
Dodge: {
|
||||||
|
Description: AssistDescription[]
|
||||||
|
Material: { [key: string]: { [key: string]: number } }
|
||||||
|
}
|
||||||
|
Special: {
|
||||||
|
Description: {
|
||||||
|
Name: string
|
||||||
|
Desc?: string
|
||||||
|
Potential: any[]
|
||||||
|
Param?: {
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
Param?: {
|
||||||
|
[key: string]: {
|
||||||
|
Main: number
|
||||||
|
Growth: number
|
||||||
|
Format: string
|
||||||
|
DamagePercentage: number
|
||||||
|
DamagePercentageGrowth: number
|
||||||
|
StunRatio: number
|
||||||
|
StunRatioGrowth: number
|
||||||
|
SpRecovery: number
|
||||||
|
SpRecoveryGrowth: number
|
||||||
|
FeverRecovery: number
|
||||||
|
FeverRecoveryGrowth: number
|
||||||
|
AttributeInfliction: number
|
||||||
|
SpConsume: number
|
||||||
|
AttackData: any[]
|
||||||
|
RpRecovery: number
|
||||||
|
RpRecoveryGrowth: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Potential: any[]
|
||||||
|
}[]
|
||||||
|
}[]
|
||||||
|
Material: { [key: string]: { [key: string]: number } }
|
||||||
|
}
|
||||||
|
Chain: {
|
||||||
|
Description: AssistDescription[]
|
||||||
|
Material: { [key: string]: { [key: string]: number } }
|
||||||
|
}
|
||||||
|
Assist: {
|
||||||
|
Description: AssistDescription[]
|
||||||
|
Material: { [key: string]: { [key: string]: number } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SkillList: {
|
||||||
|
[key: string]: {
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
ElementType: number
|
||||||
|
HitType: number
|
||||||
|
Potential: any[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passive: {
|
||||||
|
Level: {
|
||||||
|
[key: string]: {
|
||||||
|
Level: number
|
||||||
|
Id: number
|
||||||
|
Name: string[]
|
||||||
|
Desc: string[]
|
||||||
|
ExtraProperty: {}
|
||||||
|
Potential: any[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Materials: { [key: string]: { [key: string]: number } }
|
||||||
|
}
|
||||||
|
Talent: {
|
||||||
|
[key: string]: {
|
||||||
|
Level: number
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
Desc2: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FairyRecommend: {
|
||||||
|
Slot4: number
|
||||||
|
Slot2: number
|
||||||
|
SlotSub: number
|
||||||
|
Part4: Part4
|
||||||
|
Part5: Part4
|
||||||
|
Part6: Part4
|
||||||
|
PartSub: Part4
|
||||||
|
}
|
||||||
|
Potential: any[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Part4 {
|
||||||
|
Prop: number
|
||||||
|
Name: string
|
||||||
|
Format: string
|
||||||
|
Value?: number
|
||||||
|
Icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AssistDescription {
|
||||||
|
Name: string
|
||||||
|
Desc?: string
|
||||||
|
Potential: any[]
|
||||||
|
Param?: {
|
||||||
|
Name: string
|
||||||
|
Desc: string
|
||||||
|
Param: {
|
||||||
|
[key: string]: {
|
||||||
|
Main: number
|
||||||
|
Growth: number
|
||||||
|
Format: string
|
||||||
|
DamagePercentage: number
|
||||||
|
DamagePercentageGrowth: number
|
||||||
|
StunRatio: number
|
||||||
|
StunRatioGrowth: number
|
||||||
|
SpRecovery: number
|
||||||
|
SpRecoveryGrowth: number
|
||||||
|
FeverRecovery: number
|
||||||
|
FeverRecoveryGrowth: number
|
||||||
|
AttributeInfliction: number
|
||||||
|
SpConsume: number
|
||||||
|
AttackData: any[]
|
||||||
|
RpRecovery: number
|
||||||
|
RpRecoveryGrowth: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Potential: any[]
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
示例图:
|
示例图:
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img width=800 src="https://s2.loli.net/2025/03/27/OcuIPDyE5sHSJZw.jpg" title="词条权重自定义基础步骤">
|
<img width=800 src="https://s2.loli.net/2025/06/01/aTyPoZ4gi89MqRC.jpg" title="词条权重自定义基础步骤">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### 进阶操作
|
### 进阶操作
|
||||||
|
|
@ -169,7 +169,7 @@ Buff来源可分为三大类:武器、套装、角色(影画、核心被动
|
||||||
|
|
||||||
- 主词条的提升会自动注册,无需处理
|
- 主词条的提升会自动注册,无需处理
|
||||||
|
|
||||||
- 二件套效果只有**属性伤害提升**需要注册,其他已包含于初始属性
|
- 二件套效果只有**属性伤害提升**和**局外面板以外的效果**(如[追加攻击]和[冲刺攻击]造成的伤害提升)需要注册,其他已包含于初始属性
|
||||||
|
|
||||||
- 四件套效果需要单独注册
|
- 四件套效果需要单独注册
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ async function init() {
|
||||||
// debug模式下监听文件变化
|
// debug模式下监听文件变化
|
||||||
const isWatch = await (async () => {
|
const isWatch = await (async () => {
|
||||||
try {
|
try {
|
||||||
|
// @ts-ignore
|
||||||
return (await import('../../../../lib/config/config.js')).default.bot.log_level === 'debug'
|
return (await import('../../../../lib/config/config.js')).default.bot.log_level === 'debug'
|
||||||
} catch {
|
} catch {
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default function (avatar) {
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"冰属性伤害加成": 1
|
"冰属性伤害加成": 1
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"以太属性伤害加成": 1
|
"以太属性伤害加成": 1
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0.5,
|
"异常精通": 0.5,
|
||||||
"异常掌控": 0.5,
|
"异常掌控": 0.5,
|
||||||
"冰属性伤害加成": 1
|
"冰属性伤害加成": 1
|
||||||
|
|
@ -122,7 +122,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.5,
|
"穿透率": 0.5,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -136,7 +136,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -150,7 +150,7 @@
|
||||||
"暴击伤害": 0.75,
|
"暴击伤害": 0.75,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -164,7 +164,7 @@
|
||||||
"暴击伤害": 0.75,
|
"暴击伤害": 0.75,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"冰属性伤害加成": 1
|
"冰属性伤害加成": 1
|
||||||
|
|
@ -178,7 +178,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"冰属性伤害加成": 1
|
"冰属性伤害加成": 1
|
||||||
|
|
@ -192,7 +192,7 @@
|
||||||
"暴击伤害": 0.75,
|
"暴击伤害": 0.75,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -206,7 +206,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -220,7 +220,7 @@
|
||||||
"暴击伤害": 0.75,
|
"暴击伤害": 0.75,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -234,7 +234,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -248,7 +248,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"冰属性伤害加成": 1
|
"冰属性伤害加成": 1
|
||||||
|
|
@ -262,7 +262,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -276,7 +276,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 0.75,
|
"异常掌控": 0.75,
|
||||||
"电属性伤害加成": 0.75
|
"电属性伤害加成": 0.75
|
||||||
|
|
@ -290,7 +290,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"电属性伤害加成": 0.75
|
"电属性伤害加成": 0.75
|
||||||
|
|
@ -304,7 +304,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"以太属性伤害加成": 1
|
"以太属性伤害加成": 1
|
||||||
|
|
@ -318,7 +318,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -332,7 +332,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -346,7 +346,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 0.75,
|
"异常精通": 0.75,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -360,7 +360,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -374,7 +374,7 @@
|
||||||
"暴击伤害": 0.75,
|
"暴击伤害": 0.75,
|
||||||
"穿透率": 0.5,
|
"穿透率": 0.5,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 0.25,
|
"异常精通": 0.25,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"以太属性伤害加成": 0.5
|
"以太属性伤害加成": 0.5
|
||||||
|
|
@ -388,7 +388,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"火属性伤害加成": 1
|
"火属性伤害加成": 1
|
||||||
|
|
@ -402,7 +402,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
@ -416,7 +416,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -430,7 +430,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0.5,
|
"能量自动回复": 0.5,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"电属性伤害加成": 1
|
"电属性伤害加成": 1
|
||||||
|
|
@ -444,7 +444,7 @@
|
||||||
"暴击伤害": 0,
|
"暴击伤害": 0,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 1,
|
"异常精通": 1,
|
||||||
"异常掌控": 1,
|
"异常掌控": 1,
|
||||||
"以太属性伤害加成": 1
|
"以太属性伤害加成": 1
|
||||||
|
|
@ -458,7 +458,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 1,
|
"穿透率": 1,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"冰属性伤害加成": 1
|
"冰属性伤害加成": 1
|
||||||
|
|
@ -472,7 +472,7 @@
|
||||||
"暴击伤害": 1,
|
"暴击伤害": 1,
|
||||||
"穿透率": 0,
|
"穿透率": 0,
|
||||||
"穿透值": 0,
|
"穿透值": 0,
|
||||||
"能量回复": 0,
|
"能量自动回复": 0,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"以太属性伤害加成": 1
|
"以太属性伤害加成": 1
|
||||||
|
|
@ -486,7 +486,7 @@
|
||||||
"暴击伤害": 0.75,
|
"暴击伤害": 0.75,
|
||||||
"穿透率": 0.75,
|
"穿透率": 0.75,
|
||||||
"穿透值": 0.25,
|
"穿透值": 0.25,
|
||||||
"能量回复": 1,
|
"能量自动回复": 1,
|
||||||
"异常精通": 0,
|
"异常精通": 0,
|
||||||
"异常掌控": 0,
|
"异常掌控": 0,
|
||||||
"物理属性伤害加成": 1
|
"物理属性伤害加成": 1
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"11102": ["HPRatio", "生命值百分比", "生命", "大生命"],
|
"11102": ["HPRatio", "生命值百分比", "生命", "大生命"],
|
||||||
"11103": ["HP", "生命值", "生命", "小生命"],
|
"11103": ["HP", "生命值", "生命", "小生命"],
|
||||||
|
"12101": ["ATKBase", "基础攻击力", "攻击", "基础攻"],
|
||||||
"12102": ["ATKRatio", "攻击力百分比", "攻击", "大攻击"],
|
"12102": ["ATKRatio", "攻击力百分比", "攻击", "大攻击"],
|
||||||
"12103": ["ATK", "攻击力", "攻击", "小攻击"],
|
"12103": ["ATK", "攻击力", "攻击", "小攻击"],
|
||||||
"12202": ["Impact", "冲击力", "冲击", "冲击力"],
|
"12202": ["Impact", "冲击力", "冲击", "冲击力"],
|
||||||
|
|
@ -10,7 +11,7 @@
|
||||||
"21103": ["CRITDMG", "暴击伤害", "暴伤", "暴伤"],
|
"21103": ["CRITDMG", "暴击伤害", "暴伤", "暴伤"],
|
||||||
"23103": ["PenRatio", "穿透率", "穿透", "穿透率"],
|
"23103": ["PenRatio", "穿透率", "穿透", "穿透率"],
|
||||||
"23203": ["Pen", "穿透值", "穿透", "穿透值"],
|
"23203": ["Pen", "穿透值", "穿透", "穿透值"],
|
||||||
"30502": ["EnergyRegen", "能量回复", "回能", "回能"],
|
"30502": ["EnergyRegen", "能量自动回复", "回能", "回能"],
|
||||||
"31203": ["AnomalyProficiency", "异常精通", "精通", "精通"],
|
"31203": ["AnomalyProficiency", "异常精通", "精通", "精通"],
|
||||||
"31402": ["AnomalyMastery", "异常掌控", "掌控", "掌控"],
|
"31402": ["AnomalyMastery", "异常掌控", "掌控", "掌控"],
|
||||||
"31503": ["PhysicalDMGBonus", "物理属性伤害加成", "物伤", "物伤"],
|
"31503": ["PhysicalDMGBonus", "物理属性伤害加成", "物伤", "物伤"],
|
||||||
|
|
|
||||||
207
resources/map/SuitData.json
Normal file
207
resources/map/SuitData.json
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
{
|
||||||
|
"31000": {
|
||||||
|
"name": "啄木鸟电音",
|
||||||
|
"desc2": "暴击率+8%。",
|
||||||
|
"desc4": "<color=#FFFFFF>[普通攻击]</color>、<color=#FFFFFF>[闪避反击]</color>或<color=#FFFFFF>[强化特殊技]</color>命中敌人并触发暴击时,分别为装备者提供1层增益效果,每层增益效果使装备者的攻击力提升9%,持续6秒,不同招式分别结算持续时间。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 20103,
|
||||||
|
"base": "8%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31100": {
|
||||||
|
"name": "河豚电音",
|
||||||
|
"desc2": "穿透率+8%。",
|
||||||
|
"desc4": "<color=#FFFFFF>[终结技]</color>造成的伤害提升20%;发动<color=#FFFFFF>[终结技]</color>时,装备者的攻击力提升15%,持续12秒。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 23103,
|
||||||
|
"base": "8%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31200": {
|
||||||
|
"name": "震星迪斯科",
|
||||||
|
"desc2": "冲击力+6%。",
|
||||||
|
"desc4": "<color=#FFFFFF>[普通攻击]</color>、<color=#FFFFFF>[冲刺攻击]</color>、<color=#FFFFFF>[闪避反击]</color>对主要攻击目标造成的失衡值提升20%。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 12202,
|
||||||
|
"base": "6%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31300": {
|
||||||
|
"name": "自由蓝调",
|
||||||
|
"desc2": "异常精通+30点。",
|
||||||
|
"desc4": "<color=#FFFFFF>[强化特殊技]</color>命中敌人时,根据装备者的属性类型,使目标对应属性异常积蓄抗性降低20%,持续8秒,相同属性类型的效果不可叠加。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31203,
|
||||||
|
"base": "30"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31400": {
|
||||||
|
"name": "激素朋克",
|
||||||
|
"desc2": "攻击力+10%。",
|
||||||
|
"desc4": "进入接战状态或换入前场时,装备者的攻击力提升25%,持续10秒,20秒内最多触发一次。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 12102,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31500": {
|
||||||
|
"name": "灵魂摇滚",
|
||||||
|
"desc2": "防御力+16%。",
|
||||||
|
"desc4": "受到敌方攻击并损失生命值时,装备者受到的伤害降低40%,持续2.5秒,15秒内最多触发一次。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 13102,
|
||||||
|
"base": "16%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31600": {
|
||||||
|
"name": "摇摆爵士",
|
||||||
|
"desc2": "能量自动回复+20%。",
|
||||||
|
"desc4": "发动<color=#FFFFFF>[连携技]</color>或<color=#FFFFFF>[终结技]</color>时,全队角色造成的伤害提升15%,持续12秒,同名被动效果之间不可叠加。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 30502,
|
||||||
|
"base": "20%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31800": {
|
||||||
|
"name": "混沌爵士",
|
||||||
|
"desc2": "异常精通+30点。",
|
||||||
|
"desc4": "<color=#FF5521>火属性伤害</color>和<color=#2EB6FF>电属性伤害</color>提升15%;位于后场时,<color=#FFFFFF>[强化特殊技]</color>和<color=#FFFFFF>[支援攻击]</color>造成的伤害提升20%,换入前场后,该增益效果仍然保留,持续5秒,保留效果7.5秒内最多触发一次。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31203,
|
||||||
|
"base": "30"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"31900": {
|
||||||
|
"name": "原始朋克",
|
||||||
|
"desc2": "施加的护盾值提升15%。",
|
||||||
|
"desc4": "队伍中任意角色发动<color=#FFFFFF>[招架支援]</color>或<color=#FFFFFF>[回避支援]</color>时,全队角色造成的伤害提升15%,持续10秒,同名被动效果之间不可叠加。",
|
||||||
|
"properties": []
|
||||||
|
},
|
||||||
|
"32200": {
|
||||||
|
"name": "炎狱重金属",
|
||||||
|
"desc2": "<color=#FF5521>火属性伤害</color>+10%。",
|
||||||
|
"desc4": "攻击命中处于<color=#FF5521>[灼烧]</color>状态下的敌人时,装备者的暴击率提升28%,持续8秒。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31603,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32300": {
|
||||||
|
"name": "混沌重金属",
|
||||||
|
"desc2": "<color=#FE437E>以太伤害</color>+10%。",
|
||||||
|
"desc4": "装备者的暴击伤害提升20%,队伍中任意角色触发<color=#FE437E>[侵蚀]</color>效果的额外伤害时,该增益效果额外提升5.5%,最多叠加6层,持续8秒,重复触发时刷新持续时间。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31903,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32400": {
|
||||||
|
"name": "雷暴重金属",
|
||||||
|
"desc2": "<color=#2EB6FF>电属性伤害</color>+10%。",
|
||||||
|
"desc4": "当场上存在处于<color=#2EB6FF>[感电]</color>状态下的敌人时,装备者的攻击力提升28%。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31803,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32500": {
|
||||||
|
"name": "极地重金属",
|
||||||
|
"desc2": "<color=#98EFF0>冰属性伤害</color>+10%。",
|
||||||
|
"desc4": "<color=#FFFFFF>[普通攻击]</color>和<color=#FFFFFF>[冲刺攻击]</color>造成的伤害提升20%,队伍中任意角色对敌人施加<color=#98EFF0>[冻结]</color>或触发<color=#98EFF0>[碎冰]</color>效果时,该增益效果额外提升20%,持续12秒。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31703,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32600": {
|
||||||
|
"name": "獠牙重金属",
|
||||||
|
"desc2": "<color=#F0D12B>物理伤害</color>+10%。",
|
||||||
|
"desc4": "队伍中任意角色对敌人施加<color=#F0D12B>[强击]</color>效果时,装备者对目标造成的伤害提升35%,持续12秒。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31503,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32700": {
|
||||||
|
"name": "折枝剑歌",
|
||||||
|
"desc2": "暴击伤害+16%。",
|
||||||
|
"desc4": "异常掌控大于等于115点时,装备者的暴击伤害提升30%;队伍中任意角色对敌人施加<color=#98EFF0>[冻结]</color>或触发<color=#98EFF0>[碎冰]</color>效果时,装备者的暴击率提升12%,持续15秒。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 21103,
|
||||||
|
"base": "16%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32800": {
|
||||||
|
"name": "静听嘉音",
|
||||||
|
"desc2": "攻击力+10%。",
|
||||||
|
"desc4": "队伍中任意角色通过<color=#FFFFFF>[快速支援]</color>入场时,全队角色获得1层<color=#FFFFFF>[嘉音]</color>,最多叠加3层,持续15秒,重复触发时刷新持续时间,每拥有1层<color=#FFFFFF>[嘉音]</color>,通过<color=#FFFFFF>[快速支援]</color>入场的角色造成的伤害提升8%,同名被动效果之间不可叠加。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 12102,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"32900": {
|
||||||
|
"name": "如影相随",
|
||||||
|
"desc2": "<color=#FFFFFF>[追加攻击]</color>和<color=#FFFFFF>[冲刺攻击]</color>造成的伤害提升15%。",
|
||||||
|
"desc4": "<color=#FFFFFF>[追加攻击]</color>或<color=#FFFFFF>[冲刺攻击]</color>命中敌人时,若造成的伤害与装备者的属性一致,则获得1层增益效果,同一招式内最多触发一次;每拥有1层增益效果,装备者的攻击力提升4%,暴击率提升4%,最多叠加3层,持续15秒,重复触发时刷新持续时间。",
|
||||||
|
"properties": []
|
||||||
|
},
|
||||||
|
"33000": {
|
||||||
|
"name": "法厄同之歌",
|
||||||
|
"desc2": "异常掌控+8%。",
|
||||||
|
"desc4": "队伍中任意角色发动<color=#FFFFFF>[强化特殊技]</color>时,装备者的异常精通提升45点,持续8秒;如果发动<color=#FFFFFF>[强化特殊技]</color>的角色不是装备者本人时,装备者造成的<color=#FE437E>以太伤害</color>提升25%。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 31402,
|
||||||
|
"base": "8%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"33100": {
|
||||||
|
"name": "云岿如我",
|
||||||
|
"desc2": "生命值+10%",
|
||||||
|
"desc4": "发动<color=#FFFFFF>[强化特殊技]</color>、<color=#FFFFFF>[连携技]</color>、<color=#FFFFFF>[终结技]</color>时,暴击率提升4%,最多叠加3层,持续15秒,重复触发时刷新持续时间,拥有3层效果时,造成的贯穿伤害提升10%。",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"property_id": 11102,
|
||||||
|
"base": "10%"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"33200": {
|
||||||
|
"name": "山大王",
|
||||||
|
"desc2": "攻击造成的失衡值提升6%",
|
||||||
|
"desc4": "装备者为[击破]角色时,发动<color=#FFFFFF>[强化特殊技]</color>或<color=#FFFFFF>[连携技]</color>会使全队角色暴击伤害提升15%,装备者的暴击率大于等于50%时暴击伤害额外提升15%,持续15秒,重复触发时刷新持续时间,同名被动效果之间不可叠加。",
|
||||||
|
"properties": []
|
||||||
|
}
|
||||||
|
}
|
||||||
26882
resources/map/WeaponId2Data.json
Normal file
26882
resources/map/WeaponId2Data.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,68 +0,0 @@
|
||||||
{
|
|
||||||
"12001": "Weapon_B_Common_01",
|
|
||||||
"12002": "Weapon_B_Common_02",
|
|
||||||
"12003": "Weapon_B_Common_03",
|
|
||||||
"12004": "Weapon_B_Common_04",
|
|
||||||
"12005": "Weapon_B_Common_05",
|
|
||||||
"12006": "Weapon_B_Common_06",
|
|
||||||
"12007": "Weapon_B_Common_07",
|
|
||||||
"12008": "Weapon_B_Common_08",
|
|
||||||
"12009": "Weapon_B_Common_09",
|
|
||||||
"12010": "Weapon_B_Common_10",
|
|
||||||
"12011": "Weapon_B_Common_11",
|
|
||||||
"12012": "Weapon_B_Common_12",
|
|
||||||
"12013": "Weapon_B_Common_13",
|
|
||||||
"12014": "Weapon_B_Common_14",
|
|
||||||
"13001": "Weapon_A_Common_01",
|
|
||||||
"13002": "Weapon_A_Common_02",
|
|
||||||
"13003": "Weapon_A_Common_03",
|
|
||||||
"13004": "Weapon_A_Common_04",
|
|
||||||
"13005": "Weapon_A_Common_05",
|
|
||||||
"13006": "Weapon_A_Common_06",
|
|
||||||
"13007": "Weapon_A_Common_07",
|
|
||||||
"13008": "Weapon_A_Common_08",
|
|
||||||
"13009": "Weapon_A_Common_09",
|
|
||||||
"13010": "Weapon_A_Common_10",
|
|
||||||
"13011": "Weapon_A_Common_11",
|
|
||||||
"13013": "Weapon_A_Common_13",
|
|
||||||
"13015": "Weapon_A_Common_15",
|
|
||||||
"13101": "Weapon_A_1011",
|
|
||||||
"13103": "Weapon_A_1031",
|
|
||||||
"13106": "Weapon_A_1061",
|
|
||||||
"13108": "Weapon_A_1081",
|
|
||||||
"13111": "Weapon_A_1111",
|
|
||||||
"13112": "Weapon_A_1121",
|
|
||||||
"13113": "Weapon_A_1131",
|
|
||||||
"13115": "Weapon_A_1151",
|
|
||||||
"13127": "Weapon_A_1271",
|
|
||||||
"13128": "Weapon_A_1281",
|
|
||||||
"13135": "Weapon_A_1351",
|
|
||||||
"13142": "Weapon_A_1421",
|
|
||||||
"14001": "Weapon_S_Common_01",
|
|
||||||
"14002": "Weapon_S_Common_02",
|
|
||||||
"14003": "Weapon_S_Common_03",
|
|
||||||
"14102": "Weapon_S_1021",
|
|
||||||
"14104": "Weapon_S_1041",
|
|
||||||
"14107": "Weapon_S_1071",
|
|
||||||
"14109": "Weapon_S_1091",
|
|
||||||
"14110": "Weapon_S_1101",
|
|
||||||
"14114": "Weapon_S_1141",
|
|
||||||
"14116": "Weapon_S_1161",
|
|
||||||
"14117": "Weapon_S_1171",
|
|
||||||
"14118": "Weapon_S_1181",
|
|
||||||
"14119": "Weapon_S_1191",
|
|
||||||
"14120": "Weapon_S_1201",
|
|
||||||
"14121": "Weapon_S_1211",
|
|
||||||
"14122": "Weapon_S_1221",
|
|
||||||
"14124": "Weapon_S_1241",
|
|
||||||
"14125": "Weapon_S_1251",
|
|
||||||
"14126": "Weapon_S_1261",
|
|
||||||
"14129": "Weapon_S_1291",
|
|
||||||
"14131": "Weapon_S_1311",
|
|
||||||
"14132": "Weapon_S_1321",
|
|
||||||
"14133": "Weapon_S_1331",
|
|
||||||
"14136": "Weapon_S_1361",
|
|
||||||
"14137": "Weapon_S_1371",
|
|
||||||
"14138": "Weapon_S_1381",
|
|
||||||
"14139": "Weapon_S_1391"
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue