mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-25 04:59:52 +08:00
修复凌晨CD计算bug,修复超量名单潜在记录污染bug (#2935)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# 背包材料统计 v2.61
|
||||
# 背包材料统计 v2.62
|
||||
作者:吉吉喵
|
||||
|
||||
<!-- 新增:全局图片样式,控制连续图片同行显示 -->
|
||||
@@ -233,4 +233,5 @@ A:记录文件夹位于 `BetterGI\User\JsScript\背包材料统计\` 下,各
|
||||
| v2.58 | 优化背包扫图逻辑 |
|
||||
| v2.59 | 修复自动拾取匹配bug,改为双向匹配 |
|
||||
| v2.60 | 手动终止路径会被记录成noRecord模式,只参与CD计算;增加当前路线预估时长日志;材料分类升级多选框UI,刚需bgi v0.55版本;优化文件是否存在逻辑;降级ReadTextSync报错;检测码识别路径 |
|
||||
| v2.61 | 背包材料识别机制加速;修复手动终止路径noRecord模式的额外条件判断不生效;全局图片缓存;识别名单、CD文件和弹窗升级多选框UI!!!!!!注意UI修改了,配置组里需要删除并重新添加该js!!!!!! |
|
||||
| v2.61 | 背包材料识别机制加速;修复手动终止路径noRecord模式的额外条件判断不生效;全局图片缓存;识别名单、CD文件和弹窗升级多选框UI!!!!!!注意UI修改了,配置组里需要删除并重新添加该js!!!!!! |
|
||||
| v2.62 | 修复凌晨CD计算bug,修复超量名单潜在记录污染bug | |
|
||||
@@ -10,7 +10,7 @@ const CONSTANTS = {
|
||||
NO_RECORD_DIR: "pathing_record/noRecord",
|
||||
IMAGES_DIR: "assets/images",
|
||||
MONSTER_MATERIALS_PATH: "assets/Monster-Materials.txt",
|
||||
|
||||
|
||||
// 解析与处理配置
|
||||
MAX_PATH_DEPTH: 3, // 路径解析最大深度
|
||||
NOTIFICATION_CHUNK_SIZE: 500, // 通知拆分长度
|
||||
@@ -18,7 +18,7 @@ const CONSTANTS = {
|
||||
FOOD_ZERO_EXP_SUFFIX: "_狗粮-0.txt", // 新增:狗粮0 EXP记录后缀
|
||||
SUMMARY_FILE_NAME: "材料收集汇总.txt",
|
||||
ZERO_COUNT_SUFFIX: "-0.txt",
|
||||
|
||||
|
||||
// 日志模块标识
|
||||
LOG_MODULES: {
|
||||
INIT: "[初始化]",
|
||||
@@ -91,22 +91,22 @@ function generatePathContentCode(pathingFilePath) {
|
||||
if (extractedCode) {
|
||||
return extractedCode;
|
||||
}
|
||||
|
||||
|
||||
// 如果文件名中没有检测码,生成新的
|
||||
const content = safeReadTextSync(pathingFilePath);
|
||||
if (!content) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.PATH}路径文件为空: ${pathingFilePath}`);
|
||||
return "00000000";
|
||||
}
|
||||
|
||||
|
||||
const pathData = JSON.parse(content);
|
||||
const positions = pathData.positions || pathData.actions || [];
|
||||
|
||||
|
||||
if (!Array.isArray(positions) || positions.length === 0) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.PATH}路径文件无有效位置数据: ${pathingFilePath}`);
|
||||
return "00000000";
|
||||
}
|
||||
|
||||
|
||||
return generateContentCode(positions);
|
||||
} catch (error) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.PATH}生成路径检测码失败: ${error.message}`);
|
||||
@@ -125,14 +125,14 @@ var state = { completed: false, cancelRequested: false, ocrPaused: false };
|
||||
const globalImageCache = new Map();
|
||||
|
||||
function getCachedImageMat(filePath) {
|
||||
if (globalImageCache.has(filePath)) {
|
||||
return globalImageCache.get(filePath);
|
||||
}
|
||||
const mat = file.readImageMatSync(filePath);
|
||||
if (!mat.empty()) {
|
||||
globalImageCache.set(filePath, mat);
|
||||
}
|
||||
return mat;
|
||||
if (globalImageCache.has(filePath)) {
|
||||
return globalImageCache.get(filePath);
|
||||
}
|
||||
const mat = file.readImageMatSync(filePath);
|
||||
if (!mat.empty()) {
|
||||
globalImageCache.set(filePath, mat);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
@@ -149,34 +149,34 @@ const noRecord = settings.noRecord || false;
|
||||
const debugLog = settings.debugLog || false;
|
||||
const targetCount = Math.min(9999, Math.max(0, Math.floor(Number(settings.TargetCount) || 5000))); // 设定的目标数量
|
||||
const exceedCount = Math.min(9999, Math.max(0, Math.floor(Number(settings.ExceedCount) || 9000))); // 设定的超量目标数量
|
||||
const endTimeStr = settings.CurrentTime ? settings.CurrentTime : null;
|
||||
const endTimeStr = settings.CurrentTime ? settings.CurrentTime : null;
|
||||
|
||||
// 解析需要处理的CD分类
|
||||
let allowedCDCategories = [];
|
||||
try {
|
||||
allowedCDCategories = Array.from(settings.CDCategories || []);
|
||||
allowedCDCategories = Array.from(settings.CDCategories || []);
|
||||
} catch (e) {
|
||||
log.error(`${CONSTANTS.LOG_MODULES.INIT}获取CDCategories设置失败: ${e.message}`);
|
||||
log.error(`${CONSTANTS.LOG_MODULES.INIT}获取CDCategories设置失败: ${e.message}`);
|
||||
}
|
||||
|
||||
let availableCDCategories = [];
|
||||
try {
|
||||
const cdFilePaths = readAllFilePaths(CONSTANTS.MATERIAL_CD_DIR, 0, 1, ['.txt']);
|
||||
availableCDCategories = cdFilePaths.map(filePath => basename(filePath).replace('.txt', ''));
|
||||
log.info(`${CONSTANTS.LOG_MODULES.INIT}可用CD分类:${availableCDCategories.join(', ')}`);
|
||||
const cdFilePaths = readAllFilePaths(CONSTANTS.MATERIAL_CD_DIR, 0, 1, ['.txt']);
|
||||
availableCDCategories = cdFilePaths.map(filePath => basename(filePath).replace('.txt', ''));
|
||||
log.info(`${CONSTANTS.LOG_MODULES.INIT}可用CD分类:${availableCDCategories.join(', ')}`);
|
||||
} catch (e) {
|
||||
log.error(`${CONSTANTS.LOG_MODULES.INIT}扫描CD目录失败: ${e.message}`);
|
||||
log.error(`${CONSTANTS.LOG_MODULES.INIT}扫描CD目录失败: ${e.message}`);
|
||||
}
|
||||
|
||||
if (allowedCDCategories.length > 0) {
|
||||
const invalidCategories = allowedCDCategories.filter(cat => !availableCDCategories.includes(cat));
|
||||
if (invalidCategories.length > 0) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.INIT}以下CD分类不存在,将被忽略:${invalidCategories.join('、')}`);
|
||||
allowedCDCategories = allowedCDCategories.filter(cat => availableCDCategories.includes(cat));
|
||||
}
|
||||
log.info(`${CONSTANTS.LOG_MODULES.INIT}已配置只处理以下CD分类:${allowedCDCategories.join('、')}`);
|
||||
const invalidCategories = allowedCDCategories.filter(cat => !availableCDCategories.includes(cat));
|
||||
if (invalidCategories.length > 0) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.INIT}以下CD分类不存在,将被忽略:${invalidCategories.join('、')}`);
|
||||
allowedCDCategories = allowedCDCategories.filter(cat => availableCDCategories.includes(cat));
|
||||
}
|
||||
log.info(`${CONSTANTS.LOG_MODULES.INIT}已配置只处理以下CD分类:${allowedCDCategories.join('、')}`);
|
||||
} else {
|
||||
log.info(`${CONSTANTS.LOG_MODULES.INIT}未配置CD分类过滤,将处理所有分类`);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.INIT}未配置CD分类过滤,将处理所有分类`);
|
||||
}
|
||||
|
||||
// ==============================================
|
||||
@@ -212,14 +212,14 @@ function parseMonsterMaterials() {
|
||||
try {
|
||||
const content = safeReadTextSync(CONSTANTS.MONSTER_MATERIALS_PATH);
|
||||
const lines = content.split('\n').map(line => line.trim()).filter(line => line);
|
||||
|
||||
|
||||
lines.forEach(line => {
|
||||
if (!line.includes(':')) return;
|
||||
const [monsterName, materialsStr] = line.split(':');
|
||||
const materials = materialsStr.split(/[,,、 \s]+/)
|
||||
.map(mat => mat.trim())
|
||||
.filter(mat => mat);
|
||||
|
||||
|
||||
if (monsterName && materials.length > 0) {
|
||||
monsterToMaterials[monsterName] = materials;
|
||||
materials.forEach(mat => {
|
||||
@@ -262,17 +262,17 @@ if (pathingMode.onlyPathing) log.warn(`${CONSTANTS.LOG_MODULES.PATH}已开启【
|
||||
/**
|
||||
* 初始化并筛选选中的材料分类(适配multi-checkbox的Categories配置)
|
||||
* @returns {string[]} 选中的材料分类列表
|
||||
*/
|
||||
function getSelectedMaterialCategories() {
|
||||
*/
|
||||
function getSelectedMaterialCategories() {
|
||||
// 使用Array.from()确保将settings.Categories转换为真正的数组,适配multi-checkbox返回的类数组对象
|
||||
let selectedCategories = [];
|
||||
|
||||
|
||||
try {
|
||||
selectedCategories = Array.from(settings.Categories || []);
|
||||
} catch (e) {
|
||||
log.error(`${CONSTANTS.LOG_MODULES.MATERIAL}获取分类设置失败: ${e.message}`);
|
||||
}
|
||||
|
||||
|
||||
// 兼容旧的checkbox字段名
|
||||
if (!selectedCategories || selectedCategories.length === 0) {
|
||||
const checkboxToCategory = {
|
||||
@@ -290,25 +290,25 @@ function getSelectedMaterialCategories() {
|
||||
"WeaponAscension": "武器突破",
|
||||
"XP": "祝圣精华"
|
||||
};
|
||||
|
||||
|
||||
Object.keys(checkboxToCategory).forEach(checkboxName => {
|
||||
if (settings[checkboxName] === true) {
|
||||
selectedCategories.push(checkboxToCategory[checkboxName]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 默认分类
|
||||
if (!selectedCategories || selectedCategories.length === 0) {
|
||||
selectedCategories = ["一般素材", "烹饪用食材"];
|
||||
}
|
||||
|
||||
|
||||
// 过滤无效值并映射到实际分类
|
||||
return selectedCategories
|
||||
.filter(cat => typeof cat === 'string' && cat !== "")
|
||||
.map(name => material_mapping[name] || "锻造素材")
|
||||
.filter(name => name !== null);
|
||||
}
|
||||
return selectedCategories
|
||||
.filter(cat => typeof cat === 'string' && cat !== "")
|
||||
.map(name => material_mapping[name] || "锻造素材")
|
||||
.filter(name => name !== null);
|
||||
}
|
||||
|
||||
const selected_materials_array = getSelectedMaterialCategories();
|
||||
|
||||
@@ -630,10 +630,10 @@ function recordRunTime(resourceName, pathName, startTime, endTime, runTime, reco
|
||||
function getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir, pathingFilePath) {
|
||||
const checkDirs = [recordDir, noRecordDir];
|
||||
let latestEndTime = null;
|
||||
|
||||
|
||||
// 生成内容检测码
|
||||
const contentCode = pathingFilePath ? generatePathContentCode(pathingFilePath) : null;
|
||||
|
||||
|
||||
// 清理路径名中的检测码
|
||||
const cleanPathName = pathName.replace(/_[0-9a-fA-F]{8}\.json$/, '.json');
|
||||
|
||||
@@ -645,13 +645,13 @@ function getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir, pathi
|
||||
|
||||
// 按空行分割成记录块
|
||||
const recordBlocks = content.split('\n\n').filter(block => block.includes('路径名: '));
|
||||
|
||||
|
||||
recordBlocks.forEach(block => {
|
||||
const blockLines = block.split('\n');
|
||||
let blockPathName = '';
|
||||
let blockContentCode = '00000000';
|
||||
let blockEndTime = null;
|
||||
|
||||
|
||||
blockLines.forEach(line => {
|
||||
if (line.startsWith('路径名: ')) {
|
||||
blockPathName = line.split('路径名: ')[1];
|
||||
@@ -661,15 +661,15 @@ function getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir, pathi
|
||||
blockEndTime = line.split('结束时间: ')[1];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 清理记录中的路径名检测码
|
||||
const cleanBlockPathName = blockPathName.replace(/_[0-9a-fA-F]{8}\.json$/, '.json');
|
||||
|
||||
|
||||
// 匹配条件:路径名相同 或者 内容检测码相同(新逻辑)
|
||||
const isPathMatch = cleanBlockPathName === cleanPathName;
|
||||
const isContentCodeMatch = contentCode && blockContentCode === contentCode;
|
||||
const isMatch = isPathMatch || isContentCodeMatch;
|
||||
|
||||
|
||||
if (isMatch && blockEndTime) {
|
||||
const endTime = new Date(blockEndTime);
|
||||
if (!latestEndTime || endTime > new Date(latestEndTime)) {
|
||||
@@ -698,10 +698,10 @@ function getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir, pathi
|
||||
function getHistoricalPathRecords(resourceKey, pathName, recordDir, noRecordDir, isFood = false, cache = {}, pathingFilePath) {
|
||||
// 生成内容检测码
|
||||
const contentCode = pathingFilePath ? generatePathContentCode(pathingFilePath) : null;
|
||||
|
||||
|
||||
// 清理路径名中的检测码
|
||||
const cleanPathName = pathName.replace(/_[0-9a-fA-F]{8}\.json$/, '.json');
|
||||
|
||||
|
||||
// 1. 生成唯一缓存键(确保不同路径/不同文件的记录不混淆)
|
||||
const isFoodSuffix = isFood ? CONSTANTS.FOOD_EXP_RECORD_SUFFIX : ".txt";
|
||||
const recordFile = `${recordDir}/${resourceKey}${isFoodSuffix}`;
|
||||
@@ -738,7 +738,7 @@ function getHistoricalPathRecords(resourceKey, pathName, recordDir, noRecordDir,
|
||||
const lines = content.split('\n');
|
||||
// 先按空行分割成独立的记录块,避免跨记录解析
|
||||
const recordBlocks = content.split('\n\n').filter(block => block.includes('路径名: '));
|
||||
|
||||
|
||||
recordBlocks.forEach(block => {
|
||||
const blockLines = block.split('\n').map(line => line.trim()).filter(line => line);
|
||||
let runTime = 0;
|
||||
@@ -781,7 +781,7 @@ function getHistoricalPathRecords(resourceKey, pathName, recordDir, noRecordDir,
|
||||
// 匹配条件:路径名相同 或者 内容检测码相同(新逻辑)
|
||||
const isContentCodeMatch = contentCode && recordContentCode === contentCode;
|
||||
const shouldInclude = (isTargetPath || isContentCodeMatch) && runTime > 0;
|
||||
|
||||
|
||||
if (shouldInclude) {
|
||||
records.push({ runTime, quantityChange, contentCode: recordContentCode });
|
||||
}
|
||||
@@ -815,11 +815,11 @@ function estimatePathTotalTime(entry, recordDir, noRecordDir, cache = {}) {
|
||||
|
||||
// 调用公共函数获取记录(复用缓存)
|
||||
const historicalRecords = getHistoricalPathRecords(
|
||||
resourceKey,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
isFood,
|
||||
resourceKey,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
isFood,
|
||||
cache,
|
||||
pathingFilePath
|
||||
);
|
||||
@@ -854,11 +854,11 @@ function calculatePerTime(resourceName, pathName, recordDir, noRecordDir, isFood
|
||||
const isMonster = monsterToMaterials.hasOwnProperty(resourceName);
|
||||
// 调用公共函数获取记录(复用缓存)
|
||||
const historicalRecords = getHistoricalPathRecords(
|
||||
resourceName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
isFood,
|
||||
resourceName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
isFood,
|
||||
cache,
|
||||
pathingFilePath
|
||||
);
|
||||
@@ -873,7 +873,7 @@ function calculatePerTime(resourceName, pathName, recordDir, noRecordDir, isFood
|
||||
if (isMonster) {
|
||||
// 怪物路径:按中级单位计算
|
||||
const monsterMaterials = monsterToMaterials[resourceName];
|
||||
const gradeRatios = [3, 1, 1 / 3]; // 最高级×3,中级×1,最低级×1/3
|
||||
const gradeRatios = [3, 1, 1/3]; // 最高级×3,中级×1,最低级×1/3
|
||||
|
||||
historicalRecords.forEach(record => {
|
||||
const { runTime, quantityChange } = record;
|
||||
@@ -955,18 +955,28 @@ function canRunPathingFile(currentTime, lastEndTime, refreshCD, pathName) {
|
||||
log.info(`${CONSTANTS.LOG_MODULES.CD}路径${pathName}上次运行:${lastEndTimeDate.toLocaleString()},下次运行:${nextRunTime.toLocaleString()}`);
|
||||
return canRun;
|
||||
} else if (refreshCD.type === 'specific') {
|
||||
const specificHour = refreshCD.hour;
|
||||
const currentDate = new Date();
|
||||
const lastDate = new Date(lastEndTimeDate);
|
||||
const todayRefresh = new Date(currentDate);
|
||||
todayRefresh.setHours(specificHour, 0, 0, 0);
|
||||
if (currentDate > todayRefresh && currentDate.getDate() !== lastDate.getDate()) {
|
||||
return true;
|
||||
}
|
||||
const nextRefreshTime = new Date(todayRefresh);
|
||||
if (currentDate >= todayRefresh) nextRefreshTime.setDate(nextRefreshTime.getDate() + 1);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.CD}路径${pathName}上次运行:${lastEndTimeDate.toLocaleString()},下次运行:${nextRefreshTime.toLocaleString()}`);
|
||||
return false;
|
||||
const specificHour = refreshCD.hour;
|
||||
|
||||
// 计算上次运行后最近的刷新时间
|
||||
const lastRefreshAfterRun = new Date(lastEndTimeDate);
|
||||
lastRefreshAfterRun.setHours(specificHour, 0, 0, 0);
|
||||
if (lastRefreshAfterRun <= lastEndTimeDate) {
|
||||
lastRefreshAfterRun.setDate(lastRefreshAfterRun.getDate() + 1);
|
||||
}
|
||||
|
||||
// 如果当前时间已经过了最近的刷新时间,允许运行
|
||||
if (currentDate >= lastRefreshAfterRun) {
|
||||
log.info(`${CONSTANTS.LOG_MODULES.CD}路径${pathName}上次运行:${lastEndTimeDate.toLocaleString()},最近刷新时间:${lastRefreshAfterRun.toLocaleString()},允许运行`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 计算下次刷新时间
|
||||
const todayRefresh = new Date(currentDate);
|
||||
todayRefresh.setHours(specificHour, 0, 0, 0);
|
||||
const nextRefreshTime = new Date(todayRefresh);
|
||||
if (currentDate >= todayRefresh) nextRefreshTime.setDate(nextRefreshTime.getDate() + 1);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.CD}路径${pathName}上次运行:${lastEndTimeDate.toLocaleString()},下次运行:${nextRefreshTime.toLocaleString()}`);
|
||||
return false;
|
||||
} else if (refreshCD.type === 'instant') {
|
||||
return true;
|
||||
}
|
||||
@@ -998,7 +1008,7 @@ const imageMapCache = new Map(); // 保持固定,不动态刷新
|
||||
const createImageCategoryMap = (imagesDir) => {
|
||||
const map = {};
|
||||
const imageFiles = readAllFilePaths(imagesDir, 0, 1, ['.png']);
|
||||
|
||||
|
||||
for (const imagePath of imageFiles) {
|
||||
const pathParts = imagePath.split(/[\\/]/);
|
||||
if (pathParts.length < 3) continue;
|
||||
@@ -1007,12 +1017,12 @@ const createImageCategoryMap = (imagesDir) => {
|
||||
.replace(/\.png$/i, '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
|
||||
if (!(imageName in map)) {
|
||||
map[imageName] = pathParts[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
@@ -1026,7 +1036,7 @@ const loggedResources = new Set();
|
||||
*/
|
||||
function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
const processedName = (MATERIAL_ALIAS[resourceName] || resourceName).toLowerCase();
|
||||
|
||||
|
||||
if (!imageMapCache.has(imagesDir)) {
|
||||
log.debug(`${CONSTANTS.LOG_MODULES.MATERIAL}初始化图像分类缓存:${imagesDir}`);
|
||||
imageMapCache.set(imagesDir, createImageCategoryMap(imagesDir));
|
||||
@@ -1042,7 +1052,7 @@ function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
if (!loggedResources.has(processedName)) {
|
||||
loggedResources.add(processedName);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1179,22 +1189,22 @@ async function processFoodPathEntry(entry, accumulators, recordDir, noRecordDir,
|
||||
const currentTime = getCurrentTimeInHours();
|
||||
const lastEndTime = getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir, pathingFilePath);
|
||||
const perTime = noRecord ? null : calculatePerTime(
|
||||
resourceName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
resourceName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
true,
|
||||
pathRecordCache,
|
||||
pathingFilePath
|
||||
);
|
||||
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}狗粮路径${pathName} 单位EXP耗时:${perTime ?? '忽略'}秒/EXP`);
|
||||
|
||||
|
||||
const estimatedTime = estimatePathTotalTime({ path: pathingFilePath, resourceName }, recordDir, noRecordDir);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}狗粮路径${pathName} 预计耗时:${estimatedTime}秒`);
|
||||
|
||||
const canRun = canRunPathingFile(currentTime, lastEndTime, refreshCD, pathName)
|
||||
&& isPathValid
|
||||
const canRun = canRunPathingFile(currentTime, lastEndTime, refreshCD, pathName)
|
||||
&& isPathValid
|
||||
&& (noRecord || perTime === null || perTime <= timeCost);
|
||||
|
||||
if (!canRun) {
|
||||
@@ -1252,7 +1262,7 @@ async function processFoodPathEntry(entry, accumulators, recordDir, noRecordDir,
|
||||
finalCumulativeDistance = calculateDistance(initialPosition, finalPosition);
|
||||
const endTime = new Date().toLocaleString();
|
||||
runTime = (new Date(endTime) - new Date(startTime)) / 1000;
|
||||
|
||||
|
||||
const canRecord = runTime > 5 && finalCumulativeDistance > 5;
|
||||
if (canRecord) {
|
||||
const contentCode = pathingFilePath ? generatePathContentCode(pathingFilePath) : "00000000";
|
||||
@@ -1276,9 +1286,9 @@ async function processFoodPathEntry(entry, accumulators, recordDir, noRecordDir,
|
||||
async function processMonsterPathEntry(entry, context) {
|
||||
const { path: pathingFilePath, monsterName } = entry;
|
||||
const pathName = basename(pathingFilePath);
|
||||
const {
|
||||
const {
|
||||
CDCategories, timeCost, recordDir, noRecordDir, imagesDir,
|
||||
materialCategoryMap, flattenedLowCountMaterials,
|
||||
materialCategoryMap, flattenedLowCountMaterials,
|
||||
currentMaterialName: prevMaterialName,
|
||||
materialAccumulatedDifferences, globalAccumulatedDifferences,
|
||||
pathRecordCache
|
||||
@@ -1321,17 +1331,17 @@ async function processMonsterPathEntry(entry, context) {
|
||||
const lastEndTime = getLastRunEndTime(monsterName, pathName, recordDir, noRecordDir, pathingFilePath);
|
||||
const isPathValid = checkPathNameFrequency(monsterName, pathName, recordDir);
|
||||
const perTime = noRecord ? null : calculatePerTime(
|
||||
monsterName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
monsterName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
false,
|
||||
pathRecordCache,
|
||||
pathingFilePath
|
||||
);
|
||||
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}怪物路径${pathName} 单个材料耗时:${perTime ?? '忽略'}`);
|
||||
|
||||
|
||||
const estimatedTime = estimatePathTotalTime({ path: pathingFilePath, monsterName }, recordDir, noRecordDir);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}怪物路径${pathName} 预计耗时:${estimatedTime}秒`);
|
||||
|
||||
@@ -1423,7 +1433,17 @@ async function processMonsterPathEntry(entry, context) {
|
||||
});
|
||||
|
||||
log.info(`${CONSTANTS.LOG_MODULES.MATERIAL}怪物路径${pathName}数量变化: ${JSON.stringify(materialCountDifferences)}`);
|
||||
recordRunTime(monsterName, pathName, startTime, endTime, runTime, recordDir, materialCountDifferences, finalCumulativeDistance, pathingFilePath);
|
||||
// 检查怪物对应的材料是否有超量,如果有,记录到noRecord目录
|
||||
let isExcess = false;
|
||||
const monsterMaterials = monsterToMaterials[monsterName] || [];
|
||||
for (const material of monsterMaterials) {
|
||||
if (excessMaterialNames.includes(material)) {
|
||||
isExcess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const targetRecordDir = isExcess ? noRecordDir : recordDir;
|
||||
recordRunTime(monsterName, pathName, startTime, endTime, runTime, targetRecordDir, materialCountDifferences, finalCumulativeDistance, pathingFilePath);
|
||||
}
|
||||
|
||||
await sleep(1);
|
||||
@@ -1440,7 +1460,7 @@ async function processMonsterPathEntry(entry, context) {
|
||||
finalCumulativeDistance = calculateDistance(initialPosition, finalPosition);
|
||||
const endTime = new Date().toLocaleString();
|
||||
runTime = (new Date(endTime) - new Date(startTime)) / 1000;
|
||||
|
||||
|
||||
const canRecord = runTime > 5 && finalCumulativeDistance > 5;
|
||||
if (canRecord) {
|
||||
const contentCode = pathingFilePath ? generatePathContentCode(pathingFilePath) : "00000000";
|
||||
@@ -1464,9 +1484,9 @@ async function processMonsterPathEntry(entry, context) {
|
||||
async function processNormalPathEntry(entry, context) {
|
||||
const { path: pathingFilePath, resourceName } = entry;
|
||||
const pathName = basename(pathingFilePath);
|
||||
const {
|
||||
const {
|
||||
CDCategories, timeCost, recordDir, noRecordDir,
|
||||
materialCategoryMap, flattenedLowCountMaterials,
|
||||
materialCategoryMap, flattenedLowCountMaterials,
|
||||
currentMaterialName: prevMaterialName,
|
||||
materialAccumulatedDifferences, globalAccumulatedDifferences,
|
||||
pathRecordCache
|
||||
@@ -1501,17 +1521,17 @@ async function processNormalPathEntry(entry, context) {
|
||||
const lastEndTime = getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir, pathingFilePath);
|
||||
const isPathValid = checkPathNameFrequency(resourceName, pathName, recordDir);
|
||||
const perTime = noRecord ? null : calculatePerTime(
|
||||
resourceName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
resourceName,
|
||||
pathName,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
false,
|
||||
pathRecordCache,
|
||||
pathingFilePath
|
||||
);
|
||||
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}材料路径${pathName} 单个材料耗时:${perTime ?? '忽略'}`);
|
||||
|
||||
|
||||
const estimatedTime = estimatePathTotalTime({ path: pathingFilePath, resourceName }, recordDir, noRecordDir);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}材料路径${pathName} 预计耗时:${estimatedTime}秒`);
|
||||
|
||||
@@ -1594,7 +1614,23 @@ async function processNormalPathEntry(entry, context) {
|
||||
});
|
||||
|
||||
log.info(`${CONSTANTS.LOG_MODULES.MATERIAL}材料路径${pathName}数量变化: ${JSON.stringify(materialCountDifferences)}`);
|
||||
recordRunTime(resourceName, pathName, startTime, endTime, runTime, recordDir, materialCountDifferences, finalCumulativeDistance, pathingFilePath);
|
||||
// 检查材料是否在超量名单中,如果是,记录到noRecord目录
|
||||
let isExcess = false;
|
||||
// 检查当前材料是否超量
|
||||
if (excessMaterialNames.includes(resourceName)) {
|
||||
isExcess = true;
|
||||
} else if (monsterToMaterials.hasOwnProperty(resourceName)) {
|
||||
// 对于怪物路径,检查其对应的材料是否有超量
|
||||
const monsterMaterials = monsterToMaterials[resourceName];
|
||||
for (const material of monsterMaterials) {
|
||||
if (excessMaterialNames.includes(material)) {
|
||||
isExcess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const targetRecordDir = isExcess ? noRecordDir : recordDir;
|
||||
recordRunTime(resourceName, pathName, startTime, endTime, runTime, targetRecordDir, materialCountDifferences, finalCumulativeDistance, pathingFilePath);
|
||||
}
|
||||
|
||||
await sleep(1);
|
||||
@@ -1611,7 +1647,7 @@ async function processNormalPathEntry(entry, context) {
|
||||
finalCumulativeDistance = calculateDistance(initialPosition, finalPosition);
|
||||
const endTime = new Date().toLocaleString();
|
||||
runTime = (new Date(endTime) - new Date(startTime)) / 1000;
|
||||
|
||||
|
||||
const canRecord = runTime > 5 && finalCumulativeDistance > 5;
|
||||
if (canRecord) {
|
||||
const contentCode = pathingFilePath ? generatePathContentCode(pathingFilePath) : "00000000";
|
||||
@@ -1647,7 +1683,7 @@ async function processAllPaths(allPaths, CDCategories, materialCategoryMap, time
|
||||
const globalAccumulatedDifferences = {};
|
||||
const materialAccumulatedDifferences = {};
|
||||
// 单路径处理周期内的记录缓存
|
||||
const pathRecordCache = {};
|
||||
const pathRecordCache = {};
|
||||
let context = {
|
||||
CDCategories, timeCost, recordDir, noRecordDir, imagesDir,
|
||||
materialCategoryMap, flattenedLowCountMaterials,
|
||||
@@ -1676,9 +1712,9 @@ async function processAllPaths(allPaths, CDCategories, materialCategoryMap, time
|
||||
}
|
||||
|
||||
const pathTotalTimeSec = estimatePathTotalTime(
|
||||
entry,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
entry,
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
pathRecordCache
|
||||
);
|
||||
const pathTotalTimeMin = pathTotalTimeSec / 60;
|
||||
@@ -1706,15 +1742,15 @@ async function processAllPaths(allPaths, CDCategories, materialCategoryMap, time
|
||||
if (resourceName && isFoodResource(resourceName)) {
|
||||
// 狗粮路径:传递完整校验参数
|
||||
const result = await processFoodPathEntry(
|
||||
entry,
|
||||
entry,
|
||||
{
|
||||
foodExpAccumulator,
|
||||
currentMaterialName: context.currentMaterialName
|
||||
},
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
CDCategories,
|
||||
timeCost,
|
||||
},
|
||||
recordDir,
|
||||
noRecordDir,
|
||||
CDCategories,
|
||||
timeCost,
|
||||
context.pathRecordCache
|
||||
);
|
||||
foodExpAccumulator = result.foodExpAccumulator;
|
||||
@@ -1730,7 +1766,7 @@ async function processAllPaths(allPaths, CDCategories, materialCategoryMap, time
|
||||
}
|
||||
} catch (singleError) {
|
||||
log.error(`${CONSTANTS.LOG_MODULES.PATH}处理路径出错,已跳过:${singleError.message}`);
|
||||
|
||||
|
||||
await sleep(1);
|
||||
if (state.cancelRequested) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.PATH}检测到终止指令,停止处理`);
|
||||
@@ -1750,8 +1786,8 @@ async function processAllPaths(allPaths, CDCategories, materialCategoryMap, time
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentMaterialName: context.currentMaterialName,
|
||||
return {
|
||||
currentMaterialName: context.currentMaterialName,
|
||||
flattenedLowCountMaterials: context.flattenedLowCountMaterials,
|
||||
globalAccumulatedDifferences: context.globalAccumulatedDifferences,
|
||||
foodExpAccumulator
|
||||
@@ -1871,13 +1907,13 @@ async function generateAllPaths(pathingDir, targetResourceNames, cdMaterialNames
|
||||
monsterPaths.forEach((entry, index) => {
|
||||
const materials = monsterToMaterials[entry.monsterName] || [];
|
||||
if (materials.length === 0) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.MONSTER}[怪物路径${index + 1}] 怪物【${entry.monsterName}】无对应材料映射`);
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.MONSTER}[怪物路径${index+1}] 怪物【${entry.monsterName}】无对应材料映射`);
|
||||
return;
|
||||
}
|
||||
materials.forEach(mat => {
|
||||
// 添加到pathing怪物材料集合(用于OCR过滤)
|
||||
ocrContext.pathingMonsterMaterials.add(mat);
|
||||
|
||||
|
||||
const category = matchImageAndGetCategory(mat, imagesDir);
|
||||
if (!category) return;
|
||||
if (!materialCategoryMap[category]) materialCategoryMap[category] = [];
|
||||
@@ -1924,41 +1960,41 @@ async function generateAllPaths(pathingDir, targetResourceNames, cdMaterialNames
|
||||
// 路径优先级规则数组
|
||||
const PATH_PRIORITIES = [
|
||||
// 1. 目标狗粮
|
||||
{
|
||||
source: processedFoodPaths,
|
||||
filter: e => targetResourceNames.includes(e.resourceName)
|
||||
{
|
||||
source: processedFoodPaths,
|
||||
filter: e => targetResourceNames.includes(e.resourceName)
|
||||
},
|
||||
// 2. 目标怪物(掉落材料含目标)
|
||||
{
|
||||
source: processedMonsterPaths,
|
||||
{
|
||||
source: processedMonsterPaths,
|
||||
filter: e => {
|
||||
const materials = monsterToMaterials[e.monsterName] || [];
|
||||
return materials.some(mat => targetResourceNames.includes(mat));
|
||||
}
|
||||
}
|
||||
},
|
||||
// 3. 目标普通材料
|
||||
{
|
||||
source: processedNormalPaths,
|
||||
filter: e => targetResourceNames.includes(e.resourceName)
|
||||
{
|
||||
source: processedNormalPaths,
|
||||
filter: e => targetResourceNames.includes(e.resourceName)
|
||||
},
|
||||
// 4. 剩余狗粮
|
||||
{
|
||||
source: processedFoodPaths,
|
||||
filter: e => !targetResourceNames.includes(e.resourceName)
|
||||
{
|
||||
source: processedFoodPaths,
|
||||
filter: e => !targetResourceNames.includes(e.resourceName)
|
||||
},
|
||||
// 5. 剩余怪物(掉落材料未超量且低数量)
|
||||
{
|
||||
source: processedMonsterPaths,
|
||||
{
|
||||
source: processedMonsterPaths,
|
||||
filter: e => {
|
||||
const materials = monsterToMaterials[e.monsterName] || [];
|
||||
return !materials.some(mat => targetResourceNames.includes(mat)) &&
|
||||
materials.some(mat => !excessMaterialNames.includes(mat));
|
||||
}
|
||||
return !materials.some(mat => targetResourceNames.includes(mat)) &&
|
||||
materials.some(mat => !excessMaterialNames.includes(mat));
|
||||
}
|
||||
},
|
||||
// 6. 剩余普通材料
|
||||
{
|
||||
source: processedNormalPaths,
|
||||
filter: e => !targetResourceNames.includes(e.resourceName)
|
||||
{
|
||||
source: processedNormalPaths,
|
||||
filter: e => !targetResourceNames.includes(e.resourceName)
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1967,7 +2003,7 @@ async function generateAllPaths(pathingDir, targetResourceNames, cdMaterialNames
|
||||
PATH_PRIORITIES.forEach(({ source, filter }, index) => {
|
||||
const filtered = source.filter(filter);
|
||||
allPaths.push(...filtered);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}[优先级${index + 1}] 路径 ${filtered.length} 条`);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.PATH}[优先级${index+1}] 路径 ${filtered.length} 条`);
|
||||
});
|
||||
|
||||
// log.info(`${CONSTANTS.LOG_MODULES.PATH}[最终路径] 共${allPaths.length}条:${allPaths.map(p => basename(p.path))}`);
|
||||
@@ -1994,13 +2030,13 @@ function sendNotificationInChunks(msg, sendFn) {
|
||||
|
||||
const totalChunks = Math.ceil(msg.length / chunkSize);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.MAIN}通知消息过长(${msg.length}字符),拆分为${totalChunks}段发送`);
|
||||
|
||||
|
||||
let start = 0;
|
||||
for (let i = 0; i < totalChunks; i++) {
|
||||
const end = Math.min(start + chunkSize, msg.length);
|
||||
const chunkMsg = `【通知${i + 1}/${totalChunks}】\n${msg.substring(start, end)}`;
|
||||
const chunkMsg = `【通知${i+1}/${totalChunks}】\n${msg.substring(start, end)}`;
|
||||
sendFn(chunkMsg);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.MAIN}已发送第${i + 1}段通知(${chunkMsg.length}字符)`);
|
||||
log.info(`${CONSTANTS.LOG_MODULES.MAIN}已发送第${i+1}段通知(${chunkMsg.length}字符)`);
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
@@ -2062,7 +2098,7 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个
|
||||
|
||||
// 等待超量名单生成
|
||||
let waitTimes = 0;
|
||||
while (excessMaterialNames.length === 0 && !state.cancelRequested && waitTimes < 100) {
|
||||
while (excessMaterialNames.length === 0 && !state.cancelRequested && waitTimes < 100) {
|
||||
await sleep(1000);
|
||||
waitTimes++;
|
||||
}
|
||||
@@ -2070,43 +2106,43 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个
|
||||
log.info(`${CONSTANTS.LOG_MODULES.MAIN}OCR任务收到终止信号,已退出`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const getFilteredTargetTexts = () => {
|
||||
let filtered = allTargetTexts.filter(name => !excessMaterialNames.includes(name));
|
||||
|
||||
|
||||
if (ocrContext.currentPathType === 'monster') {
|
||||
// 怪物路径执行时的过滤逻辑:
|
||||
// 1. 对于怪物材料,只保留:
|
||||
// - 当前怪物的材料
|
||||
// - pathing文件夹中存在且未超量的其他怪物材料
|
||||
// 2. 非怪物材料保持不变
|
||||
|
||||
|
||||
filtered = filtered.filter(name => {
|
||||
// 如果不是怪物材料,保留
|
||||
if (!materialToMonsters[name]) return true;
|
||||
|
||||
|
||||
// 如果是怪物材料,检查是否在允许的列表中
|
||||
const currentMonsterMaterials = ocrContext.currentTargetMaterials || [];
|
||||
const pathingMonsterMaterials = Array.from(ocrContext.pathingMonsterMaterials || new Set());
|
||||
|
||||
|
||||
// 保留当前怪物的材料或pathing中的怪物材料
|
||||
return currentMonsterMaterials.includes(name) || pathingMonsterMaterials.includes(name);
|
||||
});
|
||||
|
||||
|
||||
if (debugLog) {
|
||||
const currentMonsterMaterials = ocrContext.currentTargetMaterials || [];
|
||||
const pathingMonsterMaterials = Array.from(ocrContext.pathingMonsterMaterials || new Set());
|
||||
const additionalMonsterMaterials = pathingMonsterMaterials.filter(mat =>
|
||||
const additionalMonsterMaterials = pathingMonsterMaterials.filter(mat =>
|
||||
!currentMonsterMaterials.includes(mat) && !excessMaterialNames.includes(mat)
|
||||
);
|
||||
|
||||
|
||||
log.info(`OCR拾取列表(怪物路径):`);
|
||||
log.info(` - 当前怪物材料:${currentMonsterMaterials.join('、') || '无'}`);
|
||||
log.info(` - pathing其他怪物材料(未超量):${additionalMonsterMaterials.join('、') || '无'}`);
|
||||
log.info(` - 非怪物材料:${filtered.filter(name => !materialToMonsters[name]).join('、') || '无'}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
@@ -2132,78 +2168,78 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个
|
||||
if (debugLog) log.info(`${CONSTANTS.LOG_MODULES.CD}CD文件中材料名(已过滤):${Array.from(cdMaterialNames).join(', ')}`);
|
||||
|
||||
// 生成材料分类映射
|
||||
let materialCategoryMap = {};
|
||||
|
||||
// 处理选中的材料分类
|
||||
if (selected_materials_array.length > 0 && !pathingMode.onlyPathing) {
|
||||
// 1. 初始化选中的分类(onlyPathing模式除外)
|
||||
selected_materials_array.forEach(selectedCategory => {
|
||||
materialCategoryMap[selectedCategory] = [];
|
||||
});
|
||||
let materialCategoryMap = {};
|
||||
|
||||
// 处理选中的材料分类
|
||||
if (selected_materials_array.length > 0 && !pathingMode.onlyPathing) {
|
||||
// 1. 初始化选中的分类(onlyPathing模式除外)
|
||||
selected_materials_array.forEach(selectedCategory => {
|
||||
materialCategoryMap[selectedCategory] = [];
|
||||
});
|
||||
} else {
|
||||
if (pathingMode.onlyPathing) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.MATERIAL}onlyPathing模式:将自动扫描pathing材料的实际分类`);
|
||||
} else {
|
||||
if (pathingMode.onlyPathing) {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.MATERIAL}onlyPathing模式:将自动扫描pathing材料的实际分类`);
|
||||
} else {
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.MATERIAL}未选择【材料分类】,采用【路径材料】专注模式`);
|
||||
}
|
||||
log.warn(`${CONSTANTS.LOG_MODULES.MATERIAL}未选择【材料分类】,采用【路径材料】专注模式`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 处理路径相关材料(仅includeBoth和onlyPathing模式)
|
||||
if ((pathingMode.includeBoth || pathingMode.onlyPathing) && (Object.keys(materialCategoryMap).length > 0 || pathingMode.onlyPathing)) {
|
||||
const pathingFilePaths = readAllFilePaths(CONSTANTS.PATHING_DIR, 0, 3, ['.json']);
|
||||
const pathEntries = pathingFilePaths.map(path => {
|
||||
const { materialName, monsterName } = extractResourceNameFromPath(path, cdMaterialNames);
|
||||
return { materialName, monsterName };
|
||||
});
|
||||
|
||||
// 2. 处理路径相关材料(仅includeBoth和onlyPathing模式)
|
||||
if ((pathingMode.includeBoth || pathingMode.onlyPathing) && (Object.keys(materialCategoryMap).length > 0 || pathingMode.onlyPathing)) {
|
||||
const pathingFilePaths = readAllFilePaths(CONSTANTS.PATHING_DIR, 0, 3, ['.json']);
|
||||
const pathEntries = pathingFilePaths.map(path => {
|
||||
const { materialName, monsterName } = extractResourceNameFromPath(path, cdMaterialNames);
|
||||
return { materialName, monsterName };
|
||||
});
|
||||
// 收集所有材料(含怪物掉落)
|
||||
const allMaterials = new Set();
|
||||
pathEntries.forEach(({ materialName, monsterName }) => {
|
||||
if (materialName) allMaterials.add(materialName);
|
||||
if (monsterName) {
|
||||
(monsterToMaterials[monsterName] || []).forEach(mat => allMaterials.add(mat));
|
||||
}
|
||||
});
|
||||
|
||||
// 收集所有材料(含怪物掉落)
|
||||
const allMaterials = new Set();
|
||||
pathEntries.forEach(({ materialName, monsterName }) => {
|
||||
if (materialName) allMaterials.add(materialName);
|
||||
if (monsterName) {
|
||||
(monsterToMaterials[monsterName] || []).forEach(mat => allMaterials.add(mat));
|
||||
// 构建分类映射
|
||||
Array.from(allMaterials).forEach(resourceName => {
|
||||
const category = matchImageAndGetCategory(resourceName, CONSTANTS.IMAGES_DIR);
|
||||
if (category) {
|
||||
if (!materialCategoryMap[category]) {
|
||||
materialCategoryMap[category] = [];
|
||||
}
|
||||
});
|
||||
|
||||
// 构建分类映射
|
||||
Array.from(allMaterials).forEach(resourceName => {
|
||||
const category = matchImageAndGetCategory(resourceName, CONSTANTS.IMAGES_DIR);
|
||||
if (category) {
|
||||
if (!materialCategoryMap[category]) {
|
||||
materialCategoryMap[category] = [];
|
||||
}
|
||||
if (!materialCategoryMap[category].includes(resourceName)) {
|
||||
materialCategoryMap[category].push(resourceName);
|
||||
}
|
||||
if (!materialCategoryMap[category].includes(resourceName)) {
|
||||
materialCategoryMap[category].push(resourceName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 在onlyPathing或onlyCategory模式下,保留所有选中的分类
|
||||
if (pathingMode.onlyPathing || pathingMode.onlyCategory) {
|
||||
// 对于onlyCategory模式,保留所有选中的分类,即使是空数组
|
||||
// 对于onlyPathing模式,删除空数组
|
||||
if (pathingMode.onlyPathing) {
|
||||
Object.keys(materialCategoryMap).forEach(category => {
|
||||
if (materialCategoryMap[category].length === 0) {
|
||||
delete materialCategoryMap[category];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 在onlyPathing或onlyCategory模式下,保留所有选中的分类
|
||||
if (pathingMode.onlyPathing || pathingMode.onlyCategory) {
|
||||
// 对于onlyCategory模式,保留所有选中的分类,即使是空数组
|
||||
// 对于onlyPathing模式,删除空数组
|
||||
if (pathingMode.onlyPathing) {
|
||||
Object.keys(materialCategoryMap).forEach(category => {
|
||||
if (materialCategoryMap[category].length === 0) {
|
||||
delete materialCategoryMap[category];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 在onlyCategory模式下,确保materialCategoryMap只包含选中的分类
|
||||
if (pathingMode.onlyCategory) {
|
||||
const selectedCategoriesSet = new Set(selected_materials_array);
|
||||
const filteredMap = {};
|
||||
|
||||
// 只保留选中的分类,即使是空数组
|
||||
selected_materials_array.forEach(category => {
|
||||
filteredMap[category] = materialCategoryMap[category] || [];
|
||||
});
|
||||
|
||||
materialCategoryMap = filteredMap;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 在onlyCategory模式下,确保materialCategoryMap只包含选中的分类
|
||||
if (pathingMode.onlyCategory) {
|
||||
const selectedCategoriesSet = new Set(selected_materials_array);
|
||||
const filteredMap = {};
|
||||
|
||||
// 只保留选中的分类,即使是空数组
|
||||
selected_materials_array.forEach(category => {
|
||||
filteredMap[category] = materialCategoryMap[category] || [];
|
||||
});
|
||||
|
||||
materialCategoryMap = filteredMap;
|
||||
}
|
||||
|
||||
// 生成路径数组
|
||||
const { allPaths, pathingMaterialCounts } = await generateAllPaths(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "背包统计采集系统",
|
||||
"version": "2.61",
|
||||
"version": "2.62",
|
||||
"bgi_version": "0.55",
|
||||
"description": "可统计背包养成道具、部分食物、素材的数量;根据设定数量、根据材料刷新CD执行挖矿、采集、刷怪等的路径。优势:\n+ 1. 自动判断材料CD,不需要管材料CD有没有好;\n+ 2. 可以随意添加路径,能自动排除低效、无效路径;\n+ 3. 有独立名单识别,不会交互路边的npc或是神像;可自定义识别名单,具体方法看【问题解答】增减识别名单\n+ 4. 有实时的弹窗模块,提供了常见的几种:路边信件、过期物品、月卡、调查;\n+ 5. 可识别爆满的路径材料,自动屏蔽;更多详细内容查看readme.md;可在我的主页下载 路径重命名 工具JS,给路径名批量添加检测码,方便识别。",
|
||||
"description": "可统计背包养成道具、部分食物、素材的数量;根据设定数量、根据材料刷新CD执行挖矿、采集、刷怪等的路径。优势:\n+ 1. 自动判断材料CD,不需要管材料CD有没有好;\n+ 2. 可以随意添加路径,能自动排除低效、无效路径;\n+ 3. 有独立名单识别,不会交互路边的npc或是神像;可自定义识别名单,具体方法看【问题解答】增减识别名单\n+ 4. 有实时的弹窗模块,提供了常见的几种:路边信件、过期物品、月卡、调查;\n+ 5. 可识别爆满的路径材料,自动屏蔽;更多详细内容查看readme.md;可在我的主页下载 路径重命名 工具JS,给路径名批量添加检测码,方便识别。",
|
||||
"saved_files": [
|
||||
"pathing/",
|
||||
"history_record/",
|
||||
@@ -13,8 +13,8 @@
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "吉吉喵",
|
||||
"links": "https://github.com/JJMdzh"
|
||||
"name": "吉吉喵",
|
||||
"links": "https://github.com/JJMdzh"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
|
||||
Reference in New Issue
Block a user