mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-16 03:33:25 +08:00
背包统计采集,新增noRecord模式、怪物材料CD文件 (#1985)
* 背包统计采集,新增noRecord模式、怪物材料CD文件 增加无路径间的扫描、无数量记录的noRecord模式,适合路径记录已经炼成的玩家。新增怪物材料CD文件,以支持轻度刷怪需求。 * Update settings.json * Add files via upload
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// ==UserScript==
|
||||
// @name 背包统计采集系统
|
||||
// @version 2.40
|
||||
// @version 2.42
|
||||
// @description 识别路径文件,根据材料数量,自动执行路线,或者主动选择材料类别,统计材料数量
|
||||
// @author 吉吉喵
|
||||
// @match 原神版本:5.6;BGI 版本:0.44.8
|
||||
@@ -22,7 +22,7 @@
|
||||
*
|
||||
* 使用即表示您已阅读并同意上述条款。
|
||||
*
|
||||
* Last Updated: 2025-05-19
|
||||
* Last Updated: 2025-09-23
|
||||
*/
|
||||
# 背包材料统计
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
📄 薄荷-吉吉喵.json
|
||||
📁 苹果/
|
||||
📄 旅行者的果园.json
|
||||
📁 名刀镡/
|
||||
📄 旅行者的名刀镡.json
|
||||
📁history_record/ 背包统计 自动生成,每类的历史记录(每类中旧纪录上限为365个)
|
||||
📁overwrite_record/ 背包统计 自动生成,每类的最新一次记录
|
||||
📁pathing_record/ 自动生成,路径运行时间记录
|
||||
@@ -49,8 +51,8 @@
|
||||
|
||||
## 使用方法
|
||||
1. 将脚本添加至调度器。
|
||||
2. 右键点击脚本以修改JS自定义配置。
|
||||
3. 执行路径功能,需要📁pathing有路径文件夹
|
||||
2. 右键点击脚本以修改JS自定义配置:刷怪、采集推荐:2.仅📁pathing材料;纯材料数量识别选3.仅【材料分类】勾选。
|
||||
3. 执行路径功能前,需要📁pathing有【材料正式名】的文件夹,具体可参考BetterGI\Repos\bettergi-scripts-list-git\repo\pathing目录,选取【材料正式名】的文件夹,内有子文件夹或者路径。复制到BetterGI\User\JsScript\背包材料统计\pathing目录,具体参考## 文件结构
|
||||
|
||||
## 注意
|
||||
因食物部分图片未补足,为适配快速滑页,苹果、日落果、星蕈、活化的星蕈、枯焦的星蕈、泡泡桔、烛伞蘑菇、美味的宝石闪闪,这八个食物必须有,且在第一行。不然这几个食物会无法识别。
|
||||
@@ -81,4 +83,5 @@
|
||||
+ v2.29 新增排除提示;调整平均时间成本计算;过滤掉差异较大的记录;
|
||||
+ v2.30 更改路径专注模式默认值,加log提示;去除注释掉的调试log;背包材料统计更改名为背包统计采集系统
|
||||
+ v2.40 优化背包识别时占用的内存;增加通知;
|
||||
+ v2.41 修复勾选分类的本地记录bug,新增仅背包统计选项;如果本地记录已经遭到破坏。比如每条路径都产生大量材料名-0.txt,就只能手动清理或者删除本地记录pathing_record,重新跑
|
||||
+ v2.41 修复勾选分类的本地记录bug,新增仅背包统计选项;如果本地记录已经遭到破坏。比如每条路径都产生大量材料名-0.txt,就只能手动清理或者删除本地记录pathing_record,重新跑;
|
||||
+ v2.42 增加无路径间的扫描、无数量记录的noRecord模式,适合路径记录已经炼成的玩家。新增怪物材料CD文件,以支持轻度刷怪需求。
|
||||
@@ -4,6 +4,7 @@ const OCRdelay = Math.min(50, Math.max(0, Math.floor(Number(settings.OcrDelay) |
|
||||
const imageDelay = Math.min(1000, Math.max(0, Math.floor(Number(settings.ImageDelay) || 0))); // 识图基准时长
|
||||
const timeCost = Math.min(300, Math.max(0, Math.floor(Number(settings.TimeCost) || 30))); // 耗时和材料数量的比值,即一个材料多少秒
|
||||
const notify = settings.notify || false;
|
||||
const noRecord = settings.noRecord || false; // 全局控制参数(无需传递)
|
||||
// 定义映射表"unselected": "反选材料分类",
|
||||
const material_mapping = {
|
||||
"General": "一般素材",
|
||||
@@ -774,7 +775,6 @@ function readAllFilePaths(dirPath, currentDepth = 0, maxDepth = 3, includeExtens
|
||||
const fileExtension = entry.substring(entry.lastIndexOf('.'));
|
||||
if (includeExtensions.includes(fileExtension.toLowerCase())) {
|
||||
filePaths.push(entry); // 添加文件路径
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -980,30 +980,86 @@ function recordRunTime(resourceName, pathName, startTime, endTime, runTime, reco
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------- 新增:通知分段发送工具函数 --------------------------
|
||||
/**
|
||||
* 通知分段发送(超过500字符自动拆分)
|
||||
* @param {string} msg - 原始通知消息
|
||||
* @param {Function} sendFn - 原始通知发送函数(如notification.Send)
|
||||
* @param {number} chunkSize - 每段最大字符数(默认500)
|
||||
*/
|
||||
function sendNotificationInChunks(msg, sendFn, chunkSize = 500) {
|
||||
if (!notify) return; // 未开启通知,直接返回
|
||||
if (typeof msg !== 'string' || msg.length === 0) return;
|
||||
|
||||
// 短消息直接发送
|
||||
if (msg.length <= chunkSize) {
|
||||
sendFn(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 长消息拆分发送
|
||||
const totalChunks = Math.ceil(msg.length / chunkSize);
|
||||
log.info(`通知消息过长(${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)}`;
|
||||
|
||||
// 发送当前段
|
||||
sendFn(chunkMsg);
|
||||
log.info(`已发送第${i+1}段通知(${chunkMsg.length}字符)`);
|
||||
|
||||
// 更新起始位置
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
// noRecord=true时的最终汇总记录函数
|
||||
function recordFinalSummary(recordDir, firstScanTime, endTime, totalRunTime, totalDifferences) {
|
||||
const summaryPath = `${recordDir}/final_summary_noRecord.txt`;
|
||||
const content = `===== 材料收集最终汇总(noRecord模式)=====
|
||||
首次扫描时间:${firstScanTime}
|
||||
末次扫描时间:${endTime}
|
||||
总耗时:${totalRunTime.toFixed(1)}秒
|
||||
累计获取材料:
|
||||
${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个`).join('\n')}
|
||||
=========================================\n\n`;
|
||||
writeContentToFile(summaryPath, content);
|
||||
log.info(`noRecord模式:最终汇总已记录至 ${summaryPath}`);
|
||||
}
|
||||
|
||||
// 读取材料对应的文件,获取上次运行的结束时间
|
||||
function getLastRunEndTime(resourceName, pathName, recordDir) {
|
||||
const recordPath = `${recordDir}/${resourceName}.txt`; // 记录文件路径,以材料名命名
|
||||
try {
|
||||
const content = file.readTextSync(recordPath); // 同步读取记录文件
|
||||
const lines = content.split('\n');
|
||||
function getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir) { // 接收recordDir和noRecordDir参数
|
||||
const checkDirs = [recordDir, noRecordDir];
|
||||
let latestEndTime = null;
|
||||
|
||||
// 从文件内容的开头开始查找
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('路径名: ')) {
|
||||
const currentPathName = lines[i].split('路径名: ')[1];
|
||||
if (currentPathName === pathName) {
|
||||
const endTimeLine = lines[i + 2]; // 假设结束时间在路径名后的第三行
|
||||
if (endTimeLine.startsWith('结束时间: ')) {
|
||||
return endTimeLine.split('结束时间: ')[1]; // 返回结束时间
|
||||
checkDirs.forEach(dir => {
|
||||
const recordPath = `${dir}/${resourceName}.txt`;
|
||||
try {
|
||||
const content = file.readTextSync(recordPath);
|
||||
const lines = content.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].startsWith('路径名: ') && lines[i].split('路径名: ')[1] === pathName) {
|
||||
const endTimeLine = lines[i + 2];
|
||||
if (endTimeLine?.startsWith('结束时间: ')) {
|
||||
const endTimeStr = endTimeLine.split('结束时间: ')[1];
|
||||
const endTime = new Date(endTimeStr);
|
||||
|
||||
if (!latestEndTime || endTime > new Date(latestEndTime)) {
|
||||
latestEndTime = endTimeStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log.debug(`目录${dir}中无${resourceName}.txt记录,跳过检查`);
|
||||
}
|
||||
} catch (error) {
|
||||
log.warn(`未找到记录文件或记录文件中无结束时间: ${recordPath}`);
|
||||
}
|
||||
return null; // 如果未找到记录文件或结束时间,返回 null
|
||||
});
|
||||
|
||||
return latestEndTime;
|
||||
}
|
||||
|
||||
// 计算时间成本
|
||||
@@ -1091,9 +1147,6 @@ function canRunPathingFile(currentTime, lastEndTime, refreshCD, pathName) {
|
||||
// 处理“N次0点”这样的特殊规则
|
||||
const times = refreshCD.times;
|
||||
|
||||
// 计算从上次运行时间到当前时间的天数差
|
||||
let daysPassed = Math.floor((currentDate - lastEndTimeDate) / (1000 * 60 * 60 * 24));
|
||||
|
||||
// 计算下一个刷新时间
|
||||
const nextRunTime = new Date(lastEndTimeDate);
|
||||
nextRunTime.setDate(lastEndTimeDate.getDate() + times); // 在上次运行时间的基础上加上N天
|
||||
@@ -1184,12 +1237,13 @@ function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
(async function () {
|
||||
// 定义文件夹路径
|
||||
const materialDir = "materialsCD"; // 存储材料信息的文件夹
|
||||
const pathingDir = "pathing"; // 存储路径信息的文件夹
|
||||
const recordDir = "pathing_record"; // 存储运行记录的文件夹
|
||||
const noRecordDir = `${recordDir}/noRecord`; // 基于recordDir定义
|
||||
|
||||
const imagesDir = "assets\\images"; // 存储图片的文件夹
|
||||
|
||||
// 从设置中获取目标材料名称
|
||||
@@ -1262,8 +1316,8 @@ function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
|
||||
// 调用背包材料统计
|
||||
const pathingMaterialCounts = await MaterialPath(materialCategoryMap);
|
||||
log.info(`materialCategoryMap文本:${JSON.stringify(materialCategoryMap)}`);
|
||||
log.info(`目标文本:${JSON.stringify(pathingMaterialCounts)}`);
|
||||
// log.info(`materialCategoryMap文本:${JSON.stringify(materialCategoryMap)}`);
|
||||
// log.info(`目标文本:${JSON.stringify(pathingMaterialCounts)}`);
|
||||
if (pathingMode.onlyCategory) {
|
||||
return;
|
||||
}
|
||||
@@ -1282,7 +1336,7 @@ function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
if (lowCountMaterialNames.length === 0) {
|
||||
log.info(`所有路径材料的数量均高于目标数量${targetCount}`);
|
||||
}
|
||||
log.info(`目标文本:${JSON.stringify(lowCountMaterialNames)}`);
|
||||
// log.info(`目标文本:${JSON.stringify(lowCountMaterialNames)}`);
|
||||
// 将路径文件按是否为目标材料分类
|
||||
const prioritizedPaths = [];
|
||||
const normalPaths = [];
|
||||
@@ -1316,6 +1370,12 @@ function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
// 假设 flattenedLowCountMaterials 是一个全局变量或在外部定义的变量
|
||||
let currentMaterialName = null; // 用于记录当前材料名
|
||||
|
||||
// 记录首次扫描时间和初始数量(noRecord=true时用)
|
||||
const firstScanTime = new Date().toLocaleString();
|
||||
const initialMaterialCounts = flattenedLowCountMaterials.reduce((acc, mat) => {
|
||||
acc[mat.name] = parseInt(mat.count, 10) || 0;
|
||||
return acc;
|
||||
}, {});
|
||||
// 全局累积差值统计(记录所有材料的总变化量)
|
||||
const globalAccumulatedDifferences = {};
|
||||
// 按材料分类的累积差值统计(记录每种材料的累计变化)
|
||||
@@ -1331,152 +1391,179 @@ function matchImageAndGetCategory(resourceName, imagesDir) {
|
||||
for (const [refreshCDKey, materialList] of Object.entries(materials)) {
|
||||
const refreshCD = JSON.parse(refreshCDKey);
|
||||
if (materialList.includes(resourceName)) {
|
||||
// 获取当前时间
|
||||
const currentTime = getCurrentTimeInHours();
|
||||
// 调用时传递recordDir和noRecordDir
|
||||
const lastEndTime = getLastRunEndTime(resourceName, pathName, recordDir, noRecordDir);
|
||||
// 调用时传递recordDir
|
||||
const isPathValid = checkPathNameFrequency(resourceName, pathName, recordDir);
|
||||
// 调用时传递recordDir
|
||||
const perTime = noRecord ? null : calculatePerTime(resourceName, pathName, recordDir);
|
||||
|
||||
// 读取上次运行的结束时间
|
||||
const lastEndTime = getLastRunEndTime(resourceName, pathName, recordDir);
|
||||
log.info(`路径文件:${pathName} 单个材料耗时:${perTime || 'noRecord模式忽略'}`);
|
||||
|
||||
// 计算效率
|
||||
const perTime = calculatePerTime(resourceName, pathName, recordDir);
|
||||
|
||||
log.info(`路径文件:${pathName} 单个材料耗时:${perTime}秒`);
|
||||
// 判断是否可以运行脚本
|
||||
if (
|
||||
canRunPathingFile(currentTime, lastEndTime, refreshCD, pathName) &&
|
||||
checkPathNameFrequency(recordDir, resourceName, pathName) &&
|
||||
(perTime === null || perTime <= timeCost)
|
||||
isPathValid &&
|
||||
(noRecord || perTime === null || perTime <= timeCost)
|
||||
) {
|
||||
log.info(`可调用路径文件:${pathName}`);
|
||||
|
||||
// 根据 materialCategoryMap 构建 resourceCategoryMap
|
||||
const resourceCategoryMap = {};
|
||||
for (const [materialCategory, materialList] of Object.entries(materialCategoryMap)) {
|
||||
if (materialList.includes(resourceName)) {
|
||||
resourceCategoryMap[materialCategory] = [resourceName];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 输出 resourceCategoryMap 以供调试
|
||||
log.info(`resourceCategoryMap: ${JSON.stringify(resourceCategoryMap, null, 2)}`);
|
||||
|
||||
// 如果材料名发生变化,更新 flattenedLowCountMaterials
|
||||
if (currentMaterialName !== resourceName) {
|
||||
// 材料名变更前,输出上一材料的累积差值并通知
|
||||
if (currentMaterialName && materialAccumulatedDifferences[currentMaterialName]) {
|
||||
const prevDiffs = materialAccumulatedDifferences[currentMaterialName];
|
||||
log.info(`材料[${currentMaterialName}]收集完成,累积差值:${JSON.stringify(prevDiffs, null, 2)}`);
|
||||
if (notify) {
|
||||
notification.Send(`材料[${currentMaterialName}]收集完成,累计获取:${JSON.stringify(prevDiffs, null, 2)}`);
|
||||
}
|
||||
const resourceCategoryMap = {};
|
||||
for (const [materialCategory, materialList] of Object.entries(materialCategoryMap)) {
|
||||
if (materialList.includes(resourceName)) {
|
||||
resourceCategoryMap[materialCategory] = [resourceName];
|
||||
break;
|
||||
}
|
||||
currentMaterialName = resourceName; // 更新当前材料名
|
||||
// 调用背包材料统计(获取当前材料数量)
|
||||
}
|
||||
log.info(`resourceCategoryMap: ${JSON.stringify(resourceCategoryMap, null, 2)}`);
|
||||
|
||||
if (noRecord) {
|
||||
if (currentMaterialName !== resourceName) {
|
||||
currentMaterialName = resourceName;
|
||||
materialAccumulatedDifferences[resourceName] = {};
|
||||
log.info(`noRecord模式:材料名变更为【${resourceName}】`);
|
||||
}
|
||||
|
||||
const startTime = new Date().toLocaleString();
|
||||
const initialPosition = genshin.getPositionFromMap();
|
||||
await pathingScript.runFile(pathingFilePath);
|
||||
const finalPosition = genshin.getPositionFromMap();
|
||||
const finalCumulativeDistance = calculateDistance(initialPosition, finalPosition);
|
||||
const endTime = new Date().toLocaleString();
|
||||
const runTime = (new Date(endTime) - new Date(startTime)) / 1000;
|
||||
|
||||
const noRecordContent = `路径名: ${pathName}\n开始时间: ${startTime}\n结束时间: ${endTime}\n运行时间: ${runTime}秒\n数量变化: noRecord模式忽略\n\n`;
|
||||
writeContentToFile(`${noRecordDir}/${resourceName}.txt`, noRecordContent);
|
||||
log.info(`noRecord模式:已记录至 ${noRecordDir}/${resourceName}.txt`);
|
||||
} else {
|
||||
if (currentMaterialName !== resourceName) {
|
||||
if (currentMaterialName && materialAccumulatedDifferences[currentMaterialName]) {
|
||||
const prevDiffs = materialAccumulatedDifferences[currentMaterialName];
|
||||
log.info(`材料[${currentMaterialName}]收集完成,累积差值:${JSON.stringify(prevDiffs, null, 2)}`);
|
||||
const prevMsg = `材料[${currentMaterialName}]收集完成,累计获取:${JSON.stringify(prevDiffs, null, 2)}`;
|
||||
sendNotificationInChunks(prevMsg, notification.Send);
|
||||
}
|
||||
currentMaterialName = resourceName;
|
||||
const updatedLowCountMaterials = await MaterialPath(resourceCategoryMap);
|
||||
flattenedLowCountMaterials = updatedLowCountMaterials
|
||||
.flat()
|
||||
.sort((a, b) => parseInt(a.count, 10) - parseInt(b.count, 10));
|
||||
materialAccumulatedDifferences[resourceName] = {};
|
||||
}
|
||||
|
||||
const startTime = new Date().toLocaleString();
|
||||
const initialPosition = genshin.getPositionFromMap();
|
||||
await pathingScript.runFile(pathingFilePath);
|
||||
const finalPosition = genshin.getPositionFromMap();
|
||||
const finalCumulativeDistance = calculateDistance(initialPosition, finalPosition);
|
||||
const endTime = new Date().toLocaleString();
|
||||
const runTime = (new Date(endTime) - new Date(startTime)) / 1000;
|
||||
|
||||
const updatedLowCountMaterials = await MaterialPath(resourceCategoryMap);
|
||||
// 展平数组并按数量从小到大排序
|
||||
flattenedLowCountMaterials = updatedLowCountMaterials
|
||||
const flattenedUpdatedMaterialCounts = updatedLowCountMaterials
|
||||
.flat()
|
||||
.sort((a, b) => parseInt(a.count, 10) - parseInt(b.count, 10));
|
||||
log.info(`材料名变更,更新了 flattenedLowCountMaterials`);
|
||||
|
||||
// 初始化当前材料的累积差值记录
|
||||
materialAccumulatedDifferences[resourceName] = {};
|
||||
}
|
||||
|
||||
// 记录开始时间
|
||||
const startTime = new Date().toLocaleString();
|
||||
|
||||
// 在路径执行前执行一次位移监测
|
||||
const initialPosition = genshin.getPositionFromMap();
|
||||
let initialCumulativeDistance = 0;
|
||||
|
||||
// 调用路径文件
|
||||
await pathingScript.runFile(pathingFilePath);
|
||||
|
||||
// 在路径执行后执行一次位移监测
|
||||
const finalPosition = genshin.getPositionFromMap();
|
||||
const finalCumulativeDistance = calculateDistance(initialPosition, finalPosition);
|
||||
|
||||
// 记录结束时间
|
||||
const endTime = new Date().toLocaleString();
|
||||
|
||||
// 计算运行时间
|
||||
const runTime = (new Date(endTime) - new Date(startTime)) / 1000; // 秒
|
||||
|
||||
// 调用背包材料统计(获取调用路径文件后的材料数量)
|
||||
const updatedLowCountMaterials = await MaterialPath(resourceCategoryMap);
|
||||
|
||||
// 展平数组并按数量从小到大排序
|
||||
const flattenedUpdatedMaterialCounts = updatedLowCountMaterials
|
||||
.flat()
|
||||
.sort((a, b) => parseInt(a.count, 10) - parseInt(b.count, 10));
|
||||
|
||||
|
||||
// 创建一个映射,用于存储更新前后的数量差值
|
||||
const materialCountDifferences = {};
|
||||
|
||||
// 遍历更新后的材料数量,计算差值
|
||||
flattenedUpdatedMaterialCounts.forEach(updatedMaterial => {
|
||||
const originalMaterial = flattenedLowCountMaterials.find(material => material.name === updatedMaterial.name);
|
||||
if (originalMaterial) {
|
||||
const originalCount = parseInt(originalMaterial.count, 10);
|
||||
const updatedCount = parseInt(updatedMaterial.count, 10);
|
||||
const difference = updatedCount - originalCount;
|
||||
|
||||
// 只记录非零差值,或者是当前处理的resourceName(即使差值为0)
|
||||
if (difference !== 0 || updatedMaterial.name === resourceName) {
|
||||
materialCountDifferences[updatedMaterial.name] = difference;
|
||||
|
||||
// 更新全局累积差值
|
||||
if (globalAccumulatedDifferences[updatedMaterial.name]) {
|
||||
globalAccumulatedDifferences[updatedMaterial.name] += difference;
|
||||
} else {
|
||||
globalAccumulatedDifferences[updatedMaterial.name] = difference;
|
||||
}
|
||||
|
||||
// 更新当前材料的累积差值
|
||||
if (materialAccumulatedDifferences[resourceName][updatedMaterial.name]) {
|
||||
materialAccumulatedDifferences[resourceName][updatedMaterial.name] += difference;
|
||||
} else {
|
||||
materialAccumulatedDifferences[resourceName][updatedMaterial.name] = difference;
|
||||
const materialCountDifferences = {};
|
||||
flattenedUpdatedMaterialCounts.forEach(updatedMaterial => {
|
||||
const originalMaterial = flattenedLowCountMaterials.find(material => material.name === updatedMaterial.name);
|
||||
if (originalMaterial) {
|
||||
const originalCount = parseInt(originalMaterial.count, 10);
|
||||
const updatedCount = parseInt(updatedMaterial.count, 10);
|
||||
const difference = updatedCount - originalCount;
|
||||
if (difference !== 0 || updatedMaterial.name === resourceName) {
|
||||
materialCountDifferences[updatedMaterial.name] = difference;
|
||||
if (globalAccumulatedDifferences[updatedMaterial.name]) {
|
||||
globalAccumulatedDifferences[updatedMaterial.name] += difference;
|
||||
} else {
|
||||
globalAccumulatedDifferences[updatedMaterial.name] = difference;
|
||||
}
|
||||
if (materialAccumulatedDifferences[resourceName][updatedMaterial.name]) {
|
||||
materialAccumulatedDifferences[resourceName][updatedMaterial.name] += difference;
|
||||
} else {
|
||||
materialAccumulatedDifferences[resourceName][updatedMaterial.name] = difference;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 更新 flattenedLowCountMaterials 为最新的材料数量
|
||||
flattenedLowCountMaterials = flattenedLowCountMaterials.map(material => {
|
||||
// 找到对应的更新后的材料数量
|
||||
const updatedMaterial = flattenedUpdatedMaterialCounts.find(updated => updated.name === material.name);
|
||||
if (updatedMaterial) {
|
||||
return { ...material, count: updatedMaterial.count }; // 更新数量
|
||||
flattenedLowCountMaterials = flattenedLowCountMaterials.map(material => {
|
||||
const updatedMaterial = flattenedUpdatedMaterialCounts.find(updated => updated.name === material.name);
|
||||
if (updatedMaterial) {
|
||||
return { ...material, count: updatedMaterial.count };
|
||||
}
|
||||
return material;
|
||||
});
|
||||
|
||||
log.info(`数量变化: ${JSON.stringify(materialCountDifferences, null, 2)}`);
|
||||
// 调用时传递recordDir
|
||||
recordRunTime(resourceName, pathName, startTime, endTime, runTime, recordDir, materialCountDifferences, finalCumulativeDistance);
|
||||
}
|
||||
return material;
|
||||
});
|
||||
|
||||
// 打印数量差值
|
||||
log.info(`数量变化: ${JSON.stringify(materialCountDifferences, null, 2)}`);
|
||||
|
||||
// 记录运行时间到材料对应的文件中
|
||||
recordRunTime(resourceName, pathName, startTime, endTime, runTime, recordDir, materialCountDifferences, finalCumulativeDistance);
|
||||
log.info(`当前材料名: ${JSON.stringify(resourceName, null, 2)}`);
|
||||
|
||||
categoryFound = true;
|
||||
|
||||
break;
|
||||
categoryFound = true;
|
||||
break;
|
||||
} else {
|
||||
if (perTime !== null && perTime > timeCost) {
|
||||
log.info(`路径文件 ${pathName} 的单个材料耗时大于 ${timeCost} ,不执行`);
|
||||
} else {
|
||||
log.info(`路径文件 ${pathName} 未能执行!`);
|
||||
}
|
||||
log.info(`路径文件 ${pathName} 未能执行`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (categoryFound) break;
|
||||
}
|
||||
await sleep(1); // 夹断
|
||||
}
|
||||
|
||||
// noRecord=false时保留原有全局通知(true时跳过,末次统一通知)
|
||||
if (!noRecord && Object.keys(globalAccumulatedDifferences).length > 0) {
|
||||
log.info(`所有材料收集完成,全局累积差值:${JSON.stringify(globalAccumulatedDifferences, null, 2)}`);
|
||||
// -------------------------- 替换2:所有材料完成通知(分段发送)--------------------------
|
||||
let allDoneMsg = "所有材料收集完成,累计获取:\n";
|
||||
for (const [name, diff] of Object.entries(globalAccumulatedDifferences)) {
|
||||
allDoneMsg += ` ${name}: ${diff}个\n`;
|
||||
}
|
||||
sendNotificationInChunks(allDoneMsg, notification.Send);
|
||||
}
|
||||
|
||||
// noRecord=true时执行末次扫描和总汇总
|
||||
if (noRecord) {
|
||||
log.info(`\nnoRecord模式:开始末次背包扫描,计算总差值`);
|
||||
// 末次扫描获取最终数量
|
||||
const finalMaterialCounts = await MaterialPath(materialCategoryMap);
|
||||
const flattenedFinal = finalMaterialCounts.flat();
|
||||
const finalCounts = flattenedFinal.reduce((acc, mat) => {
|
||||
acc[mat.name] = parseInt(mat.count, 10) || 0;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// 计算总差值(仅保留增加的材料)
|
||||
const totalDifferences = {};
|
||||
Object.keys(initialMaterialCounts).forEach(name => {
|
||||
const diff = finalCounts[name] - initialMaterialCounts[name];
|
||||
if (diff > 0) {
|
||||
totalDifferences[name] = diff;
|
||||
}
|
||||
});
|
||||
|
||||
// 输出总结果
|
||||
const endTime = new Date().toLocaleString();
|
||||
const totalRunTime = (new Date(endTime) - new Date(firstScanTime)) / 1000;
|
||||
log.info(`\nnoRecord模式:所有材料收集完成`);
|
||||
log.info(`首次扫描时间:${firstScanTime}`);
|
||||
log.info(`末次扫描时间:${endTime}`);
|
||||
log.info(`总耗时:${totalRunTime.toFixed(1)}秒`);
|
||||
log.info(`总累积获取:${JSON.stringify(totalDifferences, null, 2)}`);
|
||||
|
||||
// 记录最终汇总
|
||||
recordFinalSummary(recordDir, firstScanTime, endTime, totalRunTime, totalDifferences);
|
||||
|
||||
// 发送最终通知(分段发送)
|
||||
// -------------------------- 替换3:noRecord模式最终通知(分段发送)--------------------------
|
||||
let finalMsg = `材料收集完成(noRecord模式)\n总耗时:${totalRunTime.toFixed(1)}秒\n累计获取:\n`;
|
||||
for (const [n, d] of Object.entries(totalDifferences)) {
|
||||
finalMsg += ` ${n}: ${d}个\n`;
|
||||
}
|
||||
sendNotificationInChunks(finalMsg, notification.Send);
|
||||
}
|
||||
await sleep(1); // 夹断
|
||||
} catch (error) {
|
||||
log.error(`操作失败: ${error}`);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "背包统计采集系统",
|
||||
"version": "2.41",
|
||||
"version": "2.42",
|
||||
"bgi_version": "0.44.8",
|
||||
"description": "默认四行为一页;模板匹配材料,OCR识别数量。\n数字太小可能无法识别,用?代替。\n目前支持采集材料的数量统计+路径CD管理。",
|
||||
"description": "模板匹配材料,OCR识别数量;\n支持背包材料的数量统计+路径CD管理,自动优选路径;\n具体支持看材料CD文件,可自行增减材料CD。",
|
||||
"authors": [
|
||||
{
|
||||
"name": "吉吉喵",
|
||||
"links": "https://github.com/JJMdzh"
|
||||
"name": "吉吉喵"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
|
||||
2
repo/js/背包材料统计/materialsCD/掉落CD.txt
Normal file
2
repo/js/背包材料统计/materialsCD/掉落CD.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
4点:偏光棱镜,地脉的旧枝,大英雄的经验,特工祭刀,冒险家的经验,水晶棱镜,混沌炉心,猎兵祭刀,流浪者的经验,混沌回路,石化的骨片,黯淡棱镜,混沌装置,结实的骨片,隐兽鬼爪,黑晶号角,脆弱的骨片,隐兽利爪,雾虚灯芯,黑铜号角,沉重号角,混沌真眼,隐兽指爪,雾虚草囊,地脉的新芽,幽邃刻像,混沌枢纽,雾虚花粉,地脉的枯叶,夤夜刻像,混沌机关,督察长祭刀,初生的浊水幻灵,晦暗刻像,混浊棱晶,老旧的役人怀表,浊水的一掬,渊光鳍翅,破缺棱晶,茁壮菌核,休眠菌核,月色鳍翅,浊水的一滴,锲纹的横脊,失活菌核,密固的横脊,异界生命核,羽状鳍翅,外世突触,未熄的剑柄,残毁的横脊,混沌锚栓,混沌模块,漫游者的盛放之花,裂断的剑柄,隙间之核,何人所珍藏之花,役人的时时刻刻,残毁的剑柄,混沌容器,役人的制式怀表,意志巡游的符像,来自何处的待放之花,辉光棱晶,史莱姆凝液,意志明晰的寄偶,繁光躯外骸,迷光的蜷叶之心,不祥的面具,惑光的阔叶,意志破碎的残片,稀光遗骼,失光块骨,折光的胚芽,污秽的面具,聚燃的游像眼,幽雾兜盔,破损的面具,聚燃的命种,明燃的棱状壳,蕴热的背壳,冷裂壳块,幽雾片甲,禁咒绘卷,聚燃的石块,封魔绘卷,幽雾化形,秘源真芯,霜夜的煌荣,史莱姆原浆,导能绘卷,秘源机鞘,霜夜的柔辉,历战的箭簇,史莱姆清,秘源轴,霜夜的残照,原素花蜜,异海之块,浮游干核,锐利的箭簇,孢囊晶尘,异海凝珠,微光花蜜,牢固的箭簇,奇械机芯齿轮,尉官的徽记,荧光孢粉,骗骗花蜜,名刀镡,士官的徽记,机关正齿轮,蕈兽孢子,啮合齿轮,影打刀镡,新兵的徽记,织金红绸,攫金鸦印,横行霸者的利齿,破旧的刀镡,镶边红绸,浮游晶化核,老练的坚齿,藏银鸦印,褪色红绸,寻宝鸦印,异色结晶石,浮游幽核,稚嫩的尖齿,磨损的执凭,龙冠武士的金哨,战士的铁哨,卫从的木哨,精制机轴,加固机轴,毁损机轴,霜镌的执凭,精致的执凭
|
||||
@@ -1,20 +1,19 @@
|
||||
12小时:晶蝶,野猪,松鼠,狐狸,鼬,鸟,鱼,鸭子,路边闪光点,兽肉,禽肉,神秘的肉,鱼肉,鳗肉,螃蟹,青蛙,发光髓,蜥蜴尾巴,晶核,鳅鳅宝玉,燃素蜜虫,固晶甲虫
|
||||
12小时:晶蝶,野猪,松鼠,狐狸,鼬,鸟,鱼,鸭子,路边闪光点,兽肉,禽肉,神秘的肉,鱼肉,鳗肉,螃蟹,青蛙,发光髓,蜥蜴尾巴,晶核,鳅鳅宝玉,吉光虫,燃素蜜虫,固晶甲虫,月萤虫,
|
||||
|
||||
24小时:沉玉仙茗,狗粮
|
||||
24小时:沉玉仙茗,狗粮,
|
||||
|
||||
46小时:小灯草,嘟嘟莲,落落莓,塞西莉亚花,慕风蘑菇,蒲公英籽,钩钩果,风车菊,霓裳花,清心,琉璃袋,琉璃百合,夜泊石,绝云椒椒,星螺,石珀,清水玉,海灵芝,鬼兜虫,绯樱绣球,鸣草,珊瑚真珠,晶化骨髓,血斛,天云草实,幽灯蕈,沙脂蛹,月莲,帕蒂沙兰,树王圣体菇,圣金虫,万相石,悼灵花,劫波莲,赤念果,苍晶螺,海露花,柔灯铃,子探测单元,湖光铃兰,幽光星星,虹彩蔷薇,初露之源,浪沫羽鳃,灼灼彩菊,肉龙掌,青蜜莓,枯叶紫英,微光角菌,云岩裂叶,琉鳞石,奇异的「牙齿」,冰雾花花朵,烈焰花花蕊,便携轴承,霜盏花,月落银
|
||||
46小时:小灯草,嘟嘟莲,落落莓,塞西莉亚花,慕风蘑菇,蒲公英籽,钩钩果,风车菊,霓裳花,清心,琉璃袋,琉璃百合,夜泊石,绝云椒椒,星螺,石珀,清水玉,海灵芝,鬼兜虫,绯樱绣球,鸣草,珊瑚真珠,晶化骨髓,血斛,天云草实,幽灯蕈,沙脂蛹,月莲,帕蒂沙兰,树王圣体菇,圣金虫,万相石,悼灵花,劫波莲,赤念果,苍晶螺,海露花,柔灯铃,子探测单元,湖光铃兰,幽光星星,虹彩蔷薇,初露之源,浪沫羽鳃,灼灼彩菊,肉龙掌,青蜜莓,枯叶紫英,微光角菌,云岩裂叶,琉鳞石,奇异的「牙齿」,冰雾花花朵,烈焰花花蕊,便携轴承,霜盏花,月落银,
|
||||
|
||||
72小时:
|
||||
|
||||
1次0点:铁块,甜甜花,胡萝卜,蘑菇,松茸,松果,金鱼草,莲蓬,薄荷,鸟蛋,树莓,白萝卜,苹果,日落果,竹笋,海草,堇瓜,星蕈,墩墩桃,须弥蔷薇,香辛果,枣椰,泡泡桔,汐藻,茉洁草,久雨莲,颗粒果,烛伞蘑菇,澄晶实,红果果菇,苦种,烬芯花,夏槲果,白灵果,寒涌石,宿影花
|
||||
1次0点:铁块,甜甜花,胡萝卜,蘑菇,松茸,松果,金鱼草,莲蓬,薄荷,鸟蛋,树莓,白萝卜,苹果,日落果,竹笋,海草,堇瓜,星蕈,墩墩桃,须弥蔷薇,香辛果,枣椰,泡泡桔,汐藻,茉洁草,久雨莲,颗粒果,烛伞蘑菇,澄晶实,红果果菇,苦种,烬芯花,夏槲果,白灵果,寒涌石,宿影花,
|
||||
|
||||
2次0点:白铁块,星银矿石
|
||||
2次0点:白铁块,星银矿石,
|
||||
|
||||
3次0点:水晶块,紫晶块,萃凝晶,魔晶块,钓鱼点,虹滴晶
|
||||
3次0点:水晶块,紫晶块,萃凝晶,魔晶块,钓鱼点,虹滴晶,
|
||||
|
||||
4点:精英怪物,吉光虫,盐,胡椒,洋葱,牛奶,番茄,卷心菜,土豆,小麦,稻米,虾仁,豆腐,杏仁,发酵果实汁,咖啡豆,秃秃豆,面粉,奶油,熏禽肉,黄油,火腿,糖,香辛料,蟹黄,果酱,奶酪,培根,香肠,「冷鲜肉」,黑麦,酸奶油,黑麦粉
|
||||
4点:盐,胡椒,洋葱,牛奶,番茄,卷心菜,土豆,小麦,稻米,虾仁,豆腐,杏仁,发酵果实汁,咖啡豆,秃秃豆,面粉,奶油,熏禽肉,黄油,火腿,糖,香辛料,蟹黄,果酱,奶酪,培根,香肠,「冷鲜肉」,黑麦,黑麦粉,酸奶油,
|
||||
|
||||
6点:
|
||||
|
||||
即时刷新:蝴蝶,萤火虫,蝴蝶的翅膀,发光髓
|
||||
|
||||
|
||||
@@ -1,97 +1,103 @@
|
||||
[
|
||||
{
|
||||
"name": "TargetCount",
|
||||
"type": "input-text",
|
||||
"label": "js目录下默认扫描的文件结构:\n./📁BetterGI/📁User/📁JsScript/\n📁背包材料统计/\n 📁pathing/\n 📁 薄荷/\n 📄 薄荷1.json\n 📁 薄荷效率/\n 📄 薄荷-吉吉喵.json\n 📁 苹果/\n 📄 旅行者的果园.json\n----------------------------------\n目标数量,默认5000\n给📁pathing下材料设定的目标数"
|
||||
},
|
||||
{
|
||||
"name": "TargetresourceName",
|
||||
"type": "input-text",
|
||||
"label": "----------------------------------\n优先级材料,跳过目标数直接运行\n如填入 甜甜花,薄荷,苹果"
|
||||
},
|
||||
{
|
||||
"name": "TimeCost",
|
||||
"type": "input-text",
|
||||
"label": "====================\n时间成本:秒\n一单位材料的平均耗时,默认30"
|
||||
},
|
||||
{
|
||||
"name": "notify",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n是否发送通知。默认:否\n需在BGI开启JS通知,并设置通知地址"
|
||||
},
|
||||
{
|
||||
"name": "Pathing",
|
||||
"type": "select",
|
||||
"label": "====================\n扫描📁pathing下的\n或勾选【材料分类】的材料。默认:兼并",
|
||||
"options": [
|
||||
"1.兼并:📁pathing材料+【材料分类】",
|
||||
"2.仅📁pathing材料",
|
||||
"3.仅【材料分类】勾选"
|
||||
{
|
||||
"name": "TargetCount",
|
||||
"type": "input-text",
|
||||
"label": "js目录下默认扫描的文件结构:\n./📁BetterGI/📁User/📁JsScript/\n📁背包材料统计/\n 📁pathing/\n 📁 薄荷/\n 📄 薄荷1.json\n 📁 薄荷效率/\n 📄 薄荷-吉吉喵.json\n 📁 名刀镡/\n 📄 旅行者的名刀镡.json\n----------------------------------\n目标数量,默认5000\n给📁pathing下材料设定的目标数"
|
||||
},
|
||||
{
|
||||
"name": "TargetresourceName",
|
||||
"type": "input-text",
|
||||
"label": "----------------------------------\n优先级材料,跳过目标数直接运行\n如填入 甜甜花,薄荷,苹果"
|
||||
},
|
||||
{
|
||||
"name": "TimeCost",
|
||||
"type": "input-text",
|
||||
"label": "====================\n时间成本:秒\n一单位材料的平均耗时,默认30"
|
||||
},
|
||||
{
|
||||
"name": "notify",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n是否发送通知。默认:否\n需在BGI开启JS通知,并设置通知地址"
|
||||
},
|
||||
{
|
||||
"name": "noRecord",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n取消扫描数量。默认:否\n勾选将不进行单路径的扫描,但保留时间记录\n(推荐路径记录炼成后启用)"
|
||||
},
|
||||
{
|
||||
"name": "Pathing",
|
||||
"type": "select",
|
||||
"label": "====================\n扫描📁pathing下的\n或勾选【材料分类】的材料。默认:兼并",
|
||||
"options": [
|
||||
"1.兼并:📁pathing材料+【材料分类】",
|
||||
"2.仅📁pathing材料",
|
||||
"3.仅【材料分类】勾选",
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Smithing",
|
||||
"type": "checkbox",
|
||||
"label": "\n----------------------------------\n【锻造素材】"
|
||||
},
|
||||
{
|
||||
"name": "Drops",
|
||||
"type": "checkbox",
|
||||
"label": "如:矿石、原胚\n----------------------------------\n【怪物掉落素材】"
|
||||
},
|
||||
{
|
||||
"name": "ForagedFood",
|
||||
"type": "checkbox",
|
||||
"label": "如:经验书、怪物掉落\n----------------------------------\n【采集食物】,食用回血"
|
||||
},
|
||||
{
|
||||
"name": "General",
|
||||
"type": "checkbox",
|
||||
"label": "如:苹果、日落果、泡泡桔\n----------------------------------\n【一般素材】"
|
||||
},
|
||||
{
|
||||
"name": "CookingIngs",
|
||||
"type": "checkbox",
|
||||
"label": "如:特产、非食用素材\n----------------------------------\n\n【烹饪用食材】"
|
||||
},
|
||||
{
|
||||
"name": "Weekly",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【周本素材】"
|
||||
},
|
||||
{
|
||||
"name": "Wood",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【木材】"
|
||||
},
|
||||
{
|
||||
"name": "CharAscension",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【角色突破素材】"
|
||||
},
|
||||
{
|
||||
"name": "Fishing",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【鱼饵、鱼类】"
|
||||
},
|
||||
{
|
||||
"name": "Gems",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【宝石】"
|
||||
},
|
||||
{
|
||||
"name": "Talent",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【角色天赋素材】"
|
||||
},
|
||||
{
|
||||
"name": "WeaponAscension",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【武器突破素材】"
|
||||
},
|
||||
{
|
||||
"name": "ImageDelay",
|
||||
"type": "input-text",
|
||||
"label": "数字太小可能无法识别,用?代替\n====================\n识图延迟时间(默认:10 毫秒)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Smithing",
|
||||
"type": "checkbox",
|
||||
"label": "刷怪、采集推荐:2.仅📁pathing材料\n----------------------------------\n【锻造素材】"
|
||||
},
|
||||
{
|
||||
"name": "Drops",
|
||||
"type": "checkbox",
|
||||
"label": "如:矿石、原胚\n----------------------------------\n【怪物掉落素材】"
|
||||
},
|
||||
{
|
||||
"name": "ForagedFood",
|
||||
"type": "checkbox",
|
||||
"label": "如:经验书、怪物掉落\n----------------------------------\n【采集食物】,食用回血"
|
||||
},
|
||||
{
|
||||
"name": "General",
|
||||
"type": "checkbox",
|
||||
"label": "如:苹果、日落果、泡泡桔\n----------------------------------\n【一般素材】"
|
||||
},
|
||||
{
|
||||
"name": "CookingIngs",
|
||||
"type": "checkbox",
|
||||
"label": "如:特产、非食用素材\n----------------------------------\n\n【烹饪用食材】"
|
||||
},
|
||||
{
|
||||
"name": "Weekly",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【周本素材】"
|
||||
},
|
||||
{
|
||||
"name": "Wood",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【木材】"
|
||||
},
|
||||
{
|
||||
"name": "CharAscension",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【角色突破素材】"
|
||||
},
|
||||
{
|
||||
"name": "Fishing",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【鱼饵、鱼类】"
|
||||
},
|
||||
{
|
||||
"name": "Gems",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【宝石】"
|
||||
},
|
||||
{
|
||||
"name": "Talent",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【角色天赋素材】"
|
||||
},
|
||||
{
|
||||
"name": "WeaponAscension",
|
||||
"type": "checkbox",
|
||||
"label": "----------------------------------\n\n【武器突破素材】"
|
||||
},
|
||||
{
|
||||
"name": "ImageDelay",
|
||||
"type": "input-text",
|
||||
"label": "数字太小可能无法识别,用?代替\n====================\n识图延迟时间(默认:10 毫秒)"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user