diff --git a/repo/js/背包材料统计/README.md b/repo/js/背包材料统计/README.md index 45963bd74..0df212229 100644 --- a/repo/js/背包材料统计/README.md +++ b/repo/js/背包材料统计/README.md @@ -38,14 +38,15 @@ ### 核心优势 1. **自动CD判断**:无需手动关注材料刷新状态,脚本自动识别CD是否就绪; 2. **灵活路径管理**:支持自定义添加路径,自动排除低效/无效路径; -3. **独立名单识别**:不与路边NPC、神像交互;可自定义识别名单(操作见「四、问题解答」); +3. **独立名单识别**:不与路边NPC、神像交互;可自定义识别名单(操作见「四、问题解答Q4」); 4. **实时弹窗保护**:内置弹窗模块(覆盖路边信件、过期物品、月卡、调查等场景),运行时全程保护路径不被弹窗干扰。 5. **自动黑名单**:内置拾取模块,联动材料统计,可识别爆满的路径材料,自动屏蔽。 ## 二、用前须知 1. 需具备基础电脑操作能力(如文件夹复制、路径查找); -2. 脚本不自带路径文件,需手动对目标文件夹进行操作(步骤见「三、使用方法」)。 +2. 非1080p显示器,使用前需要根据显示器调整背包物品界面的 拖动距离 ,推荐“一次划页稍小于4行材料的距离”; +3. 脚本不自带路径文件,需手动对目标文件夹进行操作(步骤见「三、使用方法」)。 ## 三、使用方法 @@ -110,14 +111,16 @@ | 配置项 | 功能说明 | 操作建议 | |----------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------| | 1. 目标数量 | 仅当背包材料数量**低于此值**时,该材料的路径才会被纳入执行序列 | 这是个统一值,管理路径下全部材料的目标数量 | -| 2. 优先级材料 | 无视“目标数量”限制,直接纳入执行序列顶层(最高优先级) | 填写当前急需材料(例:“虹滴晶”“巡陆艇”) | +| 2. 优先级材料 | 无视“目标数量”限制,直接纳入执行序列顶层(最高优先级) | 填写当前急需材料(例:虹滴晶,巡陆艇) | | 3. 时间成本 | 当一个路径有3-5次运行记录后,自动计算“单材料获取时间”;超过30秒则跳过该路径 | 保持默认30秒即可,无需频繁修改(可过滤低效路径) | | 4. 发送通知 | ① 每类材料跑完通知一次;② 全部材料跑完汇总通知一次(需开启BGI通知) | 建议开启,方便实时了解进度(接收端如企业微信需自行配置) | | 5. 取消扫描 | 取消“每个路径执行后”的背包扫描,仅保留“全部执行前/后”2次扫描 | 有效路径记录达3条以上时可以开启,可节约运行时间 | | 6. 仅 pathing 材料 | 仅扫描 `pathing` 文件夹内的材料,跳过其他分类,大幅缩短扫描时间 | 路径配置完成后开启,提升脚本运行效率 | -| 7. 弹窗名 | 不填则默认循环执行 `assets\imageClick` 文件夹下所有弹窗;填写则仅执行指定弹窗 | 推荐默认,需单独适配某类弹窗时填写(例:仅处理月卡弹窗则填:月卡) | -| 8. 采用的 CD 分类 | 仅执行 `materialsCD` 文件夹内配置的材料路径(支持新增CD分类txt) | 新增材料时,需在该文件夹同步配置CD规则(操作见「四、问题解答4」) | -| 9. 拖动距离 | 解决非1080p分辨率下“划页过头”问题,需调整到“一次划页≤4行” | 拖动点建议选“第五行材料附近”;大于1080p屏可适当减小数值 | +| 7. 弹窗名 | 不填则默认循环执行 `assets\imageClick` 文件夹下所有弹窗;填写则仅执行指定弹窗 | 推荐默认,需单独适配某类弹窗时填写(例:月卡,复苏) | +| 8. 采用的 CD 分类 | 不填则默认执行 `materialsCD` 文件夹内配置的CD分类;填写则仅执行指定CD分类 | 新增材料时,需在该文件夹同步配置CD规则(操作见「四、问题解答Q2」) | +| 9. 采用的识别名单 | 不填则默认执行 `targetText` 文件夹内配置的识别名单;填写则仅执行指定识别名单 | 新增名单时,需符合配置规则(操作见「四、问题解答Q4」) | +| 10. 超量阈值 | 首次扫描后,超量的路径材料,将从识别名单中剔除,默认5000 | 不推荐9999,怪物材料有几千就够了,采用默认数值,可自动避免爆背包 | +| 11. 拖动距离 | 解决非1080p分辨率下“划页过头”问题,需调整到“一次划页≤4行” | 拖动点建议选“第五行材料附近”;大于1080p屏可适当减小数值 | ## 四、注意事项 @@ -134,23 +137,9 @@ ### Q1:如何排除不想要的路径? A:1. 打开 `pathing` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\pathing`); 2. 直接删除/移走目标材料/怪物的路径文件夹; - 3. **注意**:不要将路径文件放入 `targetText` 或 `materialsCD` 文件夹(① 这两个文件夹默认全部读取,增加负担;② 也不安全,会被更新覆盖)。 + **其他方法**:看「四、问题解答Q2」,按格式要求填入对应的材料名(或者从其他CD文件中复制过来),在「JS 自定义设置」【采用的 CD 分类】中输入新建的文件名,即可实现只加载该CD文件里材料的路径。 -### Q2:如何自定义识别名单? -A:1. 打开 `targetText` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\targetText`); - 2. 新建/编辑txt文件,按格式填写:`自定义名称:目标1,目标2`(英文冒号+英文逗号,例:“新材料:霜盏花,便携轴承,”); - 3. 若需排除怪物掉落材料:找到“掉落.txt”,删除对应材料名即可; - 4. 操作参考截图: -
- 自定义识别名单操作截图1 - 自定义识别名单操作截图2 -
- -### Q3:如何识别不规范命名的路径文件夹(如“纳塔食材一条龙”“果园.json”)? -A:将不规范的文件夹/文件,放入**适配的材料文件夹**中即可(路径CD由“所在材料文件夹”决定)。 - 例:“果园.json”放入“苹果”文件夹,将按“苹果”的CD规则执行。 - -### Q4:如何添加新材料? +### Q2:如何添加新材料? A:1. 打开 `materialsCD` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\materialsCD`); 2. 新建/编辑txt文件,按格式填写:`CD规则:材料1,材料2`(中文冒号+中文逗号,CD规则参考自带文件,例:“1次0点:月落银,宿影花,”),`材料1`或`材料2`将会作为标准名; 3. **关键要求**:路径文件夹名、材料图片名必须与“材料1或2”完全一致(多层文件夹默认读取最外层标准名文件夹); @@ -160,6 +149,20 @@ A:1. 打开 `materialsCD` 文件夹(脚本路径:`BetterGI\User\JsScript\ 添加新材料操作截图2 +### Q3:如何识别不规范命名的路径文件夹(如“纳塔食材一条龙”“果园.json”)? +A:将不规范的文件夹/文件,放入**适配的材料文件夹**中即可(路径CD由“所在材料文件夹”决定)。 + 例:“果园.json”放入“苹果”文件夹,将按“苹果”的CD规则执行。 + +### Q4:如何自定义识别名单? +A:1. 打开 `targetText` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\targetText`); + 2. 新建/编辑txt文件,按格式填写:`自定义名称:目标1,目标2`(英文冒号+英文逗号,例:“新材料:霜盏花,便携轴承,”); + 3. 若需排除怪物掉落材料:找到“掉落.txt”,删除对应材料名即可; + 4. 操作参考截图: +
+ 自定义识别名单操作截图1 + 自定义识别名单操作截图2 +
+ ### Q5:如何取消路径执行后扫描背包? A:在「JS自定义设置」中勾选“取消扫描”(依旧会保留“全部材料执行始/末”的2次扫描)。 @@ -209,4 +212,5 @@ A:记录文件夹位于 `BetterGI\User\JsScript\背包材料统计\` 下,各 | v2.41 | 修复“勾选分类的本地记录”bug;新增“仅背包统计”选项;补充记录损坏处理说明 | | v2.42 | 新增“无路径间扫描”“noRecord模式”(适合成熟路径);新增怪物材料CD文件 | | v2.50 | 新增独立名单拾取、弹窗模块;支持怪物名识别 | -| v2.51 | 自定义设置新增“拖动距离/拖动点”;新增月卡弹窗识别;路径材料达9999自动上黑名单;修复怪物0收获记录 | \ No newline at end of file +| v2.51 | 自定义设置新增“拖动距离/拖动点”;新增月卡弹窗识别;路径材料达9999自动上黑名单;修复怪物0收获记录 | +| v2.52 | 自定义设置新增“超量阈值”和“识别名单”输入框;新增多层弹窗逻辑 | \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/其他/F/config.json b/repo/js/背包材料统计/assets/imageClick/其他/F/config.json new file mode 100644 index 000000000..4a23daa02 --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/其他/F/config.json @@ -0,0 +1,10 @@ +{ + "isSpecial": true, + "operationType": "key_press", + "keyCode": "VK_F", + "xOffset": 0, + "yOffset": 0, + "detectRegion": { "x": 1207, "y": 783, "width": 42, "height": 42 }, + "nextLevelOnSuccess": "", + "nextLevelOnFailure": "" +} \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/其他/F/icon/F_2.png b/repo/js/背包材料统计/assets/imageClick/其他/F/icon/F_2.png new file mode 100644 index 000000000..cef22ceb2 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/其他/F/icon/F_2.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json new file mode 100644 index 000000000..2326821af --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json @@ -0,0 +1,10 @@ +{ + "isSpecial": true, + "operationType": "key_mouse_script", + "scriptPath": "assets/滚轮下翻.json", + "xOffset": 0, + "yOffset": 0, + "detectRegion": { "x": 1206, "y": 175, "width": 44, "height": 735 }, + "nextLevelOnSuccess": "assets\\imageClick\\其他\\F", + "nextLevelOnFailure": "" +} \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/其他/滚轮/icon/F_2.png b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/icon/F_2.png new file mode 100644 index 000000000..cef22ceb2 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/icon/F_2.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/其他/调查/Picture/image6.png b/repo/js/背包材料统计/assets/imageClick/其他/调查/Picture/image6.png new file mode 100644 index 000000000..1f6c9c4bd Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/其他/调查/Picture/image6.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/其他/调查/icon/123.png b/repo/js/背包材料统计/assets/imageClick/其他/调查/icon/123.png new file mode 100644 index 000000000..f45347133 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/其他/调查/icon/123.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/复苏/Picture/全员复苏.png b/repo/js/背包材料统计/assets/imageClick/复苏/Picture/全员复苏.png new file mode 100644 index 000000000..e11d2c8b1 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/复苏/Picture/全员复苏.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/复苏/icon/确认.png b/repo/js/背包材料统计/assets/imageClick/复苏/icon/确认.png new file mode 100644 index 000000000..6a8d84c25 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/复苏/icon/确认.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/对话/config.json b/repo/js/背包材料统计/assets/imageClick/对话/config.json new file mode 100644 index 000000000..bb476daf9 --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/对话/config.json @@ -0,0 +1,15 @@ +{ + "isSpecial": true, + "operationType": "key_press", + "keyCode": "VK_SPACE", + "xOffset": 950, + "yOffset": 1050, + "detectRegion": { + "x": 264, + "y": 36, + "width": 66, + "height": 24 + }, + "nextLevelOnSuccess": "assets\\imageClick\\其他\\滚轮", + "nextLevelOnFailure": "" +} \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/对话/icon/NoUI.png b/repo/js/背包材料统计/assets/imageClick/对话/icon/NoUI.png new file mode 100644 index 000000000..c3e42de61 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/对话/icon/NoUI.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/月卡/config.json b/repo/js/背包材料统计/assets/imageClick/月卡/config.json new file mode 100644 index 000000000..3dd0434d6 --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/月卡/config.json @@ -0,0 +1,6 @@ +{ + "xOffset": 0, + "yOffset": 455, + "loopCount": 3, + "loopDelay": 1000 +} \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/登录/Picture/load.png b/repo/js/背包材料统计/assets/imageClick/登录/Picture/load.png new file mode 100644 index 000000000..18c847767 Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/登录/Picture/load.png differ diff --git a/repo/js/背包材料统计/assets/imageClick/登录/config.json b/repo/js/背包材料统计/assets/imageClick/登录/config.json new file mode 100644 index 000000000..a45f728f4 --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/登录/config.json @@ -0,0 +1 @@ +{ "xOffset": -845, "yOffset": 325 } \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/登录/icon/setting.png b/repo/js/背包材料统计/assets/imageClick/登录/icon/setting.png new file mode 100644 index 000000000..3877b3c4e Binary files /dev/null and b/repo/js/背包材料统计/assets/imageClick/登录/icon/setting.png differ diff --git a/repo/js/背包材料统计/lib/autoPick.js b/repo/js/背包材料统计/lib/autoPick.js index e0038dec1..941db67c4 100644 --- a/repo/js/背包材料统计/lib/autoPick.js +++ b/repo/js/背包材料统计/lib/autoPick.js @@ -31,18 +31,31 @@ function readtargetTextCategories(targetTextDir) { const targetTextFilePaths = readAllFilePaths(targetTextDir, 0, 1); const materialCategories = {}; + // 解析筛选名单 + const pickTextNames = (settings.PickCategories || "") + .split(/[,,、 \s]+/).map(n => n.trim()).filter(n => n); + + // 【新增:兜底日志】确认pickTextNames是否为空,方便排查 + log.info(`筛选名单状态:${pickTextNames.length === 0 ? '未指定(空),将加载所有文件' : '指定了:' + pickTextNames.join(',')}`); + for (const filePath of targetTextFilePaths) { if (state.cancelRequested) break; const content = file.readTextSync(filePath); if (!content) { log.error(`加载文件失败:${filePath}`); - continue; // 跳过当前文件 + continue; } const sourceCategory = basename(filePath).replace('.txt', ''); // 去掉文件扩展名 + // 【核心筛选:空名单直接跳过判断,加载所有】 + if (pickTextNames.length === 0) { + // 空名单时,直接保留当前文件,不跳过 + } else if (!pickTextNames.includes(sourceCategory)) { + // 非空名单且不在列表里,才跳过 + continue; + } materialCategories[sourceCategory] = parseCategoryContent(content); } - // log.info(`完成材料分类信息读取,分类信息:${JSON.stringify(materialCategories, null, 2)}`); return materialCategories; } // 定义替换映射表 @@ -171,7 +184,7 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text log.info("检测中..."); lastLogTime = currentTime; } - await sleep(50); // 关键50时可避免F多目标滚动中拾取错,背包js这边有弹窗模块,就没必要增加延迟降低效率了 + await sleep(50); cachedFrame?.dispose(); cachedFrame = captureGameRegion(); @@ -192,7 +205,6 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text for (let targetText of targetTexts) { let targetResult = ocrResults.find(res => res.text.includes(targetText)); if (targetResult) { - // log.info(`找到目标文本: ${targetText}`); // 生成唯一标识并更新识别计数(文本+Y坐标) const materialId = `${targetText}-${targetResult.y}`; @@ -203,7 +215,6 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text // log.info(`目标文本 '${targetText}' 和 F 图标水平对齐`); if (recognitionCount.get(materialId) >= 1) { keyPress("F"); // 执行交互操作 - // log.info(`F键执行成功,识别计数: ${recognitionCount.get(materialId)}`); log.info(`交互或拾取: ${targetText}`); // F键后清除计数,确保单次交互 diff --git a/repo/js/背包材料统计/lib/backStats.js b/repo/js/背包材料统计/lib/backStats.js index e75e40183..76320e18c 100644 --- a/repo/js/背包材料统计/lib/backStats.js +++ b/repo/js/背包材料统计/lib/backStats.js @@ -4,6 +4,7 @@ const holdX = Math.min(1920, Math.max(0, Math.floor(Number(settings.HoldX) || 10 const holdY = Math.min(1080, Math.max(0, Math.floor(Number(settings.HoldY) || 750))); const totalPageDistance = Math.max(10, Math.floor(Number(settings.PageScrollDistance) || 711)); 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) || 5000))); // 设定的超量目标数量 const imageDelay = Math.min(1000, Math.max(0, Math.floor(Number(settings.ImageDelay) || 0))); // 识图基准时长 await sleep(imageDelay); // 配置参数 @@ -383,7 +384,7 @@ async function scanMaterials(materialsCategory, materialCategoryMap) { width: 66 + 2 * tolerance, height: 22 + 2 * tolerance }; - const ocrResult = await recognizeText(ocrRegion, 1000, 10, 5, 2, ra); + const ocrResult = await recognizeText(ocrRegion, 200, 10, 5, 2, ra); materialInfo.push({ name, count: ocrResult || "?" }); if (!hasFoundFirstMaterial) { @@ -516,7 +517,7 @@ var excessMaterialNames = []; // 超量材料名单 function filterLowCountMaterials(pathingMaterialCounts, materialCategoryMap) { // 新增:超量阈值(普通材料9999,矿石处理后也是9999) - const EXCESS_THRESHOLD = 9999; + const EXCESS_THRESHOLD = exceedCount; // 新增:临时存储本次超量材料 const tempExcess = []; diff --git a/repo/js/背包材料统计/lib/file.js b/repo/js/背包材料统计/lib/file.js index a7d83cca6..a5d3d5666 100644 --- a/repo/js/背包材料统计/lib/file.js +++ b/repo/js/背包材料统计/lib/file.js @@ -1,16 +1,20 @@ +// ======================== 全局工具函数(只定义1次,所有函数共用)======================== +// 1. 路径标准化函数(统一处理,消除重复) +function normalizePath(path) { + if (!path || typeof path !== 'string') return ''; + let standardPath = path.replace(/\\/g, '/').replace(/\/+/g, '/'); + return standardPath.endsWith('/') ? standardPath.slice(0, -1) : standardPath; +} - -// ============================================== -// 5. 角色识别与策略执行相关函数(保留原始功能) -// ============================================== -// 工具函数 +// 2. 提取路径最后一级名称 function basename(filePath) { if (!filePath || typeof filePath !== 'string') return ''; - const normalizedPath = filePath.replace(/\\/g, '/'); + const normalizedPath = normalizePath(filePath); const lastSlashIndex = normalizedPath.lastIndexOf('/'); return lastSlashIndex !== -1 ? normalizedPath.substring(lastSlashIndex + 1) : normalizedPath; } + /* // 如果路径存在且返回的是数组,则认为是目录Folder function pathExists(path) { diff --git a/repo/js/背包材料统计/lib/imageClick.js b/repo/js/背包材料统计/lib/imageClick.js index b82271560..1eeecf484 100644 --- a/repo/js/背包材料统计/lib/imageClick.js +++ b/repo/js/背包材料统计/lib/imageClick.js @@ -1,195 +1,457 @@ -// 新增:独立的预加载函数,负责所有资源预处理 +// ======================== 1. 预加载函数(精简日志版)======================== async function preloadImageResources(specificNames) { - log.info("开始预加载所有图片资源"); + // log.info("开始预加载所有图片资源"); + + function hasIconFolder(dirPath) { + try { + const entries = readAllFilePaths(dirPath, 0, 0, [], true); + return entries.some(entry => normalizePath(entry).endsWith('/icon')); + } catch (e) { + log.error(`检查目录【${dirPath}】是否有icon文件夹失败:${e.message}`); + return false; + } + } - // 统一参数格式(与原逻辑一致) let preSpecificNames = specificNames; - if (typeof specificNames === 'string') { - preSpecificNames = [specificNames]; - } + if (typeof specificNames === 'string') preSpecificNames = [specificNames]; const isAll = !preSpecificNames || preSpecificNames.length === 0; - if (isAll) { - log.info("未指定具体弹窗名称,将执行所有弹窗目录处理"); - } else { - log.info(`指定处理弹窗名称:${preSpecificNames.join(', ')}`); - } + // if (isAll) log.info("未指定具体弹窗名称,将执行所有含icon文件夹的弹窗目录处理"); + // else log.info(`指定处理弹窗名称:${preSpecificNames.join(', ')}(仅含icon文件夹的目录)`); - // 定义根目录(与原代码一致) const rootDir = "assets/imageClick"; + const rootDirNormalized = normalizePath(rootDir); + const subDirs = readAllFilePaths(rootDir, 0, 2, [], true); - // 获取所有子目录(与原代码一致) - const subDirs = readAllFilePaths(rootDir, 0, 0, [], true); - - // 筛选目标目录(与原代码一致) - const targetDirs = isAll - ? subDirs - : subDirs.filter(subDir => { - const dirName = basename(subDir); - return preSpecificNames.includes(dirName); - }); + const targetDirs = subDirs.filter(subDir => { + const dirName = basename(subDir); + const hasIcon = hasIconFolder(subDir); + const matchName = isAll ? true : preSpecificNames.includes(dirName); + return hasIcon && matchName; + }); if (targetDirs.length === 0) { - log.info(`未找到与指定名称匹配的目录,名称列表:${preSpecificNames?.join(', ') || '所有'}`); + // log.info("未找到符合条件的弹窗目录"); return []; } - // 预加载所有目录的资源(原imageClick内的资源加载逻辑) const preloadedResources = []; for (const subDir of targetDirs) { const dirName = basename(subDir); - // log.info(`开始预处理弹窗类型:${dirName}`); + const fullPath = normalizePath(subDir); + const pathSegments = fullPath.slice(rootDirNormalized.length + 1).split('/'); + const level = pathSegments.length; + const isFirstLevel = level === 1; - // 查找icon和Picture文件夹(与原代码一致) - const entries = readAllFilePaths(subDir, 0, 1, [], true); - const iconDir = entries.find(entry => entry.endsWith('\icon')); - const pictureDir = entries.find(entry => entry.endsWith('\Picture')); + let popupConfig = { + isSpecial: false, + operationType: "click", + ocrConfig: null, + xOffset: 0, + yOffset: 0, + detectRegion: null, + nextLevelOnSuccess: "", + nextLevelOnFailure: "", + loopCount: 1, + loopDelay: 0 + }; + const configPath = normalizePath(`${subDir}/config.json`); + let isSpecialModule = false; + let specialDetectRegion = null; - if (!iconDir) { - log.warn(`未找到 icon 文件夹,跳过分类文件夹:${subDir}`); - continue; - } - if (!pictureDir) { - log.warn(`未找到 Picture 文件夹,跳过分类文件夹:${subDir}`); - continue; + if (fileExists(configPath)) { + try { + const configContent = file.readTextSync(configPath); + popupConfig = { ...popupConfig, ...JSON.parse(configContent) }; + isSpecialModule = popupConfig.isSpecial === true + && typeof popupConfig.detectRegion === 'object' + && popupConfig.detectRegion !== null + && popupConfig.detectRegion.x != null + && popupConfig.detectRegion.y != null + && popupConfig.detectRegion.width != null + && popupConfig.detectRegion.height != null + && popupConfig.detectRegion.width > 0 + && popupConfig.detectRegion.height > 0; + specialDetectRegion = isSpecialModule ? popupConfig.detectRegion : null; + // log.info(`【${dirName}】加载配置成功:${isFirstLevel ? '第一级' : '第二级'} | 模块类型:${isSpecialModule ? '特殊模块' : '普通模块'}`); + } catch (e) { + log.error(`【${dirName}】解析配置失败,使用默认配置:${e.message}`); + isSpecialModule = false; + } } - // 读取图片文件(与原代码一致) - const iconFilePaths = readAllFilePaths(iconDir, 0, 0, ['.png', '.jpg', '.jpeg']); - const pictureFilePaths = readAllFilePaths(pictureDir, 0, 0, ['.png', '.jpg', '.jpeg']); - - // 预创建icon识别对象(与原代码一致) - const iconRecognitionObjects = []; - for (const filePath of iconFilePaths) { - const mat = file.readImageMatSync(filePath); - if (mat.empty()) { - log.error(`加载图标失败:${filePath}`); + if (isSpecialModule) { + const entries = readAllFilePaths(subDir, 0, 1, [], true); + const iconDir = entries.find(entry => normalizePath(entry).endsWith('/icon')); + const iconFilePaths = readAllFilePaths(iconDir, 0, 0, ['.png', '.jpg', '.jpeg']); + + if (iconFilePaths.length === 0) { + log.warn(`【${dirName}】特殊模块无有效icon文件,跳过`); continue; } - const recognitionObject = RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080); - iconRecognitionObjects.push({ name: basename(filePath), ro: recognitionObject, iconDir }); - } - // 预创建图库区域(与原代码一致) - const pictureRegions = []; - for (const filePath of pictureFilePaths) { - const mat = file.readImageMatSync(filePath); - if (mat.empty()) { - log.error(`加载图库图片失败:${filePath}`); + const iconRecognitionObjects = []; + for (const filePath of iconFilePaths) { + const mat = file.readImageMatSync(filePath); + if (mat.empty()) { + log.error(`【${dirName}】特殊模块加载图标失败:${filePath}`); + continue; + } + iconRecognitionObjects.push({ + name: basename(filePath), + ro: RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080), + iconDir, + mat: mat + }); + } + + const targetIcon = iconRecognitionObjects[0]; + const manualRegion = new ImageRegion(targetIcon.mat, specialDetectRegion.x, specialDetectRegion.y); + manualRegion.width = specialDetectRegion.width; + manualRegion.height = specialDetectRegion.height; + + const foundRegions = [{ + pictureName: "特殊模块", + iconName: targetIcon.name, + region: manualRegion, + iconDir: iconDir + }]; + // log.info(`【${dirName}】特殊模块生成识别区域:x=${manualRegion.x}, y=${manualRegion.y}, 宽=${manualRegion.width}, 高=${manualRegion.height}`); + + preloadedResources.push({ + dirName, + fullPath, + foundRegions, + popupConfig, + isFirstLevel: isFirstLevel, + level: level + }); + + } else { + const entries = readAllFilePaths(subDir, 0, 1, [], true); + const iconDir = entries.find(entry => normalizePath(entry).endsWith('/icon')); + const pictureDir = entries.find(entry => normalizePath(entry).endsWith('/Picture')); + + if (!pictureDir) { + log.warn(`【${dirName}】普通模块无Picture文件夹,跳过`); continue; } - pictureRegions.push({ name: basename(filePath), region: new ImageRegion(mat, 0, 0) }); - } - // 预计算匹配区域(与原代码一致) - const foundRegions = []; - for (const picture of pictureRegions) { - for (const icon of iconRecognitionObjects) { - const foundRegion = picture.region.find(icon.ro); - if (foundRegion.isExist()) { - foundRegions.push({ - pictureName: picture.name, - iconName: icon.name, - region: foundRegion, - iconDir: icon.iconDir - }); + const iconFilePaths = readAllFilePaths(iconDir, 0, 0, ['.png', '.jpg', '.jpeg']); + const pictureFilePaths = readAllFilePaths(pictureDir, 0, 0, ['.png', '.jpg', '.jpeg']); + + // 仅在资源为空时警告 + if (iconFilePaths.length === 0) { + log.warn(`【${dirName}】普通模块无有效icon文件,跳过`); + continue; + } + if (pictureFilePaths.length === 0) { + log.warn(`【${dirName}】普通模块无有效Picture文件,跳过`); + continue; + } + + const iconRecognitionObjects = []; + for (const filePath of iconFilePaths) { + const mat = file.readImageMatSync(filePath); + if (mat.empty()) { + log.error(`【${dirName}】加载图标失败:${filePath}`); + continue; + } + iconRecognitionObjects.push({ + name: basename(filePath), + ro: RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080), + iconDir + }); + } + + const pictureRegions = []; + for (const filePath of pictureFilePaths) { + const mat = file.readImageMatSync(filePath); + if (mat.empty()) { + log.error(`【${dirName}】加载图库图片失败:${filePath}`); + continue; + } + pictureRegions.push({ + name: basename(filePath), + region: new ImageRegion(mat, 0, 0) + }); + } + + const foundRegions = []; + for (const picture of pictureRegions) { + for (const icon of iconRecognitionObjects) { + const foundRegion = picture.region.find(icon.ro); + if (foundRegion.isExist()) { + foundRegions.push({ + pictureName: picture.name, + iconName: icon.name, + region: foundRegion, + iconDir: icon.iconDir + }); + } } } - } + if (foundRegions.length === 0) { + log.warn(`【${dirName}】普通模块无匹配图标,跳过`); + continue; + } - // 保存预处理结果 - preloadedResources.push({ - dirName, - foundRegions - }); + preloadedResources.push({ + dirName, + fullPath, + foundRegions, + popupConfig, + isFirstLevel: isFirstLevel, + level: level + }); + } } - log.info(`预加载完成,共处理 ${preloadedResources.length} 个目录`); + // log.info(`预加载完成,共${preloadedResources.length}个有效弹窗目录(第一级:${preloadedResources.filter(r => r.isFirstLevel).length}个,第二级:${preloadedResources.filter(r => !r.isFirstLevel).length}个)`); return preloadedResources; } -// 新增:imageClick后台任务函数 +// ======================== 2. 后台任务函数(精简日志版)======================== async function imageClickBackgroundTask() { log.info("imageClick后台任务已启动"); - const imageClickDelay = Math.min(99, Math.max(1, Math.floor(Number(settings.PopupClickDelay) || 5)))*1000; - // 可以根据需要配置要处理的弹窗名称 + + // 配置参数 + 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 !== ""); - // 调用独立预加载函数(循环前仅执行一次) + // 预加载资源 const preloadedResources = await preloadImageResources(specificNames); if (preloadedResources.length === 0) { - log.info("无可用预加载资源,任务结束"); + log.info("无可用弹窗资源,任务结束"); return { success: false }; } - // 循环执行,仅使用预加载资源 + // 筛选一级弹窗 + const firstLevelDirs = preloadedResources.filter(res => res.isFirstLevel); + if (firstLevelDirs.length === 0) { + log.warn("无第一级弹窗目录,任务终止"); + return { success: false }; + } + + // 打印资源检测结果 + log.info("\n==================== 现有弹窗加载结果 ===================="); + log.info("1. 一级弹窗(共" + firstLevelDirs.length + "个):"); + firstLevelDirs.forEach((res, idx) => log.info(` ${idx+1}. 【${res.dirName}】`)); + const secondLevelResources = preloadedResources.filter(res => !res.isFirstLevel); + log.info("\n2. 二级弹窗(共" + secondLevelResources.length + "个):"); + secondLevelResources.forEach((res, idx) => log.info(` ${idx+1}. 【${res.dirName}】`)); + log.info("=============================================================\n"); + + // 核心逻辑:外循环遍历所有一级弹窗 while (!state.completed && !state.cancelRequested) { - try { - // 调用imageClick时传入预加载资源 - await imageClick(preloadedResources, null, specificNames, true); - } catch (error) { - log.info(`弹窗识别失败(继续重试):${error.message}`); + // log.info(`\n===== 外循环开始:遍历所有一级弹窗(共${firstLevelDirs.length}个) =====`); + + // 遍历所有一级弹窗 + for (const currentFirstLevel of firstLevelDirs) { + // 检查当前一级弹窗是否被触发 + const levelResult = await imageClick([currentFirstLevel], null, [currentFirstLevel.dirName], true); + + if (levelResult.success) { + log.info(`【${currentFirstLevel.dirName}】触发成功,进入内部流程...`); + const levelStack = [currentFirstLevel]; + + // 内循环处理内部流程 + while (levelStack.length > 0 && !state.completed && !state.cancelRequested) { + const currentResource = levelStack[levelStack.length - 1]; + const innerResult = await imageClick([currentResource], null, [currentResource.dirName], true); + + if (innerResult.success) { + const nextPath = normalizePath(currentResource.popupConfig.nextLevelOnSuccess); + if (nextPath && nextPath.trim()) { + const nextResource = preloadedResources.find(res => res.fullPath === nextPath); + if (nextResource) { + levelStack.push(nextResource); + } else { + // log.warn(`内循环:下一级(${nextPath})不存在,回退`); + levelStack.pop(); + } + } else { + levelStack.pop(); + } + } else { + const nextPath = normalizePath(currentResource.popupConfig.nextLevelOnFailure); + if (nextPath && nextPath.trim()) { + const nextResource = preloadedResources.find(res => res.fullPath === nextPath); + if (nextResource) { + levelStack.push(nextResource); + } else { + // log.warn(`内循环:下一级(${nextPath})不存在,回退`); + levelStack.pop(); + } + } else { + levelStack.pop(); + } + } + + await sleep(100); + } + + log.info(`【${currentFirstLevel.dirName}】内部流程处理完毕`); + } } - // 短暂等待后再次执行 - await sleep(imageClickDelay); + + // log.info(`===== 外循环结束:等待${taskDelay/1000}秒后开始下一次循环 =====`); + await sleep(taskDelay); } - log.info("imageClick后台任务已结束"); + log.info("imageClick后台任务结束"); return { success: true }; } -// 优化:使用预加载资源,保留所有原执行逻辑 +// ======================== 3. 识别与操作函数(精简日志版)======================== async function imageClick(preloadedResources, ra = null, specificNames = null, useNewScreenshot = false) { - // 保留原参数格式处理(兼容历史调用) if (typeof specificNames === 'string') { specificNames = [specificNames]; } const isAll = !specificNames || specificNames.length === 0; + let isAnySuccess = false; - // 遍历预处理好的资源(原targetDirs循环逻辑) for (const resource of preloadedResources) { - const { dirName, foundRegions } = resource; + const { dirName, foundRegions, popupConfig } = resource; + const { xOffset, yOffset } = popupConfig; + let hasAnyIconDetected = false; // 标记是否有图标被识别 + for (const foundRegion of foundRegions) { - // 保留原识别对象创建逻辑(使用预处理的路径) const tolerance = 1; - const iconMat = file.readImageMatSync(`${foundRegion.iconDir}/${foundRegion.iconName}`); + const iconMat = file.readImageMatSync(`${normalizePath(foundRegion.iconDir)}/${foundRegion.iconName}`); + + const { detectRegion } = popupConfig; + const defaultX = foundRegion.region.x - tolerance; + const defaultY = foundRegion.region.y - tolerance; + const defaultWidth = foundRegion.region.width + 2 * tolerance; + const defaultHeight = foundRegion.region.height + 2 * tolerance; const recognitionObject = RecognitionObject.TemplateMatch( iconMat, - foundRegion.region.x - tolerance, - foundRegion.region.y - tolerance, - foundRegion.region.width + 2 * tolerance, - foundRegion.region.height + 2 * tolerance + detectRegion?.x ?? defaultX, + detectRegion?.y ?? defaultY, + detectRegion?.width ?? defaultWidth, + detectRegion?.height ?? defaultHeight ); recognitionObject.threshold = 0.85; - // 保留原识别逻辑 const result = await recognizeImage( recognitionObject, ra, - 1000, // timeout - 500, // interval + 1000, + 500, useNewScreenshot, - dirName // iconType + dirName ); - // 保留原点击逻辑 if (result.isDetected && result.x !== 0 && result.y !== 0) { - const x = Math.round(result.x + result.width / 2); - const y = Math.round(result.y + result.height / 2); - log.info(`即将点击【${dirName}】类型下的图标:${foundRegion.iconName},位置: (${x}, ${y})`); - await click(x, y); - log.info(`点击【${dirName}】类型下的${foundRegion.iconName}成功`); - await sleep(10); - return { success: true }; - } else { - // log.info(`未发现弹窗【${dirName}】的图标:${foundRegion.iconName}`); + hasAnyIconDetected = true; + isAnySuccess = true; + const centerX = Math.round(result.x + result.width / 2); + const centerY = Math.round(result.y + result.height / 2); + const actualX = centerX + xOffset; + const actualY = centerY + yOffset; + log.info(`识别到【${dirName}】弹窗,偏移后位置(${actualX}, ${actualY})`); + + if (!popupConfig.isSpecial) { + // log.info(`点击【${dirName}】弹窗:(${actualX}, ${actualY})`); + // 新增:普通点击加循环(默认1次,0间隔,与原逻辑一致) + const clickCount = popupConfig.loopCount; + const clickDelay = popupConfig.loopDelay; + for (let i = 0; i < clickCount; i++) { + await click(actualX, actualY); // 保留原始点击逻辑 + if (i < clickCount - 1) await sleep(clickDelay); // 非最后一次加间隔 + } + } else { + switch (popupConfig.operationType) { + case "key_press": { + const targetKey = popupConfig.keyCode || "VK_SPACE"; + // 新增:key_press用循环(默认3次,1000ms间隔,与原硬编码逻辑一致) + const pressCount = popupConfig.loopCount || 3; + const pressDelay = popupConfig.loopDelay || 1000; + for (let i = 0; i < pressCount; i++) { + keyPress(targetKey); // 保留原始按键逻辑 + if (i < pressCount - 1) await sleep(pressDelay); // 非最后一次加间隔 + } + log.info(`【${dirName}】弹窗触发按键【${targetKey}】,共${pressCount}次,间隔${pressDelay}ms`); + break; + } + case "ocr_click": { + isAnySuccess = false; + const { targetTexts, xRange, yRange, timeout = 2000 } = popupConfig.ocrConfig || {}; + if (!targetTexts || !xRange || !yRange) { + log.error(`【${dirName}】弹窗OCR配置不全,跳过`); + break; + } + const ocrResults = await performOcr(targetTexts, xRange, yRange, timeout, ra); + if (ocrResults.length > 0) { + const ocrActualX = Math.round(ocrResults[0].x + ocrResults[0].width/2) + xOffset; + const ocrActualY = Math.round(ocrResults[0].y + ocrResults[0].height/2) + yOffset; + // 新增:OCR点击加循环(默认1次,0间隔,与原逻辑一致) + const ocrCount = popupConfig.loopCount; + const ocrDelay = popupConfig.loopDelay; + for (let i = 0; i < ocrCount; i++) { + await click(ocrActualX, ocrActualY); // 保留原始OCR点击逻辑 + if (i < ocrCount - 1) await sleep(ocrDelay); // 非最后一次加间隔 + } + log.info(`【${dirName}】弹窗OCR点击“${ocrResults[0].text}”:(${ocrActualX}, ${ocrActualY}),共${ocrCount}次,间隔${ocrDelay}ms`); + isAnySuccess = true; + } else { + log.warn(`【${dirName}】弹窗OCR未识别到文本`); + } + break; + } + case "key_mouse_script": { + try { + const scriptPath = normalizePath(popupConfig.scriptPath || ""); + if (!scriptPath) { + log.error(`【${dirName}】弹窗未配置键鼠脚本路径,跳过执行`); + isAnySuccess = false; + break; + } + if (!fileExists(scriptPath)) { + log.error(`【${dirName}】弹窗键鼠脚本不存在:${scriptPath}`); + isAnySuccess = false; + break; + } + // 新增:键鼠脚本加循环(默认1次,0间隔,与原逻辑一致) + const scriptCount = popupConfig.loopCount; + const scriptDelay = popupConfig.loopDelay; + for (let i = 0; i < scriptCount; i++) { + await keyMouseScript.runFile(scriptPath); // 保留原始脚本执行逻辑 + if (i < scriptCount - 1) await sleep(scriptDelay); // 非最后一次加间隔 + } + log.info(`【${dirName}】弹窗键鼠脚本执行完成,共${scriptCount}次,间隔${scriptDelay}ms`); + isAnySuccess = true; + } catch (error) { + log.error(`【${dirName}】弹窗键鼠脚本执行失败:${error.message}`); + isAnySuccess = false; + } + break; + } + default: + log.error(`【${dirName}】弹窗未知操作类型:${popupConfig.operationType},默认执行点击`); + // 新增:默认操作加循环(默认1次,0间隔,与原逻辑一致) + const defaultCount = popupConfig.loopCount; + const defaultDelay = popupConfig.loopDelay; + for (let i = 0; i < defaultCount; i++) { + await click(actualX, actualY); // 保留原始默认点击逻辑 + if (i < defaultCount - 1) await sleep(defaultDelay); // 非最后一次加间隔 + } + isAnySuccess = true; + } + } } } + + // 仅在该弹窗无任何图标识别时输出警告 + if (!hasAnyIconDetected && !isAnySuccess) { + // log.warn(`【${dirName}】弹窗未发现任何有效图标`); + } + await sleep(10); } - // 所有目标处理完毕仍未成功(保留原返回逻辑) - return { success: false }; + return { success: isAnySuccess, message: isAnySuccess ? "弹窗识别操作成功" : "未识别到弹窗" }; } diff --git a/repo/js/背包材料统计/main.js b/repo/js/背包材料统计/main.js index a96f2eb4d..c2b189502 100644 --- a/repo/js/背包材料统计/main.js +++ b/repo/js/背包材料统计/main.js @@ -111,7 +111,6 @@ function parseMonsterMaterials() { } materialToMonsters[mat].add(monsterName); }); - log.debug(`${CONSTANTS.LOG_MODULES.MONSTER}解析怪物材料:${monsterName} -> [${materials.join(', ')}]`); } }); } catch (error) { @@ -155,7 +154,11 @@ function getSelectedMaterialCategories() { return acc; }, {}); - const finalSettings = { ...initialSettings, ...settings }; + const finalSettings = Object.keys(initialSettings).reduce((acc, key) => { + // 若settings中有该键则使用其值,否则用默认的false(确保只处理material_mapping中的键) + acc[key] = settings.hasOwnProperty(key) ? settings[key] : initialSettings[key]; + return acc; + }, {}); return Object.keys(finalSettings) .filter(key => key !== "unselected") @@ -294,6 +297,7 @@ function readMaterialCD() { const materialCDCategories = {}; for (const filePath of materialFilePaths) { + if (state.cancelRequested) break; const content = file.readTextSync(filePath); if (!content) { log.error(`${CONSTANTS.LOG_MODULES.CD}加载文件失败:${filePath}`); @@ -1156,7 +1160,6 @@ function classifyNormalPathFiles(pathingDir, targetResourceNames, lowCountMateri if (pathEntries.length > 0) { log.info(`${CONSTANTS.LOG_MODULES.PATH}\n===== 匹配到的材料路径列表 =====`); pathEntries.forEach((entry, index) => { - log.info(`${index + 1}. 材料:${entry.resourceName},路径:${entry.path}`); }); log.info(`=================================\n`); } else { @@ -1355,6 +1358,11 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个 =========================================\n\n`; writeContentToFile(summaryPath, content); log.info(`${CONSTANTS.LOG_MODULES.RECORD}最终汇总已记录至 ${summaryPath}`); + // ============================================== + // 新增:汇总后强制触发结束指令,确保程序终止 + // ============================================== + state.completed = true; // 标记任务完全完成 + state.cancelRequested = true; // 终止所有后台任务(如图像点击、OCR) } // ============================================== @@ -1387,11 +1395,15 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个 } // 关键补充:等待超量名单生成(由filterLowCountMaterials更新) let waitTimes = 0; - while (excessMaterialNames.length === 0 && waitTimes < 300) { + while (excessMaterialNames.length === 0 && !state.cancelRequested && waitTimes < 100) { await sleep(1000); // 每1秒查一次 waitTimes++; } - + // 若收到终止信号,直接退出OCR任务(不再执行后续逻辑) + if (state.cancelRequested) { + log.info(`${CONSTANTS.LOG_MODULES.MAIN}OCR任务收到终止信号,已退出`); + return; + } // 现在过滤才有效(确保excessMaterialNames已生成) allTargetTexts = allTargetTexts.filter(name => !excessMaterialNames.includes(name)); log.info(`OCR最终目标文本(已过滤超量):${allTargetTexts.join('、')}`); diff --git a/repo/js/背包材料统计/manifest.json b/repo/js/背包材料统计/manifest.json index eeca672ea..ece08cf61 100644 --- a/repo/js/背包材料统计/manifest.json +++ b/repo/js/背包材料统计/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "背包统计采集系统", - "version": "2.52", + "version": "2.53", "bgi_version": "0.44.8", "description": "可统计背包养成道具、部分食物、素材的数量;根据设定数量、根据材料刷新CD执行挖矿、采集、刷怪等的路径。优势:\n+ 1. 自动判断材料CD,不需要管材料CD有没有好;\n+ 2. 可以随意添加路径,能自动排除低效、无效路径;\n+ 3. 有独立名单识别,不会交互路边的npc或是神像;可自定义识别名单,具体方法看【问题解答】增减识别名单\n+ 4. 有实时的弹窗模块,提供了常见的几种:路边信件、过期物品、月卡、调查;\n+ 5. 可识别爆满的路径材料,自动屏蔽;更多详细内容查看readme.md", "saved_files": [ diff --git a/repo/js/背包材料统计/settings.json b/repo/js/背包材料统计/settings.json index 10b129e01..a1dca168c 100644 --- a/repo/js/背包材料统计/settings.json +++ b/repo/js/背包材料统计/settings.json @@ -102,12 +102,27 @@ { "name": "PopupClickDelay", "type": "input-text", - "label": "如 过期物品,信件,自定义文件夹名。注意分隔符和文件夹格式\n----------------------------------\n弹窗循环间隔(默认:5 秒)" + "label": "如 过期物品,信件,自定义文件夹名。注意文件夹结构\n----------------------------------\n弹窗循环间隔(默认:15 秒)" }, { "name": "CDCategories", "type": "input-text", - "label": "----------------------------------\n\n采用的CD分类(默认:全部)" + "label": "====================\n\n采用的CD分类(默认:全部) 举例:采集,怪物,木材" + }, + { + "name": "PickCategories", + "type": "input-text", + "label": "根据CD分类来加载路径文件,具体看materialsCD目录\n====================\n\n采用的识别名单(默认:全部) 举例:交互,采集,宝箱" + }, + { + "name": "ExceedCount", + "type": "input-text", + "label": "根据拾取分类来加载OCR名单,具体看targetText目录\n----------------------------------\n\n超量阈值(默认:5000)超量的路径材料将不拾取" + }, + { + "name": "PageScrollDistance", + "type": "input-text", + "label": "====================\n拖动距离:(默认711像素点)" }, { "name": "HoldX", @@ -118,10 +133,5 @@ "name": "HoldY", "type": "input-text", "label": "------------------------\n翻页拖动点Y坐标:0~1080(默认750)" - }, - { - "name": "PageScrollDistance", - "type": "input-text", - "label": "------------------------\n拖动距离:(默认711像素点)" } -] \ No newline at end of file +]