诸多优化修复:!!!!!!注意UI修改了,配置组里需要删除并重新添加该js!!!!!! (#2878)

* 诸多优化修复:!!!!!!注意UI修改了,配置组里需要删除并重新添加该js!!!!!!

v2.61   背包材料翻页机制加速;修复手动终止路径noRecord模式的额外条件判断不生效;全局图片缓存;识别名单、CD文件和弹窗升级多选框UI;
!!!!!!注意UI修改了,配置组里需要删除并重新添加该js!!!!!!

* Add files via upload

* Update manifest.json

* Add files via upload

* Add files via upload

* Update settings.json

* Add files via upload

* fix typo.

---------

Co-authored-by: 起个名字好难的喵 <25520958+MisakaAldrich@users.noreply.github.com>
This commit is contained in:
吉吉喵
2026-02-16 13:04:14 +08:00
committed by GitHub
parent 51dcda9d0f
commit d9da47f362
9 changed files with 1358 additions and 868 deletions

View File

@@ -1,4 +1,4 @@
# 背包材料统计 v2.60
# 背包材料统计 v2.61
作者:吉吉喵
<!-- 新增:全局图片样式,控制连续图片同行显示 -->
@@ -41,6 +41,8 @@
3. **独立名单识别**不与路边NPC、神像交互可自定义识别名单操作见「四、问题解答Q4」
4. **实时弹窗保护**:内置弹窗模块(覆盖路边信件、过期物品、月卡、调查等场景),运行时全程保护路径不被弹窗干扰。
5. **自动黑名单**:内置拾取模块,联动材料统计,可识别爆满的路径材料,自动屏蔽。
6. **路径检测码**:根据路径生成检测码,可识别改名单坐标未变的路径文件,自动匹配带检测码的路径记录。
7. **怪物材料防爆仓**对怪物材料路径添加多重机制防止蓝紫怪物材料爆仓怪物材料路径执行时不会拾其他的怪物材料除非pathing文件夹里存在且未超量
## 二、用前须知
@@ -116,11 +118,11 @@
| 4. 发送通知 | ① 每类材料跑完通知一次;② 全部材料跑完汇总通知一次需开启BGI通知 | 建议开启,方便实时了解进度(接收端如企业微信需自行配置) |
| 5. 取消扫描 | 取消“每个路径执行后”的背包扫描,仅保留“全部执行前/后”2次扫描 | 有效路径记录达3条以上时可以开启可节约运行时间 |
| 6. 仅 pathing 材料 | 仅扫描 `pathing` 文件夹内的材料,跳过其他分类,大幅缩短扫描时间 | 路径配置完成后开启,提升脚本运行效率 |
| 7. 弹窗名 | 不则默认循环执行 `assets\imageClick` 文件夹下所有弹窗;填写则仅执行指定弹窗 | 推荐默认,需单独适配某类弹窗时填写(例:月卡,复苏) |
| 8. 采用的 CD 分类 | 不则默认执行 `materialsCD` 文件夹内配置的CD分类填写则仅执行指定CD分类 | 新增材料时需在该文件夹同步配置CD规则操作见「四、问题解答Q2」 |
| 7. 弹窗名 | 不则默认循环执行 `assets\imageClick` 文件夹下所有弹窗;填写则仅执行指定弹窗 | 推荐默认,需单独适配某类弹窗时填写(例:月卡,复苏) |
| 8. 采用的 CD 分类 | 不则默认执行 `materialsCD` 文件夹内配置的CD分类填写则仅执行指定CD分类 | 新增材料时需在该文件夹同步配置CD规则操作见「四、问题解答Q2」 |
| 9. 终止时刻 | 不填则不执行定时终止路径无时间记录时会预判路径耗时5分钟且预留2分钟空闲 | 填写需要按24小时格式4:10 |
| 10. 采用的识别名单 | 不则默认执行 `targetText` 文件夹内配置的识别名单;填写则仅执行指定识别名单 | 新增名单时需符合配置规则操作见「四、问题解答Q4」 |
| 11. 超量阈值 | 首次扫描后,超量的路径材料,将从识别名单中剔除,默认5000 | 不推荐9999怪物材料有几千就够了采用默认数值可自动避免爆背包 |
| 10. 采用的识别名单 | 不则默认执行 `targetText` 文件夹内配置的识别名单;填写则仅执行指定识别名单 | 新增名单时需符合配置规则操作见「四、问题解答Q4」 |
| 11. 超量阈值 | 首次扫描后,超量的路径材料,将从识别名单中剔除,默认9000 | 不推荐9999怪物材料有几千就够了采用默认数值可自动避免爆背包 |
| 12. 拖动距离 | 解决非1080p分辨率下“划页过头”问题需调整到“一次划页≤4行” | 拖动点建议选“第五行材料附近”大于1080p屏可适当减小数值 |
@@ -130,7 +132,7 @@
3. **食物识别强制要求**:背包食物界面**第一行必须包含8种食物**(苹果、日落果、星蕈、活化的星蕈、枯焦的星蕈、泡泡桔、烛伞蘑菇、美味的宝石闪闪),缺少则这些食物无法识别;
4. **关键文件备份**:建议不定期备份 `pathing` 文件夹(路径文件)和 `pathing_record` 文件夹(路径运行记录),便于丢失后或被污染后,记录能恢复如初;
5. **OCR配置**默认最新调整识别名单时用的是V5Auto
6. **手动终止运行**如果要终止JS运行推荐在当前路径采集到当前材料前或者采集完进入背包扫描时终止会在扫描结束后终止以保护当前记录如果是【取消扫描】模式,不会储存当前记录的材料数目,随意。
6. **手动终止运行**手动终止路径会被记录成noRecord模式只参与CD计算如果是【取消扫描】模式,不会储存当前记录的材料数目,两种情况都随意。
## 五、问题解答
@@ -230,4 +232,5 @@ A记录文件夹位于 `BetterGI\User\JsScript\背包材料统计\` 下,各
| v2.57 | 补全圣遗物无CD管理bug |
| v2.58 | 优化背包扫图逻辑 |
| v2.59 | 修复自动拾取匹配bug改为双向匹配 |
| v2.60 | 手动终止路径会被记录成noRecord模式只参与CD计算增加当前路线预估时长日志升级多选框UI刚需bgi v0.55版本优化文件是否存在逻辑降级ReadTextSync报错 |
| v2.60 | 手动终止路径会被记录成noRecord模式只参与CD计算增加当前路线预估时长日志材料分类升级多选框UI刚需bgi v0.55版本优化文件是否存在逻辑降级ReadTextSync报错;检测码识别路径 |
| v2.61 | 背包材料识别机制加速修复手动终止路径noRecord模式的额外条件判断不生效全局图片缓存识别名单、CD文件和弹窗升级多选框UI注意UI修改了配置组里需要删除并重新添加该js |

View File

@@ -26,14 +26,34 @@ function parseCategoryContent(content) {
// 从 targetText 文件夹中读取分类信息无修改与OCR无关
function readtargetTextCategories(targetTextDir) {
const targetTextFilePaths = readAllFilePaths(targetTextDir, 0, 1);
const targetTextFilePaths = readAllFilePaths(targetTextDir, 0, 1, ['.txt']);
const materialCategories = {};
// 解析筛选名单
const pickTextNames = (settings.PickCategories || "")
.split(/[,,、 \s]+/).map(n => n.trim()).filter(n => n);
let pickTextNames = [];
try {
pickTextNames = Array.from(settings.PickCategories || []);
} catch (e) {
log.error(`获取PickCategories设置失败: ${e.message}`);
}
let availablePickCategories = [];
try {
availablePickCategories = targetTextFilePaths.map(filePath => basename(filePath).replace('.txt', ''));
log.info(`可用识别名单:${availablePickCategories.join(', ')}`);
} catch (e) {
log.error(`扫描识别名单目录失败: ${e.message}`);
}
if (pickTextNames.length === 0) {
log.info("未指定识别名单,将加载所有文件");
} else {
const invalidCategories = pickTextNames.filter(name => !availablePickCategories.includes(name));
if (invalidCategories.length > 0) {
log.warn(`以下识别名单不存在,将被忽略:${invalidCategories.join(', ')}`);
pickTextNames = pickTextNames.filter(name => availablePickCategories.includes(name));
}
}
// 兜底日志确认pickTextNames是否为空方便排查
log.info(`筛选名单状态:${pickTextNames.length === 0 ? '未指定(空),将加载所有文件' : '指定了:' + pickTextNames.join(',')}`);
for (const filePath of targetTextFilePaths) {
@@ -92,36 +112,38 @@ const ScrollRo = RecognitionObject.TemplateMatch(
/**
* 对齐并交互目标核心改造适配最新版performOcr
* @param {string[]} targetTexts - 待匹配的目标文本列表
* @param {string[]|Function} targetTextsOrFunc - 待匹配的目标文本列表或函数
* @param {Object} fDialogueRo - F图标的识别对象
* @param {Object} textxRange - 文本识别的X轴范围 { min: number, max: number }
* @param {number} texttolerance - 文本与F图标Y轴对齐的容差
* @param {Object} cachedFrame - 缓存的图像帧(可选)
*/
async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, texttolerance, cachedFrame = null) {
async function alignAndInteractTarget(targetTextsOrFunc, fDialogueRo, textxRange, texttolerance, cachedFrame = null) {
let lastLogTime = Date.now();
const recognitionCount = new Map(); // 避免误触:文本+Y坐标 → 计数
const ocrScreenshots = []; // 收集最新版performOcr返回的截图统一释放
const recognitionCount = new Map();
const ocrScreenshots = [];
try {
while (!state.completed && !state.cancelRequested) {
recognitionCount.clear(); // 每次循环开始时清空计数
if (state.ocrPaused) {
await sleep(100);
continue;
}
recognitionCount.clear();
const currentTime = Date.now();
// 每10秒输出检测日志保留原逻辑
if (currentTime - lastLogTime >= 10000) {
log.info("独立OCR识别中...");
lastLogTime = currentTime;
}
await sleep(50);
// 1. 释放上一帧缓存,捕获新帧(保留原逻辑)
if (cachedFrame) {
if (cachedFrame.Dispose) cachedFrame.Dispose();
else if (cachedFrame.dispose) cachedFrame.dispose();
}
cachedFrame = captureGameRegion();
// 2. 识别F图标/Scroll.png保留原逻辑
let fRes = await findFIcon(fDialogueRo, 10, cachedFrame);
if (!fRes) {
const scrollRes = await findFIcon(ScrollRo, 10, cachedFrame);
@@ -132,21 +154,19 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text
continue;
}
// 3. 核心改造调用最新版performOcr
// 适配点1参数顺序调整为「targetTexts, xRange, yRange, ra, timeout, interval」
// 适配点2接收返回的「results+screenshot」并收集screenshot
const yRange = { min: fRes.y - 3, max: fRes.y + 37 }; // 原Y轴范围不变
const { results: ocrResults, screenshot: ocrScreenshot } = await performOcr(
targetTexts, // 目标文本列表(原逻辑)
textxRange, // 文本X轴范围原逻辑
yRange, // 文本Y轴范围原逻辑
cachedFrame, // 初始截图最新版第4个参数为ra
10, // 超时时间保留原10ms
5 // 重试间隔保留原5ms
);
ocrScreenshots.push(ocrScreenshot); // 收集截图,避免内存泄漏
const targetTexts = typeof targetTextsOrFunc === 'function' ? targetTextsOrFunc() : targetTextsOrFunc;
const yRange = { min: fRes.y - 3, max: fRes.y + 37 };
const { results: ocrResults, screenshot: ocrScreenshot } = await performOcr(
targetTexts,
textxRange,
yRange,
cachedFrame,
10,
5
);
ocrScreenshots.push(ocrScreenshot);
// 4. 文本匹配与交互(双向匹配,增强容错性)
let foundTarget = false;
for (const targetText of targetTexts) {
const targetResult = ocrResults.find(res =>
@@ -154,11 +174,9 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text
);
if (!targetResult) continue;
// 计数防误触
const materialId = `${targetText}-${targetResult.y}`;
recognitionCount.set(materialId, (recognitionCount.get(materialId) || 0) + 1);
// Y轴对齐判断
const centerYTargetText = targetResult.y + targetResult.height / 2;
if (Math.abs(centerYTargetText - (fRes.y + fRes.height / 2)) <= texttolerance) {
if (recognitionCount.get(materialId) >= 1) {
@@ -171,7 +189,6 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text
}
}
// 5. 未找到目标则翻滚(保留原逻辑)
if (!foundTarget) {
await keyMouseScript.runFile(`assets/滚轮下翻.json`);
}
@@ -179,20 +196,16 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text
} catch (error) {
log.error(`对齐交互异常: ${error.message}`);
} finally {
// 6. 统一释放所有资源(新增:解决内存泄漏)
// 释放缓存帧
if (cachedFrame) {
if (cachedFrame.Dispose) cachedFrame.Dispose();
else if (cachedFrame.dispose) cachedFrame.dispose();
}
// 释放OCR截图
for (const screenshot of ocrScreenshots) {
if (screenshot) {
if (screenshot.Dispose) screenshot.Dispose();
else if (screenshot.dispose) screenshot.dispose();
}
}
// 任务状态日志(保留原逻辑)
if (state.cancelRequested) {
log.info("检测任务已取消");
} else if (!state.completed) {

View File

@@ -5,6 +5,9 @@ const holdY = Math.min(1080, Math.max(0, Math.floor(Number(settings.HoldY) || 75
const totalPageDistance = Math.max(10, Math.floor(Number(settings.PageScrollDistance) || 711));
const imageDelay = Math.min(1000, Math.max(0, Math.floor(Number(settings.ImageDelay) || 0))); // 识图基准时长 await sleep(imageDelay);
// 全局图片缓存(避免重复加载)
const globalMaterialImageCache = {};
// 配置参数
const pageScrollCount = 22; // 最多滑页次数
@@ -127,7 +130,22 @@ async function scrollPage(totalDistance, stepDistance = 10, delayMs = 5) {
stepDistance,
stepInterval: delayMs,
waitBefore: 50,
waitAfter: 700, // 原逻辑中松开后等待700ms
waitAfter: 500, // 原逻辑中松开后等待700ms
repeat: 1
});
}
// 回退翻页函数(用于后续优先级材料扫描)
async function scrollBackPage(totalDistance, stepDistance = 10, delayMs = 5) {
const backHoldY = 1080 - holdY; // 回退翻页的Y值
await mouseDrag({
holdMouseX: holdX, // 固定起点X
holdMouseY: backHoldY, // 回退翻页的Y值
totalDistance: totalDistance, // 向下滑动(正值)
stepDistance,
stepInterval: delayMs,
waitBefore: 50,
waitAfter: 500,
repeat: 1
});
}
@@ -281,49 +299,47 @@ async function mouseDrag({
}
function filterMaterialsByPriority(materialsCategory) {
// 获取当前材料分类的优先级
const currentPriority = materialPriority[materialsCategory];
if (currentPriority === undefined) {
throw new Error(`Invalid materialsCategory: ${materialsCategory}`);
}
// 获取当前材料分类的 materialTypeMap 对应值
const currentType = materialTypeMap[materialsCategory];
if (currentType === undefined) {
throw new Error(`Invalid materialTypeMap for: ${materialsCategory}`);
}
// 获取所有优先级更低的材料分类(后位材料)
const backPriorityMaterials = Object.keys(materialPriority)
.filter(mat => materialPriority[mat] > currentPriority && materialTypeMap[mat] === currentType);
const allSameTypeMaterials = Object.keys(materialPriority)
.filter(mat => materialTypeMap[mat] === currentType);
// 合并当前和后位材料分类(只包含同位和后位,不包含前位)
// 只有同位或后位材料才会触发全列扫描
const finalFilteredMaterials = [...backPriorityMaterials, materialsCategory];
const finalFilteredMaterials = allSameTypeMaterials.sort((a, b) => materialPriority[a] - materialPriority[b]);
return finalFilteredMaterials
}
// 扫描材料
async function scanMaterials(materialsCategory, materialCategoryMap) {
// 材料图片缓存
const materialImages = {}; // 用于缓存加载的图片
const priorityMaterialImages = {}; // 用于缓存优先级材料图片
async function scanMaterials(materialsCategory, materialCategoryMap, isPostPriority = false) {
// 使用全局图片缓存
const materialImages = globalMaterialImageCache;
const currentType = materialTypeMap[materialsCategory];
const currentPriority = materialPriority[materialsCategory];
// 获取当前+后位材料名单(仅包含同位和后位,不包含前位)
const priorityMaterialNames = [];
const finalFilteredMaterials = await filterMaterialsByPriority(materialsCategory);
for (const category of finalFilteredMaterials) {
const currentTypeMaterials = finalFilteredMaterials.filter(category => materialTypeMap[category] === currentType);
for (const category of currentTypeMaterials) {
const materialIconDir = `assets/images/${category}`;
const materialIconFilePaths = file.ReadPathSync(materialIconDir);
for (const filePath of materialIconFilePaths) {
const name = basename(filePath).replace(".png", ""); // 去掉文件扩展名
const name = basename(filePath).replace(".png", "");
priorityMaterialNames.push({ category, name });
// 预加载优先级材料图片
if (!priorityMaterialImages[name]) {
const mat = file.readImageMatSync(filePath);
if (!materialImages[name]) {
const mat = getCachedImageMat(filePath);
if (!mat.empty()) {
priorityMaterialImages[name] = mat;
materialImages[name] = mat;
}
}
}
@@ -352,7 +368,7 @@ async function scanMaterials(materialsCategory, materialCategoryMap) {
continue;
}
const mat = file.readImageMatSync(filePath);
const mat = getCachedImageMat(filePath);
if (mat.empty()) {
log.error(`加载图标失败:${filePath}`);
continue; // 跳过当前文件
@@ -413,33 +429,119 @@ async function scanMaterials(materialsCategory, materialCategoryMap) {
tempPhrases.sort(() => Math.random() - 0.5); // 打乱数组顺序,确保随机性
let phrasesStartTime = Date.now();
let previousScreenshot = null; // 用于存储上一次翻页前的截图
// 后续优先级材料扫描:滑条重置后先检查第八列是否有前位材料
if (isPostPriority) {
log.info(`后续优先级材料扫描 - 检查第八列前位材料`);
const ra = captureGameRegion();
const lowerPriorityMaterials = currentTypeMaterials.filter(category => materialPriority[category] < currentPriority);
log.info(`检查前位材料分类: ${lowerPriorityMaterials.map(c => `${c}(优先级${materialPriority[c]})`).join(', ')}`);
const lowerPriorityMatches = [];
for (const category of lowerPriorityMaterials) {
const categoryMaterials = priorityMaterialNames
.filter(({ name, category: cat }) => cat === category)
.map(({ name }) => {
const mat = materialImages[name];
return mat ? { name, mat } : null;
})
.filter(Boolean);
if (categoryMaterials.length > 0) {
const matchResults = await parallelTemplateMatch(ra, categoryMaterials, 1142, startY, columnWidth, columnHeight, 0.8);
const foundMaterials = matchResults.filter(r => r.result).map(r => r.name);
if (foundMaterials.length > 0) {
log.info(`第八列识别到前位材料 [${category}]: ${foundMaterials.join(', ')}`);
}
lowerPriorityMatches.push(...matchResults.filter(r => r.result));
}
}
log.info(`第八列前位材料总数: ${lowerPriorityMatches.length}`);
if (lowerPriorityMatches.length === 0) {
log.info(`未发现前位材料,回退一页`);
await scrollBackPage(totalPageDistance, 10, 5);
await sleep(500);
} else {
log.info(`发现前位材料,照常继续扫描`);
}
}
// 扫描背包中的材料
for (let scroll = 0; scroll <= pageScrollCount; scroll++) {
const ra = captureGameRegion();
// 重置foundPriorityMaterial标志每次翻页都重新检查
foundPriorityMaterial = false;
// 检查第八列是否有目标材料
// priorityMaterialNames只包含当前和后位材料
const priorityMaterialsToMatch = priorityMaterialNames
.filter(({ name }) => !recognizedMaterials.has(name))
.map(({ name }) => {
const mat = priorityMaterialImages[name];
return mat ? { name, mat } : null;
})
.filter(Boolean);
const finalFilteredMaterials = await filterMaterialsByPriority(materialsCategory);
if (priorityMaterialsToMatch.length > 0) {
const matchResults = await parallelTemplateMatch(ra, priorityMaterialsToMatch, 1142, startY, columnWidth, columnHeight, 0.8);
const currentTypeMaterials = finalFilteredMaterials.filter(category => materialTypeMap[category] === currentType);
for (const { name, result } of matchResults) {
if (result) {
foundPriorityMaterial = true; // 标记找到目标材料
// log.info(`发现目标材料: ${name},开始全列扫描`);
break;
log.info(`第八列扫描 - 当前分类: ${materialsCategory}, 优先级: ${currentPriority}`);
if (currentPriority <= 2) {
const lowerPriorityMaterials = currentTypeMaterials.filter(category => materialPriority[category] < currentPriority);
log.info(`检查前位材料分类: ${lowerPriorityMaterials.map(c => `${c}(优先级${materialPriority[c]})`).join(', ')}`);
const lowerPriorityMatches = [];
for (const category of lowerPriorityMaterials) {
const categoryMaterials = priorityMaterialNames
.filter(({ name, category: cat }) => cat === category && !recognizedMaterials.has(name))
.map(({ name }) => {
const mat = materialImages[name];
return mat ? { name, mat } : null;
})
.filter(Boolean);
if (categoryMaterials.length > 0) {
const matchResults = await parallelTemplateMatch(ra, categoryMaterials, 1142, startY, columnWidth, columnHeight, 0.8);
const foundMaterials = matchResults.filter(r => r.result).map(r => r.name);
if (foundMaterials.length > 0) {
log.info(`第八列识别到前位材料 [${category}]: ${foundMaterials.join(', ')}`);
}
lowerPriorityMatches.push(...matchResults.filter(r => r.result));
}
}
log.info(`第八列前位材料总数: ${lowerPriorityMatches.length}`);
if (lowerPriorityMatches.length < 4) {
log.info(`前位材料少于4张触发全列扫描`);
foundPriorityMaterial = true;
} else {
log.info(`4张都是前位材料继续翻页`);
}
} else {
const currentOrHigherPriorityMaterials = currentTypeMaterials.filter(category => materialPriority[category] >= currentPriority);
log.info(`检查同位/后位材料分类: ${currentOrHigherPriorityMaterials.map(c => `${c}(优先级${materialPriority[c]})`).join(', ')}`);
const currentOrHigherMatches = [];
for (const category of currentOrHigherPriorityMaterials) {
const categoryMaterials = priorityMaterialNames
.filter(({ name, category: cat }) => cat === category && !recognizedMaterials.has(name))
.map(({ name }) => {
const mat = materialImages[name];
return mat ? { name, mat } : null;
})
.filter(Boolean);
if (categoryMaterials.length > 0) {
const matchResults = await parallelTemplateMatch(ra, categoryMaterials, 1142, startY, columnWidth, columnHeight, 0.8);
const foundMaterials = matchResults.filter(r => r.result).map(r => r.name);
if (foundMaterials.length > 0) {
log.info(`第八列识别到同位/后位材料 [${category}]: ${foundMaterials.join(', ')}`);
}
currentOrHigherMatches.push(...matchResults.filter(r => r.result));
}
}
log.info(`第八列同位/后位材料总数: ${currentOrHigherMatches.length}`);
if (currentOrHigherMatches.length > 0) {
log.info(`发现同位/后位材料,触发全列扫描`);
foundPriorityMaterial = true;
} else {
log.info(`未发现同位/后位材料,继续翻页`);
}
}
// 只有发现目标材料时,才执行全列扫描
@@ -558,7 +660,7 @@ async function scanMaterials(materialsCategory, materialCategoryMap) {
}
// 滑动到下一页
if (scroll < pageScrollCount) {
if (scroll < pageScrollCount && !shouldEndScan) {
if (useScreenComparison && previousScreenshot) {
// 使用模板匹配比较两次翻页前的截图(兜底机制)
const matchRo = RecognitionObject.TemplateMatch(
@@ -733,219 +835,232 @@ function dynamicMaterialGrouping(materialCategoryMap) {
// 主逻辑函数
async function MaterialPath(materialCategoryMap, cachedFrame = null) {
// 1. 先记录原始名称与别名的映射关系(用于最后反向转换)
const nameMap = new Map();
Object.values(materialCategoryMap).flat().forEach(originalName => {
const aliasName = MATERIAL_ALIAS[originalName] || originalName;
nameMap.set(aliasName, originalName); // 存储:别名→原始名
});
// 2. 转换materialCategoryMap为别名用于内部处理
const processedMap = {};
Object.entries(materialCategoryMap).forEach(([category, names]) => {
processedMap[category] = names.map(name => MATERIAL_ALIAS[name] || name);
});
materialCategoryMap = processedMap;
const maxStage = 4; // 最大阶段数
let stage = 0; // 当前阶段
let currentGroupIndex = 0; // 当前处理的分组索引
let currentCategoryIndex = 0; // 当前处理的分类索引
let materialsCategory = ""; // 当前处理的材料分类名称
const allLowCountMaterials = []; // 用于存储所有识别到的低数量材料信息
// 添加状态变量,记录上一个分类的信息
let prevCategory = null;
let prevCategoryObject = null;
let prevPriority = null;
let prevGroup = null;
let skipSliderReset = false; // 是否跳过滑条重置
const sortedGroups = dynamicMaterialGrouping(materialCategoryMap);
// log.info("材料 动态[分组]结果:");
sortedGroups.forEach(group => {
log.info(`类型 ${group.type} | 包含分类: ${group.categories.join(', ')}`);
});
let loopCount = 0;
const maxLoopCount = 200; // 合理阈值正常流程约50-100次循环
while (stage <= maxStage && loopCount <= maxLoopCount) { // ===== 补充优化:加入循环次数限制 =====
loopCount++;
switch (stage) {
case 0: // 返回主界面
log.info("返回主界面");
await genshin.returnMainUi();
await sleep(500);
stage = 1; // 进入下一阶段
break;
case 1: // 打开背包界面
// log.info("打开背包界面");
keyPress("B"); // 打开背包界面
await sleep(800); // 减少等待时间
cachedFrame?.dispose();
cachedFrame = captureGameRegion();
const backpackResult = await recognizeImage(BagpackRo, cachedFrame, 2000);
if (backpackResult.isDetected) {
// log.info("成功识别背包图标");
stage = 2; // 进入下一阶段
} else {
log.warn("未识别到背包图标,重新尝试");
// ===== 补充优化:连续回退时释放资源 =====
cachedFrame?.dispose();
stage = 0; // 回退
}
break;
case 2: // 按分组处理材料分类
if (currentGroupIndex < sortedGroups.length) {
const group = sortedGroups[currentGroupIndex];
if (currentCategoryIndex < group.categories.length) {
materialsCategory = group.categories[currentCategoryIndex];
const offset = materialTypeMap[materialsCategory];
const menuClickX = Math.round(575 + (offset - 1) * 96.25);
// log.info(`点击坐标 (${menuClickX},75)`);
click(menuClickX, 75);
await sleep(500);
cachedFrame?.dispose();
cachedFrame = captureGameRegion();
stage = 3; // 进入下一阶段
} else {
currentGroupIndex++;
currentCategoryIndex = 0; // 重置分类索引
stage = 2; // 继续处理下一组
}
} else {
stage = 5; // 跳出循环
}
break;
case 3: // 识别材料分类
let CategoryObject = getCategoryObject(materialsCategory);
if (!CategoryObject) {
log.error("未知的材料分类");
// ===== 补充优化:异常时释放资源并退出 =====
cachedFrame?.dispose();
stage = 0; // 回退到阶段0
return;
}
const CategoryResult = await recognizeImage(CategoryObject, cachedFrame);
if (CategoryResult.isDetected) {
log.info(`识别到${materialsCategory} 所在分类。`);
stage = 4; // 进入下一阶段
} else {
log.warn("未识别到材料分类图标,重新尝试");
// log.warn(`识别结果:${JSON.stringify(CategoryResult)}`);
// ===== 补充优化:连续回退时释放资源 =====
cachedFrame?.dispose();
stage = 2; // 回退到阶段2
}
break;
case 4: // 扫描材料
log.info("芭芭拉,冲鸭!");
// 判断是否需要重置滑条
if (!skipSliderReset) {
await moveMouseTo(1288, 124); // 移动鼠标至滑条顶端
await sleep(200);
leftButtonDown(); // 长按左键重置材料滑条
await sleep(300);
leftButtonUp();
await sleep(200);
} else {
log.info("同一大类且为后位材料,跳过滑条重置");
// 不重置滑条,直接从当前位置开始检查第八列
}
// 扫描材料并获取低于目标数量的材料
const lowCountMaterials = await scanMaterials(materialsCategory, materialCategoryMap);
allLowCountMaterials.push(lowCountMaterials);
// 保存当前分类信息,用于下一个分类的判断
prevCategory = materialsCategory;
prevPriority = materialPriority[materialsCategory];
// 获取当前分类的CategoryObject
const currentCategoryObject = getCategoryObject(materialsCategory);
prevCategoryObject = currentCategoryObject;
prevGroup = sortedGroups[currentGroupIndex];
currentCategoryIndex++;
// 判断下一个分类是否是同一个大类CategoryObject下的后位材料
let nextCategory = null;
let nextCategoryObject = null;
let nextPriority = null;
// 检查是否还有下一个分类
if (currentGroupIndex < sortedGroups.length) {
const group = sortedGroups[currentGroupIndex];
if (currentCategoryIndex < group.categories.length) {
nextCategory = group.categories[currentCategoryIndex];
// 获取下一个分类的CategoryObject
nextCategoryObject = getCategoryObject(nextCategory);
// 获取下一个分类的优先级
nextPriority = materialPriority[nextCategory];
}
}
// 判断是否跳过滑条重置:同一大类且为后位材料
if (nextCategory &&
nextCategoryObject === prevCategoryObject &&
nextPriority > prevPriority) {
skipSliderReset = true;
} else {
skipSliderReset = false;
}
stage = 2; // 返回阶段2处理下一个分类
break;
case 5: // 所有分组处理完毕
log.info("所有分组处理完毕,返回主界面");
await genshin.returnMainUi();
stage = maxStage + 1; // 确保退出循环
break;
}
}
// ===== 补充优化:循环超限处理,防止卡死 =====
if (loopCount > maxLoopCount) {
log.error(`主循环次数超限(${maxLoopCount}次),强制退出`);
cachedFrame?.dispose();
await genshin.returnMainUi();
return [];
}
await genshin.returnMainUi(); // 返回主界面
log.info("扫描流程结束");
// 3. 处理完成后,将输出结果转换回原始名称
const finalResult = allLowCountMaterials.map(categoryMaterials => {
return categoryMaterials.map(material => {
// 假设material包含name属性将别名转回原始名
return {
...material,
name: nameMap.get(material.name) || material.name // 反向映射
};
try {
// 1. 先记录原始名称与别名的映射关系(用于最后反向转换)
const nameMap = new Map();
Object.values(materialCategoryMap).flat().forEach(originalName => {
const aliasName = MATERIAL_ALIAS[originalName] || originalName;
nameMap.set(aliasName, originalName); // 存储:别名→原始名
});
});
cachedFrame?.dispose();
return finalResult; // 返回转换后的结果(如"晶蝶"
// 2. 转换materialCategoryMap为别名用于内部处理
const processedMap = {};
Object.entries(materialCategoryMap).forEach(([category, names]) => {
processedMap[category] = names.map(name => MATERIAL_ALIAS[name] || name);
});
materialCategoryMap = processedMap;
const maxStage = 4; // 最大阶段数
let stage = 0; // 当前阶段
let currentGroupIndex = 0; // 当前处理的分组索引
let currentCategoryIndex = 0; // 当前处理的分类索引
let materialsCategory = ""; // 当前处理的材料分类名称
const allLowCountMaterials = []; // 用于存储所有识别到的低数量材料信息
// 添加状态变量,记录上一个分类的信息
let prevCategory = null;
let prevCategoryObject = null;
let prevPriority = null;
let prevGroup = null;
let skipSliderReset = false; // 是否跳过滑条重置
const sortedGroups = dynamicMaterialGrouping(materialCategoryMap);
// log.info("材料 动态[分组]结果:");
sortedGroups.forEach(group => {
log.info(`类型 ${group.type} | 包含分类: ${group.categories.join(', ')}`);
});
let loopCount = 0;
const maxLoopCount = 200; // 合理阈值正常流程约50-100次循环
while (stage <= maxStage && loopCount <= maxLoopCount) { // ===== 补充优化:加入循环次数限制 =====
loopCount++;
switch (stage) {
case 0: // 返回主界面
log.info("返回主界面");
await genshin.returnMainUi();
await sleep(500);
stage = 1; // 进入下一阶段
break;
case 1: // 打开背包界面
// log.info("打开背包界面");
keyPress("B"); // 打开背包界面
state.ocrPaused = true;
log.info("背包扫描开始已暂停OCR拾取任务");
await sleep(800); // 减少等待时间
cachedFrame?.dispose();
cachedFrame = captureGameRegion();
const backpackResult = await recognizeImage(BagpackRo, cachedFrame, 2000, 500, true, "背包");
if (backpackResult.isDetected) {
// log.info("成功识别背包图标");
stage = 2; // 进入下一阶段
} else {
log.warn("未识别到背包图标,重新尝试");
// ===== 补充优化:连续回退时释放资源 =====
cachedFrame?.dispose();
stage = 0; // 回退
}
break;
case 2: // 按分组处理材料分类
if (currentGroupIndex < sortedGroups.length) {
const group = sortedGroups[currentGroupIndex];
if (currentCategoryIndex < group.categories.length) {
materialsCategory = group.categories[currentCategoryIndex];
const offset = materialTypeMap[materialsCategory];
const menuClickX = Math.round(575 + (offset - 1) * 96.25);
// log.info(`点击坐标 (${menuClickX},75)`);
click(menuClickX, 75);
await sleep(500);
cachedFrame?.dispose();
cachedFrame = captureGameRegion();
stage = 3; // 进入下一阶段
} else {
currentGroupIndex++;
currentCategoryIndex = 0; // 重置分类索引
stage = 2; // 继续处理下一组
}
} else {
stage = 5; // 跳出循环
}
break;
case 3: // 识别材料分类
let CategoryObject = getCategoryObject(materialsCategory);
if (!CategoryObject) {
log.error("未知的材料分类");
// ===== 补充优化:异常时释放资源并退出 =====
cachedFrame?.dispose();
stage = 0; // 回退到阶段0
return;
}
const CategoryResult = await recognizeImage(CategoryObject, cachedFrame, 2000, 500, true, materialsCategory);
if (CategoryResult.isDetected) {
log.info(`识别到${materialsCategory} 所在分类。`);
stage = 4; // 进入下一阶段
} else {
log.warn("未识别到材料分类图标,重新尝试");
// log.warn(`识别结果:${JSON.stringify(CategoryResult)}`);
// ===== 补充优化:连续回退时释放资源 =====
cachedFrame?.dispose();
stage = 2; // 回退到阶段2
}
break;
case 4: // 扫描材料
log.info("芭芭拉,冲鸭!");
// 判断是否需要重置滑条
if (!skipSliderReset) {
await moveMouseTo(1288, 124); // 移动鼠标至滑条顶端
await sleep(200);
leftButtonDown(); // 长按左键重置材料滑条
await sleep(300);
leftButtonUp();
await sleep(200);
} else {
log.info("同一大类且为后位材料,跳过滑条重置");
// 不重置滑条,直接从当前位置开始检查第八列
}
// 判断是否是后续优先级材料(优先级高于前一个分类)
const currentPriority = materialPriority[materialsCategory];
const isPostPriority = prevPriority !== null && currentPriority > prevPriority;
if (isPostPriority) {
log.info(`后续优先级材料扫描 - 当前优先级: ${currentPriority}, 前位优先级: ${prevPriority}`);
}
// 扫描材料并获取低于目标数量的材料
const lowCountMaterials = await scanMaterials(materialsCategory, materialCategoryMap, isPostPriority);
allLowCountMaterials.push(lowCountMaterials);
// 保存当前分类信息,用于下一个分类的判断
prevCategory = materialsCategory;
prevPriority = materialPriority[materialsCategory];
// 获取当前分类的CategoryObject
const currentCategoryObject = getCategoryObject(materialsCategory);
prevCategoryObject = currentCategoryObject;
prevGroup = sortedGroups[currentGroupIndex];
currentCategoryIndex++;
// 判断下一个分类是否是同一个大类CategoryObject下的后位材料
let nextCategory = null;
let nextCategoryObject = null;
let nextPriority = null;
// 检查是否还有下一个分类
if (currentGroupIndex < sortedGroups.length) {
const group = sortedGroups[currentGroupIndex];
if (currentCategoryIndex < group.categories.length) {
nextCategory = group.categories[currentCategoryIndex];
// 获取下一个分类的CategoryObject
nextCategoryObject = getCategoryObject(nextCategory);
// 获取下一个分类的优先级
nextPriority = materialPriority[nextCategory];
}
}
// 判断是否跳过滑条重置:同一大类且为后位材料
if (nextCategory &&
nextCategoryObject === prevCategoryObject &&
nextPriority > prevPriority) {
skipSliderReset = true;
} else {
skipSliderReset = false;
}
stage = 2; // 返回阶段2处理下一个分类
break;
case 5: // 所有分组处理完毕
log.info("所有分组处理完毕,返回主界面");
await genshin.returnMainUi();
stage = maxStage + 1; // 确保退出循环
break;
}
}
// ===== 补充优化:循环超限处理,防止卡死 =====
if (loopCount > maxLoopCount) {
log.error(`主循环次数超限(${maxLoopCount}次),强制退出`);
cachedFrame?.dispose();
await genshin.returnMainUi();
return [];
}
await genshin.returnMainUi(); // 返回主界面
log.info("扫描流程结束");
// 3. 处理完成后,将输出结果转换回原始名称
const finalResult = allLowCountMaterials.map(categoryMaterials => {
return categoryMaterials.map(material => {
// 假设material包含name属性将别名转回原始名
return {
...material,
name: nameMap.get(material.name) || material.name // 反向映射
};
});
});
cachedFrame?.dispose();
return finalResult; // 返回转换后的结果(如"晶蝶"
} finally {
state.ocrPaused = false;
log.info("背包扫描结束已恢复OCR拾取任务");
}
}

View File

@@ -217,11 +217,36 @@ async function imageClickBackgroundTask() {
// 配置参数
const taskDelay = Math.min(999, Math.max(1, Math.floor(Number(settings.PopupClickDelay) || 15))) * 1000;
const specificNamesStr = settings.PopupNames || "";
const specificNames = specificNamesStr
.split(/[,,、 \s]+/)
.map(name => name.trim())
.filter(name => name !== "");
let specificNames = [];
try {
specificNames = Array.from(settings.PopupNames || []);
} catch (e) {
log.error(`获取PopupNames设置失败: ${e.message}`);
}
let availablePopupDirs = [];
try {
const imageClickDir = "assets/imageClick";
const subDirs = readAllFilePaths(imageClickDir, 0, 2, [], true);
availablePopupDirs = subDirs.filter(subDir => {
const dirName = basename(subDir);
const entries = readAllFilePaths(subDir, 0, 0, [], true);
return entries.some(entry => normalizePath(entry).endsWith('/icon'));
}).map(dir => basename(dir));
log.info(`可用弹窗目录:${availablePopupDirs.join(', ')}`);
} catch (e) {
log.error(`扫描弹窗目录失败: ${e.message}`);
}
if (specificNames.length === 0) {
log.info("未指定弹窗名称,将处理所有可用弹窗");
} else {
const invalidNames = specificNames.filter(name => !availablePopupDirs.includes(name));
if (invalidNames.length > 0) {
log.warn(`以下弹窗名称不存在,将被忽略:${invalidNames.join(', ')}`);
specificNames = specificNames.filter(name => availablePopupDirs.includes(name));
}
}
// 预加载资源
const preloadedResources = await preloadImageResources(specificNames);

View File

@@ -0,0 +1,180 @@
// 自动更新settings.json中的options数组
// 使用BetterGI的file对象不需要Node.js的fs模块
var SETTINGS_FILE = "settings.json";
function updateSettingsOptions() {
log.info("开始更新settings.json...");
try {
var settingsContent = file.readTextSync(SETTINGS_FILE);
log.info("settings.json内容长度: " + settingsContent.length);
var settings = JSON.parse(settingsContent);
log.info("settings.json解析成功配置项数量: " + settings.length);
var hasChanges = false;
var popupDirs = readAllFilePaths("assets/imageClick", 0, 2, [], true)
.filter(function (dirPath) {
var entries = readAllFilePaths(dirPath, 0, 0, [], true);
return entries.some(function (entry) {
return normalizePath(entry).endsWith('/icon');
});
})
.filter(function (dirPath) {
return !normalizePath(dirPath).includes('/其他/');
})
.map(function (dirPath) {
return basename(dirPath);
})
.sort();
log.info("扫描到弹窗目录数量: " + popupDirs.length);
var popupSetting = settings.find(function (s) {
return s.name === "PopupNames";
});
if (popupSetting) {
log.info("找到PopupNames配置项");
var existingOptions = popupSetting.options || [];
log.info("现有options数量: " + existingOptions.length);
var existingSet = {};
for (var k = 0; k < existingOptions.length; k++) {
existingSet[existingOptions[k]] = true;
}
var popupSet = {};
for (var p = 0; p < popupDirs.length; p++) {
popupSet[popupDirs[p]] = true;
}
var newOptions = [];
var removedOptions = [];
for (var m = 0; m < popupDirs.length; m++) {
if (!existingSet[popupDirs[m]]) {
newOptions.push(popupDirs[m]);
}
}
for (var n = 0; n < existingOptions.length; n++) {
if (!popupSet[existingOptions[n]]) {
removedOptions.push(existingOptions[n]);
}
}
log.info("新增options数量: " + newOptions.length);
log.info("删除options数量: " + removedOptions.length);
if (newOptions.length > 0 || removedOptions.length > 0) {
popupSetting.options = popupDirs;
hasChanges = true;
if (newOptions.length > 0) {
log.info("PopupNames新增选项: " + newOptions.join(', '));
}
if (removedOptions.length > 0) {
log.info("PopupNames删除选项: " + removedOptions.join(', '));
}
} else {
log.info("PopupNames无新增选项");
}
} else {
log.info("未找到PopupNames配置项");
}
var cdCategories = readAllFilePaths("materialsCD", 0, 1, ['.txt'])
.map(function (filePath) {
return basename(filePath).replace('.txt', '');
})
.sort();
var cdSetting = settings.find(function (s) {
return s.name === "CDCategories";
});
if (cdSetting) {
var existingOptions = cdSetting.options || [];
var existingSet = {};
for (var k = 0; k < existingOptions.length; k++) {
existingSet[existingOptions[k]] = true;
}
var cdSet = {};
for (var p = 0; p < cdCategories.length; p++) {
cdSet[cdCategories[p]] = true;
}
var newOptions = [];
var removedOptions = [];
for (var m = 0; m < cdCategories.length; m++) {
if (!existingSet[cdCategories[m]]) {
newOptions.push(cdCategories[m]);
}
}
for (var n = 0; n < existingOptions.length; n++) {
if (!cdSet[existingOptions[n]]) {
removedOptions.push(existingOptions[n]);
}
}
if (newOptions.length > 0 || removedOptions.length > 0) {
cdSetting.options = cdCategories;
hasChanges = true;
if (newOptions.length > 0) {
log.info("CDCategories新增选项: " + newOptions.join(', '));
}
if (removedOptions.length > 0) {
log.info("CDCategories删除选项: " + removedOptions.join(', '));
}
}
}
var pickCategories = readAllFilePaths("targetText", 0, 1, ['.txt'])
.map(function (filePath) {
return basename(filePath).replace('.txt', '');
})
.sort();
var pickSetting = settings.find(function (s) {
return s.name === "PickCategories";
});
if (pickSetting) {
var existingOptions = pickSetting.options || [];
var existingSet = {};
for (var k = 0; k < existingOptions.length; k++) {
existingSet[existingOptions[k]] = true;
}
var pickSet = {};
for (var p = 0; p < pickCategories.length; p++) {
pickSet[pickCategories[p]] = true;
}
var newOptions = [];
var removedOptions = [];
for (var m = 0; m < pickCategories.length; m++) {
if (!existingSet[pickCategories[m]]) {
newOptions.push(pickCategories[m]);
}
}
for (var n = 0; n < existingOptions.length; n++) {
if (!pickSet[existingOptions[n]]) {
removedOptions.push(existingOptions[n]);
}
}
if (newOptions.length > 0 || removedOptions.length > 0) {
pickSetting.options = pickCategories;
hasChanges = true;
if (newOptions.length > 0) {
log.info("PickCategories新增选项: " + newOptions.join(', '));
}
if (removedOptions.length > 0) {
log.info("PickCategories删除选项: " + removedOptions.join(', '));
}
}
}
if (hasChanges) {
var updatedContent = JSON.stringify(settings, null, 2);
file.writeTextSync(SETTINGS_FILE, updatedContent, false);
log.info("settings.json已自动更新");
} else {
log.info("settings.json无需更新");
}
} catch (error) {
log.error("自动更新settings.json失败: " + error.message);
}
}
updateSettingsOptions();

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{
"manifest_version": 1,
"name": "背包材料统计采集管理系统",
"version": "2.60",
"name": "背包统计采集系统",
"version": "2.61",
"bgi_version": "0.55",
"description": "可统计背包养成道具、部分食物、素材的数量根据设定数量、根据材料刷新CD执行挖矿、采集、刷怪等的路径。优势\n+ 1. 自动判断材料CD不需要管材料CD有没有好\n+ 2. 可以随意添加路径,能自动排除低效、无效路径;\n+ 3. 有独立名单识别不会交互路边的npc或是神像可自定义识别名单具体方法看【问题解答】增减识别名单\n+ 4. 有实时的弹窗模块,提供了常见的几种:路边信件、过期物品、月卡、调查;\n+ 5. 可识别爆满的路径材料自动屏蔽更多详细内容查看readme.md",
"description": "可统计背包养成道具、部分食物、素材的数量根据设定数量、根据材料刷新CD执行挖矿、采集、刷怪等的路径。优势\n+ 1. 自动判断材料CD不需要管材料CD有没有好\n+ 2. 可以随意添加路径,能自动排除低效、无效路径;\n+ 3. 有独立名单识别不会交互路边的npc或是神像可自定义识别名单具体方法看【问题解答】增减识别名单\n+ 4. 有实时的弹窗模块,提供了常见的几种:路边信件、过期物品、月卡、调查;\n+ 5. 可识别爆满的路径材料自动屏蔽更多详细内容查看readme.md;可在我的主页下载 路径重命名 工具JS给路径名批量添加检测码方便识别。",
"saved_files": [
"pathing/",
"history_record/",

View File

@@ -1,105 +1,135 @@
[
{
"name": "TargetCount",
"type": "input-text",
"label": "js目录下默认扫描的文件结构\n./📁BetterGI/📁User/📁JsScript/📁背包材料统计/\n 📁pathing/\n 📁 食材与炼金/\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或勾选【材料分类】的材料。默认仅📁pathing材料",
"options": [
"1.兼并:📁pathing材料+【材料分类】",
"2.仅📁pathing材料",
"3.仅【材料分类】勾选"
]
},
{
"name": "Categories",
"type": "multi-checkbox",
"label": "\n----------------------------------\n【材料分类】",
"options": [
"矿石、原胚",
"经验书、怪物掉落",
"采集食物",
"一般素材",
"烹饪用食材",
"周本素材",
"木材",
"世界BOSS",
"鱼饵、鱼类",
"宝石",
"天赋素材",
"武器突破"
],
"default": [
"一般素材",
"烹饪用食材"
]
},
{
"name": "PopupNames",
"type": "input-text",
"label": "数字太小可能无法识别,用?代替\n====================\n弹窗名默认全部)"
},
{
"name": "PopupClickDelay",
"type": "input-text",
"label": "如 过期物品,信件,自定义文件夹名。注意分隔符和文件夹格式\n----------------------------------\n弹窗循环间隔默认:5 秒)"
},
{
"name": "CDCategories",
"type": "input-text",
"label": "----------------------------------\n\n采用的CD分类默认全部) 举例:采集,怪物,木材"
},
{
"name": "CurrentTime",
"type": "input-text",
"label": "根据CD分类来加载路径文件具体看materialsCD目录\n====================\n\n终止时刻默认不执行) 例4:00"
},
{
"name": "PickCategories",
"type": "input-text",
"label": "====================\n\n采用的识别名单默认全部) 举例:交互,采集,宝箱"
},
{
"name": "ExceedCount",
"type": "input-text",
"label": "举例:交互,采集,宝箱\n----------------------------------\n超量阈值默认:9000)超量的路径材料将不拾取"
},
{
"name": "HoldX",
"type": "input-text",
"label": "------------------------\n(0,0)———> X 增加\n |\n |\n V Y 增加\n\n翻页拖动点X坐标0~1920(默认1050)"
},
{
"name": "HoldY",
"type": "input-text",
"label": "------------------------\n翻页拖动点Y坐标0~1080(默认750)"
},
{
"name": "PageScrollDistance",
"type": "input-text",
"label": "------------------------\n拖动距离(默认711像素点)"
}
[
{
"name": "TargetCount",
"type": "input-text",
"label": "js目录下默认扫描的文件结构\n./📁BetterGI/📁User/📁JsScript/📁背包材料统计/\n 📁pathing/\n 📁 食材与炼金/\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": "PickCategories",
"type": "multi-checkbox",
"label": "====================\n\n采用的识别名单默认全部\n不勾选则默认",
"options": [
"交互",
"宝箱",
"掉落",
"采集"
]
},
{
"name": "ExceedCount",
"type": "input-text",
"label": "----------------------------------\n超量阈值默认:9000),超量的路径材料将也不拾取\n普通材料超量或者怪物掉落的三个材料都超量则跳过其路径"
},
{
"name": "notify",
"type": "checkbox",
"label": "----------------------------------\n是否发送通知。默认否\n需在BGI开启JS通知并设置通知地址"
},
{
"name": "noRecord",
"type": "checkbox",
"label": "----------------------------------\n取消扫描。默认否\n勾选将不进行单路径的扫描但保留运行时间记录\n(推荐路径记录炼成后启用)"
},
{
"name": "Pathing",
"type": "multi-checkbox",
"label": "====================\n统计选择📁pathing下的材料或【扫描额外的分类】的材料。默认仅📁pathing材料",
"options": [
"📁pathing材料",
"【扫描额外的分类】"
],
"default": [
"📁pathing材料"
]
},
{
"name": "Categories",
"type": "multi-checkbox",
"label": "\n----------------------------------\n【扫描的分类】",
"options": [
"矿石、原胚",
"经验书、怪物掉落",
"采集食物",
"一般素材",
"烹饪用食材",
"周本素材",
"木材",
"世界BOSS",
"鱼饵、鱼类",
"宝石",
"天赋素材",
"武器突破"
],
"default": [
"一般素材",
"烹饪用食材"
]
},
{
"name": "PopupNames",
"type": "multi-checkbox",
"label": "数字太小可能无法识别,用?代替\n\n====================\n弹窗名默认全部不勾选则默认。",
"options": [
"信件",
"冻结",
"复苏",
"对话",
"月卡",
"登录",
"调查",
"过期物品"
]
},
{
"name": "PopupClickDelay",
"type": "input-text",
"label": "----------------------------------\n弹窗循环间隔默认:15 秒)"
},
{
"name": "CDCategories",
"type": "multi-checkbox",
"label": "====================\n\n根据CD分类选择加载路径文件默认全部\n不勾选则默认。",
"options": [
"怪物",
"掉落",
"木材",
"狗粮",
"采集"
]
},
{
"name": "CurrentTime",
"type": "input-text",
"label": "\n\n----------------------------------\n\n终止时刻默认不执行) 例4:00"
},
{
"name": "HoldX",
"type": "input-text",
"label": "------------------------\n(0,0)———> X 增加\n |\n |\n V Y 增加\n\n翻页拖动点X坐标0~1920(默认1050)"
},
{
"name": "HoldY",
"type": "input-text",
"label": "------------------------\n翻页拖动点Y坐标0~1080(默认750)"
},
{
"name": "PageScrollDistance",
"type": "input-text",
"label": "------------------------\n拖动距离(默认711像素点)推荐一次划页稍小于4行材料的距离"
},
{
"name": "debugLog",
"type": "checkbox",
"label": "------------------------\n调试日志。默认否\n输出详细的调试信息"
}
]

View File

@@ -6,5 +6,3 @@
矿石:萃凝晶,紫晶块,星银矿石,魔晶块,水晶块,白铁块,铁块,虹滴晶,
狗粮:冒险家头带,冒险家金杯,冒险家怀表,冒险家尾羽,冒险家之花,幸运儿银冠,幸运儿之杯,幸运儿沙漏,幸运儿鹰羽,幸运儿绿花,游医的方巾,游医的药壶,游医的怀钟,游医的枭羽,游医的银莲,感别之冠,异国之盏,逐光之石,归乡之羽,故人之心,奇迹耳坠,奇迹之杯,奇迹之沙,奇迹之羽,奇迹之花,战狂的鬼面,战狂的骨杯,战狂的时计,战狂的翎羽,战狂的蔷薇,教官的帽子,教官的茶杯,教官的怀表,教官的羽饰,教官的胸花,流放者头冠,流放者之杯,流放者怀表,流放者之羽,流放者之花,守护束带,守护之皿,守护座钟,守护徽印,守护之花,勇士的勋章,勇士的期许,勇士的坚毅,勇士的壮行,勇士的冠冕,武人的红花,武人的羽饰,武人的水漏,武人的酒杯,武人的头巾,赌徒的胸花,赌徒的羽饰,赌徒的怀表,赌徒的骰蛊,赌徒的耳环,学士的书签,学士的羽笔,学士的时钟,学士的墨杯,学士的镜片,祭雷礼冠,祭火礼冠,祭水礼冠,祭冰礼冠,