From 21b89154184c7ad72cec57a295ecb87ccefa6dc8 Mon Sep 17 00:00:00 2001
From: UCPr <2032385471@qq.com>
Date: Mon, 9 Jun 2025 15:40:27 +0800
Subject: [PATCH] =?UTF-8?q?feature=EF=BC=9A=E6=94=AF=E6=8C=81Enka=E9=9D=A2?=
=?UTF-8?q?=E6=9D=BF=E6=9B=B4=E6=96=B0=EF=BC=9A`%=E6=9B=B4=E6=96=B0?=
=?UTF-8?q?=E5=B1=95=E6=9F=9C=E9=9D=A2=E6=9D=BF`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 9 +-
apps/help.js | 8 +-
apps/panel.js | 56 +-
lib/avatar.js | 13 +-
lib/convert/char.js | 75 +-
lib/convert/equip.js | 17 +-
lib/convert/property.js | 10 +-
lib/convert/weapon.js | 23 +-
lib/db.js | 2 +-
lib/mysapi.js | 2 +-
lib/plugin.js | 30 +-
model/Enka/enkaApi.js | 64 +
model/Enka/formater.js | 524 +
model/Enka/formater.ts | 608 +
model/Enka/interface.js | 1 +
model/Enka/interface.ts | 597 +
model/damage/README.md | 4 +-
model/damage/avatar.ts | 1 +
model/damage/character/模板/score.js | 2 +-
resources/map/EquipScore.json | 70 +-
resources/map/PartnerId2Data.json | 7154 ++++++-
resources/map/Property2Name.json | 3 +-
resources/map/SuitData.json | 207 +
resources/map/WeaponId2Data.json | 26882 +++++++++++++++++++++++++
resources/map/WeaponId2Sprite.json | 68 -
25 files changed, 36048 insertions(+), 382 deletions(-)
create mode 100644 model/Enka/enkaApi.js
create mode 100644 model/Enka/formater.js
create mode 100644 model/Enka/formater.ts
create mode 100644 model/Enka/interface.js
create mode 100644 model/Enka/interface.ts
create mode 100644 resources/map/SuitData.json
create mode 100644 resources/map/WeaponId2Data.json
delete mode 100644 resources/map/WeaponId2Sprite.json
diff --git a/README.md b/README.md
index 6b8e73f..ff884d2 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,12 @@ git clone --depth=1 https://gitee.com/bietiaop/ZZZ-Plugin.git ./plugins/ZZZ-Plug
以下所有功能前缀为:`#zzz`、`%`、`#ZZZ`、`#绝区零` 任选其一
-
+
+点击展开帮助图
+
+
+
+
## 攻略、图鉴
@@ -81,6 +86,8 @@ git clone --depth=1 https://gitee.com/bietiaop/ZZZ-Plugin.git ./plugins/ZZZ-Plug
绑定设备**无法100%解决**账号异常问题。
+若更新面板遇见账号异常问题,可尝试 **%更新展柜面板**,这将调用[Enka](https://enka.network/?zzz)接口更新游戏内展示的角色的数据。如若通过此方法更新的角色数据与实际不一致,请[提出issue](https://github.com/ZZZure/ZZZ-Plugin/issues/new)
+
## 角色图缺失
由于历史代码缘故,以前在游戏资源未更新就进行资源下载的可能导致角色图片缺失,你可以到插件资源目录手动删除对应文件,或者执行命令 `%删除全部资源` 进行删除。删除全部资源指令目前**不会**删除自定义面板图,仅会删除下载的图片资源,再次使用时需重新下载图片(自动下载)。
diff --git a/apps/help.js b/apps/help.js
index 4b138b1..69ba16d 100644
--- a/apps/help.js
+++ b/apps/help.js
@@ -101,10 +101,10 @@ const helpData = [
items: [
{
title: '刷新角色面板',
- desc: '刷新角色面板',
+ desc: '%更新展柜面板 通过Enka接口获取游戏内展柜角色,无需CK',
needCK: true,
needSK: false,
- commands: ['刷新面板', '更新面板', '面板刷新', '面板更新'],
+ commands: ['刷新面板', '更新面板', '刷新/更新展柜面板'],
},
{
title: '查看角色面板列表',
@@ -115,10 +115,10 @@ const helpData = [
},
{
title: '查看角色面板',
- desc: '查看角色详细面板信息',
+ desc: '查看角色详细面板/技能伤害信息',
needCK: false,
needSK: false,
- commands: ['角色名+面板'],
+ commands: ['角色名+面板', '角色名+伤害'],
},
{
title: '查看角色面板图',
diff --git a/apps/panel.js b/apps/panel.js
index bcd5596..e106c6f 100644
--- a/apps/panel.js
+++ b/apps/panel.js
@@ -1,5 +1,8 @@
import { ZZZPlugin } from '../lib/plugin.js';
+import { parsePlayerInfo, refreshPanelFromEnka } from '../model/Enka/enkaApi.js';
+import { getCk } from '../lib/common.js';
import {
+ mergePanel,
getPanelList,
refreshPanel as refreshPanelFunction,
getPanelOrigin,
@@ -20,7 +23,7 @@ export class Panel extends ZZZPlugin {
priority: _.get(settings.getConfig('priority'), 'panel', 70),
rule: [
{
- reg: `${rulePrefix}(.*)面板(刷新|更新|列表)?$`,
+ reg: `${rulePrefix}(.*)面板(展柜)?(刷新|更新|列表)?$`,
fnc: 'handleRule',
},
{
@@ -40,7 +43,7 @@ export class Panel extends ZZZPlugin {
}
async handleRule() {
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 suf = this.e.msg.match(reg)[5]?.trim();
if (['刷新', '更新'].includes(pre) || ['刷新', '更新'].includes(suf))
@@ -57,19 +60,36 @@ export class Panel extends ZZZPlugin {
const panelSettings = settings.getConfig('panel');
const coldTime = _.get(panelSettings, 'interval', 300);
if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) {
- await this.reply(`${coldTime}秒内只能刷新一次,请稍后再试`);
+ await this.reply(`${coldTime}秒内只能更新一次,请稍后再试`);
return false;
}
- const { api, deviceFp } = await this.getAPI();
- await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
- await this.reply('正在刷新面板列表,请稍候...');
- await this.getPlayerInfo();
- const result = await refreshPanelFunction(api, deviceFp).catch(e => {
- this.reply(e.message);
- throw e;
- });
+ const isEnka = this.e.msg.includes('展柜') || !(await getCk(this.e))
+ let result
+ if (isEnka) {
+ const data = await refreshPanelFromEnka(uid)
+ await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
+ if (typeof data === 'object') {
+ 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) {
- await this.reply('面板列表刷新失败,请稍后再试');
+ await this.reply('面板列表更新失败,请稍后再试\n账号异常时,可尝试%更新展柜面板(所更新角色数据与实际不一致时,请提issue)');
return false;
}
const newChar = result.filter(item => item.isNew);
@@ -79,14 +99,16 @@ export class Panel extends ZZZPlugin {
};
await this.render('panel/refresh.html', finalData);
}
+
async getCharPanelList() {
const uid = await this.getUID();
const result = getPanelList(uid);
- if (!result) {
- await this.reply('未找到面板数据,请先%刷新面板');
+ if (!result.length) {
+ await this.reply(`UID:${uid}无本地面板数据,请先%更新面板 或 %更新展柜面板`);
return false;
}
- await this.getPlayerInfo();
+ const hasCk = !!(await getCk(this.e));
+ await this.getPlayerInfo(hasCk ? undefined : parsePlayerInfo({ uid }));
const timer = setTimeout(() => {
if (this?.reply) {
this.reply('查询成功,正在下载图片资源,请稍候。');
@@ -123,7 +145,7 @@ export class Panel extends ZZZPlugin {
const name = match[4];
const data = getPanelOrigin(uid, name);
if (!data) {
- await this.reply(`未找到角色${name}的面板信息,请先刷新面板`);
+ await this.reply(`未找到角色${name}的面板信息,请确保角色名称/别称存在且已更新面板`);
return;
}
let handler = this.e.runtime.handler || {};
@@ -201,7 +223,7 @@ export class Panel extends ZZZPlugin {
const uid = await this.getUID();
const result = getPanelList(uid);
if (!result) {
- await this.reply('未找到面板数据,请先%刷新面板');
+ await this.reply('未找到面板数据,请先%更新面板 或 %更新展柜面板');
return false;
}
await this.getPlayerInfo();
diff --git a/lib/avatar.js b/lib/avatar.js
index 9d1f56c..715a221 100644
--- a/lib/avatar.js
+++ b/lib/avatar.js
@@ -102,17 +102,24 @@ export const refreshPanel = async (api, deviceFp) => {
// 获取新数据
const newData = await getAvatarInfoList(api, deviceFp, true);
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));
for (const item of formattedData) {
// 下载图片资源
await item.get_basic_assets();
}
return formattedData;
-};
+}
/**
*获取面板数据
diff --git a/lib/convert/char.js b/lib/convert/char.js
index b2f2719..3fede56 100644
--- a/lib/convert/char.js
+++ b/lib/convert/char.js
@@ -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 { 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}
*/
export const IDToCharName = (id, full = true, en = false) => {
- const data = PartnerId2SpriteId?.[id];
- if (!data) return null;
- if (en) return data?.['en_name'];
- if (full) return data?.['full_name'];
- return data?.['name'];
-};
+ const data = PartnerId2Data?.[id]
+ if (!data) return null
+ if (en) return data?.['en_name']
+ if (full) return data?.['full_name']
+ return data?.['name']
+}
/**
*
@@ -25,73 +26,73 @@ export const IDToCharName = (id, full = true, en = false) => {
* @returns {string | null}
*/
export const IDToCharSprite = id => {
- const data = PartnerId2SpriteId?.[id];
- if (!data) return null;
- return data?.['sprite_id'];
-};
+ const data = PartnerId2Data?.[id]
+ if (!data) return null
+ return data?.['sprite_id']
+}
/**
* @param {string} name
* @returns {number | null}
*/
export const charNameToID = name => {
- for (const [id, data] of Object.entries(PartnerId2SpriteId)) {
- if (data['name'] === name) return Number(id);
+ for (const [id, data] of Object.entries(PartnerId2Data)) {
+ if (data['name'] === name) return Number(id)
}
- return null;
-};
+ return null
+}
/**
* @param {string} name
* @returns {string | null}
*/
export const charNameToSprite = name => {
- for (const [_id, data] of Object.entries(PartnerId2SpriteId)) {
- if (data['name'] === name) return data['sprite'];
+ for (const [_id, data] of Object.entries(PartnerId2Data)) {
+ if (data['name'] === name) return data['sprite_id']
}
- return null;
-};
+ return null
+}
/**
* @param {string} _alias
* @returns {string | null}
*/
export const aliasToName = _alias => {
- const alias = settings.getConfig('alias');
+ const alias = settings.getConfig('alias')
for (const [id, data] of Object.entries(alias)) {
- if (id === _alias) return id;
- if (data.includes(_alias)) return id;
+ if (id === _alias) return id
+ if (data.includes(_alias)) return id
}
// 判断PartnerId2SpriteId是否有对应的name
- for (const [_, data] of Object.entries(PartnerId2SpriteId)) {
- if (data['name'] === _alias) return data['name'];
+ for (const [_, data] of Object.entries(PartnerId2Data)) {
+ if (data['name'] === _alias) return data['name']
}
- return null;
-};
+ return null
+}
/**
* @param {string} _alias
* @returns {string | null}
*/
export const aliasToSprite = _alias => {
- const name = aliasToName(_alias);
- return charNameToSprite(name);
-};
+ const name = aliasToName(_alias)
+ return charNameToSprite(name)
+}
/**
* @param {string} name
* @returns {number | null}
*/
export const aliasToID = name => {
- const _name = aliasToName(name);
- const id = charNameToID(_name);
- return id;
-};
+ const _name = aliasToName(name)
+ const id = charNameToID(_name)
+ return id
+}
/**
* 获取所有角色ID
* @returns {string[]}
*/
export const getAllCharactersID = () => {
- return Object.keys(PartnerId2SpriteId);
-};
+ return Object.keys(PartnerId2Data)
+}
diff --git a/lib/convert/equip.js b/lib/convert/equip.js
index 4a7cab1..18e9013 100644
--- a/lib/convert/equip.js
+++ b/lib/convert/equip.js
@@ -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}
*/
export function equipIdToSprite(equipId) {
- equipId = equipId.toString();
+ equipId = equipId.toString()
if (equipId.length === 5) {
- const suitId = equipId.slice(0, 3) + '00';
- if (equipData.hasOwnProperty(suitId)) {
- return equipData[suitId]['sprite_file'].replace('3D', '');
+ const suitId = equipId.slice(0, 3) + '00'
+ if (EquipId2Data.hasOwnProperty(suitId)) {
+ return EquipId2Data[suitId]['sprite_file'].replace('3D', '')
}
}
- return null;
+ return null
}
/**
@@ -23,5 +24,5 @@ export function equipIdToSprite(equipId) {
* @returns {string[]}
*/
export function getAllEquipID() {
- return Object.keys(equipData);
+ return Object.keys(EquipId2Data)
}
diff --git a/lib/convert/property.js b/lib/convert/property.js
index ffe32df..36f57b1 100644
--- a/lib/convert/property.js
+++ b/lib/convert/property.js
@@ -93,7 +93,7 @@ export const idToShortName3 = id => {
*/
export const nameToShortName3 = propName => {
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;
};
@@ -101,13 +101,15 @@ export const nameToShortName3 = propName => {
/**
* 属性名转id
* @param {string} propName 属性名
- * @returns {string}
+ * @returns {number}
*/
export const nameToId = (propName) => {
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;
};
/**
diff --git a/lib/convert/weapon.js b/lib/convert/weapon.js
index 798059a..9b1a18e 100644
--- a/lib/convert/weapon.js
+++ b/lib/convert/weapon.js
@@ -1,32 +1,33 @@
// 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
* @returns string
*/
export const IDToWeaponFileName = id => {
- const data = WeaponId2Sprite?.[id];
- return data;
-};
+ const data = WeaponId2Data?.[id]?.CodeName
+ return data
+}
/**
* @param {string} name
* @returns string
*/
export const weaponFileNameToID = name => {
- for (const [id, data] of Object.entries(WeaponId2Sprite)) {
- if (data === name) return id;
+ for (const [id, data] of Object.entries(WeaponId2Data)) {
+ if (data.CodeName === name) return id
}
- return null;
-};
+ return null
+}
/**
* 获取所有武器的ID
* @returns string[]
*/
export const getAllWeaponID = () => {
- return Object.keys(WeaponId2Sprite);
-};
+ return Object.keys(WeaponId2Data)
+}
diff --git a/lib/db.js b/lib/db.js
index 4c8a97d..2ad8e00 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -18,7 +18,7 @@ export function saveGachaLog(uid, data) {
/**
* @param {string} uid
- * @returns {Array