diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/README.md" "b/repo/js/\351\223\201\345\214\240\351\223\272/README.md" index d1ba52275e..bfac886697 100644 --- "a/repo/js/\351\223\201\345\214\240\351\223\272/README.md" +++ "b/repo/js/\351\223\201\345\214\240\351\223\272/README.md" @@ -53,9 +53,10 @@ BGI 0.48.2 版本 ```json { "smithyName": "枫丹铁匠铺", - "ore": "水晶块", - "secondaryOre": "紫晶块", - "tertiaryOre": "萃凝晶", + "CondessenceCrystal": "萃凝晶", + "CrystalChunk": "水晶块", + "AmethystLump": "紫晶块", + "RainbowdropCrystal": "虹滴晶", "notice": true, "forgedOrNot": "是", "model": "模式一" @@ -68,9 +69,13 @@ BGI 0.48.2 版本 - 水晶块 - 紫晶块 - 萃凝晶 +- 虹滴晶 -**扩展矿石配置**: -如需使用"星银矿石"、"白铁块"或"铁块",请在 `settings.json` 中取消对应矿石前的注释(`//`),保存后即可生效。 +**矿石优先级说明**: +- 每种矿石的优先级可在 `settings.json` 中配置,数值范围为 `0-4`。 +- **优先级为 0**:表示不使用该矿石,脚本会自动排除。 +- **优先级为 1-4**:数值越小,优先级越高。 +- **优先级相同时**:按照矿石的默认顺序(萃凝晶 > 水晶块 > 紫晶块 > 虹滴晶)决定使用顺序。 ## ⚙️ 功能详解 @@ -87,6 +92,7 @@ BGI 0.48.2 版本 - **智能兜底**:自动模式识别失败时,自动切换到手动优先级模式 - **通知与日志**:支持桌面通知和详细日志,便于追踪脚本执行状态 - **异常处理**:多次识别失败、材料不足等情况均有详细提示和自动处理 +- **优先级排序**:脚本会根据用户配置的矿石优先级进行排序,优先级为 0 的矿石会被自动排除。 ## 📋 使用注意事项 @@ -94,6 +100,7 @@ BGI 0.48.2 版本 2. **窗口状态**:运行脚本时请保持游戏窗口处于前台且未被遮挡 3. **权限设置**:确保已授予脚本必要的系统权限 3. **权限设置**:部分模块由于使用了快捷键,请确保快捷键为默认值。例如纳塔铁匠铺以及仅领取锻造前检查,使用地图快捷键"M";模式一矿物识别部分,使用背包快捷键"B"。因为模式一调用了OCR,数字识别部分可能出现错误 +4. **矿石优先级配置**:请确保至少有一种矿石的优先级大于 0,否则脚本将停止运行并提示错误。 ## ❓ 常见问题 @@ -114,6 +121,12 @@ Q:OCR识别矿石数量不准确怎么办? ## 📜 更新日志 +### 2.4.0`(2025-11-29)` +- **优化**:纳塔铁匠铺路线可能出现跑过头的问题 +- **新增**: + 1. 支持月国铁匠铺,矿石虹滴晶的使用 + 2. 重构全新的矿石使用排序方式 + 3. 选择矿石排序为0时,可以排除对应矿石 ### 2.3.1`(2025-08-30)` - **修复**: @@ -163,7 +176,9 @@ Q:OCR识别矿石数量不准确怎么办? --- -**最后更新**:2025 年 8 月 30 日 +**最后更新**:2025 年 11 月 29 日 **维护者**:@呱呱z -> 💡 提示:建议定期检查更新以获取最佳体验和最新功能 \ No newline at end of file +> 💡 提示:建议定期检查更新以获取最佳体验和最新功能 + +**矿石优先级说明**: \ No newline at end of file diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\346\214\252\345\276\267\345\215\241\350\216\261\351\223\201\345\214\240\351\223\272.json" "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\346\214\252\345\276\267\345\215\241\350\216\261\351\223\201\345\214\240\351\223\272.json" new file mode 100644 index 0000000000..3bb756dd5c --- /dev/null +++ "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\346\214\252\345\276\267\345\215\241\350\216\261\351\223\201\345\214\240\351\223\272.json" @@ -0,0 +1,67 @@ +{ + "info": { + "authors": [ + { + "links": "", + "name": "呱呱z" + } + ], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1759332476359, + "map_match_method": "", + "map_name": "Teyvat", + "name": "挪德卡莱铁匠铺", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "action_params": "", + "id": 1, + "move_mode": "walk", + "type": "teleport", + "x": 9458.0341796875, + "y": 1660.6650390625 + }, + { + "action": "", + "action_params": "", + "id": 2, + "move_mode": "fly", + "type": "path", + "x": 9448.4970703125, + "y": 1651.05859375 + }, + { + "action": "", + "action_params": "", + "id": 3, + "move_mode": "run", + "type": "path", + "x": 9419.3515625, + "y": 1640.990234375 + }, + { + "action": "", + "action_params": "", + "id": 4, + "move_mode": "run", + "type": "path", + "x": 9412.615234375, + "y": 1642.33447265625 + }, + { + "action": "", + "action_params": "", + "id": 5, + "move_mode": "dash", + "type": "target", + "x": 9410.013671875, + "y": 1643.15771484375 + } + ] +} \ No newline at end of file diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\347\272\263\345\241\224\351\223\201\345\214\240\351\223\272.json" "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\347\272\263\345\241\224\351\223\201\345\214\240\351\223\272.json" index e03c069f14..5489850625 100644 --- "a/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\347\272\263\345\241\224\351\223\201\345\214\240\351\223\272.json" +++ "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/Pathing/\347\272\263\345\241\224\351\223\201\345\214\240\351\223\272.json" @@ -1,40 +1,49 @@ { - "info": { - "authors": [ - { - "links": "", - "name": "呱呱z" - } - ], - "bgi_version": "0.48.2", - "description": "", - "enable_monster_loot_split": false, - "last_modified_time": 1756367298175, - "map_match_method": "", - "map_name": "Teyvat", - "name": "纳塔铁匠铺", - "tags": [], - "type": "collect", - "version": "1.0" + "info": { + "authors": [ + { + "links": "", + "name": "呱呱z" + } + ], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1759332274135, + "map_match_method": "", + "map_name": "Teyvat", + "name": "纳塔铁匠铺", + "tags": [], + "type": "collect", + "version": "1.1" + }, + "positions": [ + { + "action": "", + "action_params": "", + "id": 1, + "move_mode": "run", + "type": "path", + "x": 9025.548828125, + "y": -1913.60009765625 }, - "positions": [ - { - "action": "", - "action_params": "", - "id": 1, - "move_mode": "walk", - "type": "path", - "x": 9025.55078125, - "y": -1913.60107421875 - }, - { - "action": "", - "action_params": "", - "id": 2, - "move_mode": "dash", - "type": "target", - "x": 9036.7490234375, - "y": -1939.0771484375 - } - ] -} + { + "action": "", + "action_params": "", + "id": 2, + "move_mode": "run", + "type": "path", + "x": 9033.8525390625, + "y": -1938.1494140625 + }, + { + "action": "", + "action_params": "", + "id": 3, + "move_mode": "dash", + "type": "target", + "x": 9035.6044921875, + "y": -1938.7529296875 + } + ] +} \ No newline at end of file diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/assets/Picture/RainbowdropCrystal.png" "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/Picture/RainbowdropCrystal.png" new file mode 100644 index 0000000000..08fb393a1d Binary files /dev/null and "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/Picture/RainbowdropCrystal.png" differ diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/assets/RecognitionObject/ItemImage/RainbowdropCrystal.png" "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/RecognitionObject/ItemImage/RainbowdropCrystal.png" new file mode 100644 index 0000000000..ad55bbf683 Binary files /dev/null and "b/repo/js/\351\223\201\345\214\240\351\223\272/assets/RecognitionObject/ItemImage/RainbowdropCrystal.png" differ diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/main.js" "b/repo/js/\351\223\201\345\214\240\351\223\272/main.js" index bb754b869d..859078735f 100644 --- "a/repo/js/\351\223\201\345\214\240\351\223\272/main.js" +++ "b/repo/js/\351\223\201\345\214\240\351\223\272/main.js" @@ -1,10 +1,13 @@ /*********************** 配置与常量 ***********************/ // 用户配置 -let smithyName = settings.smithyName || "枫丹铁匠铺"; // 铁匠铺地区 -let primaryOre = settings.ore || "水晶块"; // 主选矿石 -let secondaryOre = settings.secondaryOre || "萃凝晶"; // 备选矿石1 -let tertiaryOre = settings.tertiaryOre || "紫晶块"; // 备选矿石2 +let smithyName = settings.smithyName ?? "枫丹铁匠铺"; // 铁匠铺地区 +let CondessenceCrystal = settings.CondessenceCrystal ?? "1"; // 萃凝晶 +let CrystalChunk = settings.CrystalChunk ?? "2"; // 水晶块 +let AmethystLump = settings.AmethystLump ?? "3"; // 紫晶块 +let RainbowdropCrystal = settings.RainbowdropCrystal ?? "4"; // 虹滴晶 + + let notice = settings.notice ?? false; // 通知状态 let forgedOrNot = (settings.forgedOrNot && settings.forgedOrNot.trim() !== "") ? settings.forgedOrNot : "是"; // 是否锻造 let model = settings.model || "模式一"; // 模式选择 @@ -12,8 +15,10 @@ let model = settings.model || "模式一"; // 模式选 // 矿石图像与中文名称映射 const ingredientImageMap = { 萃凝晶: "assets/Picture/CondessenceCrystal.png", - 紫晶块: "assets/Picture/AmethystLump.png", 水晶块: "assets/Picture/CrystalChunk.png", + 紫晶块: "assets/Picture/AmethystLump.png", + 虹滴晶: "assets/Picture/RainbowdropCrystal.png", + 星银矿石: "assets/Picture/Starsilver.png", 白铁块: "assets/Picture/WhiteIronChunk.png", 铁块: "assets/Picture/IronChunk.png", @@ -21,8 +26,10 @@ const ingredientImageMap = { const OreChineseMap = { 萃凝晶: "萃凝晶", - 紫晶块: "紫晶块", 水晶块: "水晶块", + 紫晶块: "紫晶块", + 虹滴晶: "虹滴晶", + 星银矿石: "星银矿石", 白铁块: "白铁块", 铁块: "铁块", @@ -34,7 +41,8 @@ const smithyMap = { "稻妻铁匠铺": { x: -4402, y: -3052, country: "稻妻" }, "须弥铁匠铺": { x: 2786, y: -503, country: "须弥" }, "枫丹铁匠铺": { x: 4507, y: 3630, country: "枫丹" }, - "纳塔铁匠铺": { x: 9085, y: -1964, country: "纳塔" } + "纳塔铁匠铺": { x: 9085, y: -1964, country: "纳塔" }, + "挪德卡莱铁匠铺": { x: 9458, y: 1660, country: "挪德卡莱" } }; // 模板识别对象 @@ -60,30 +68,41 @@ const ForgingInterFaceRo = RecognitionObject.TemplateMatch( //锻造界面物品图标-未使用这部分代码 const CondessenceCrystalForgeRo = RecognitionObject.TemplateMatch( file.ReadImageMatSync("Assets/Picture/CondessenceCrystal.png"), - 0, 870, 1920, 210 + 40, 200, 770, 720 ); // 【萃凝晶】 const AmethystLumpForgeRo = RecognitionObject.TemplateMatch( file.ReadImageMatSync("Assets/Picture/AmethystLump.png"), - 0, 870, 1920, 210 + 40, 200, 770, 720 ); // 【紫晶块】 const CrystalChunkForgeRo = RecognitionObject.TemplateMatch( file.ReadImageMatSync("Assets/Picture/CrystalChunk.png"), - 0, 870, 1920, 210 + 40, 200, 770, 720 ); // 【水晶块】 +const RainbowdropCrystalForgeRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync("Assets/Picture/RainbowdropCrystal.png"), + 40, 200, 770, 720 +); // 【虹滴晶】 + + //背包界面物品图标 const CondessenceCrystalRo = RecognitionObject.TemplateMatch( file.ReadImageMatSync("Assets/RecognitionObject/ItemImage/CondessenceCrystal.png"), - 115, 115, 1270, 625 + 115, 115, 1300, 955 ); // 【萃凝晶】 -const AmethystLumpRo = RecognitionObject.TemplateMatch( - file.ReadImageMatSync("Assets/RecognitionObject/ItemImage/AmethystLump.png"), - 115, 115, 1165, 510 -); // 【紫晶块】 const CrystalChunkRo = RecognitionObject.TemplateMatch( file.ReadImageMatSync("Assets/RecognitionObject/ItemImage/CrystalChunk.png"), - 115, 115, 1165, 510 + 115, 115, 1300, 955 ); // 【水晶块】 +const AmethystLumpRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync("Assets/RecognitionObject/ItemImage/AmethystLump.png"), + 115, 115, 1300, 955 +); // 【紫晶块】 +const RainbowdropCrystalRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync("Assets/RecognitionObject/ItemImage/RainbowdropCrystal.png"), + 115, 115, 1300, 955 +); // 【虹滴晶】 + //对话框图标 const ForgeRo = RecognitionObject.TemplateMatch( @@ -110,17 +129,21 @@ const MapForgeRo = RecognitionObject.TemplateMatch( 0, 0, 400, 625 ); // 地图左上角【锻造】图标 -// 计算矿物图标的坐标(行列排列) -const rows = [1, 2, 3]; -const cols = [1, 2, 3, 4, 5]; -const gridCoordinates = []; -for (const row of rows) { - for (const col of cols) { - const ProcessingX = Math.round(150 + (col - 1) * 145); - const ProcessingY = Math.round(230 + (row - 1) * 170); - gridCoordinates.push({ row, col, x: ProcessingX, y: ProcessingY }); - } -} + +// 定义优先级配置和昵称顺序。读取 settings.json 里的矿石优先级配置,转为数字 +const orePriorityConfig = { + "萃凝晶": Number(CondessenceCrystal) ?? 0, + "水晶块": Number(CrystalChunk) ?? 0, + "紫晶块": Number(AmethystLump) ?? 0, + "虹滴晶": Number(RainbowdropCrystal) ?? 0 +}; +//定义昵称顺序,用于优先级相同时的排序。 +const nicknameOrder = ["萃凝晶", "水晶块", "紫晶块", "虹滴晶"]; + + + + + /*********************** 工具函数 ***********************/ @@ -140,27 +163,28 @@ for (const row of rows) { async function findAndInteract(target, options = {}) { // 合并默认选项 const { - useClick = false, - timeout = 3000, - interval = 300, - clickCenter = true, - postClickDelay = 300, - singleAttempt = false + useClick = false, // 是否执行点击操作 + timeout = 3000, // 识别超时时间(毫秒) + interval = 300, // 重试间隔(毫秒) + clickCenter = true, // 是否点击中心点 + postClickDelay = 300, // 点击后等待时间(毫秒) + singleAttempt = false, // 是否仅尝试一次 + silentOnFail = false // 失败时不报错(静默模式) } = options || {}; const startTime = Date.now(); let attemptCount = 0; let lastError = null; - // 结果对象结构 + // 模板————结果对象结构 const resultTemplate = { - success: false, - position: null, // {x, y} 原始位置 - clickPosition: null, // {x, y} 点击位置 - dimensions: null, // {width, height} - attempts: 0, - elapsed: 0, - error: null + success: false, // 操作是否成功 + position: null, // 识别到的原始坐标 {x, y} + clickPosition: null, // 实际点击的坐标 {x, y}(可能包含偏移) + dimensions: null, // 识别区域的尺寸 {width, height} + attempts: 0, // 尝试次数 + elapsed: 0, // 耗时(毫秒) + error: null // 错误信息 }; // 主识别循环 @@ -176,7 +200,7 @@ async function findAndInteract(target, options = {}) { if (found?.isExist?.() === true) { // 构建成功结果 const result = { - ...resultTemplate, + ...resultTemplate, // 复制模板所有属性 success: true, position: { x: found.x, y: found.y }, dimensions: { width: found.width, height: found.height }, @@ -184,17 +208,21 @@ async function findAndInteract(target, options = {}) { elapsed: Date.now() - startTime }; - //log.info(`✅ 识别成功 | 位置: (${found.x}, ${found.y}) | 尺寸: ${found.width}x${found.height} | 尝试: ${attemptCount}次`); + //log.info(`✅ 识别成功 | 左上角位置: (${found.x}, ${found.y}) | 尺寸: ${found.width}x${found.height} | 尝试: ${attemptCount}次`); // 3. 处理点击交互 if (useClick) { // 计算点击位置 - const clickPos = clickCenter - ? { + const clickPos = clickCenter ?// 条件判断:是否点击中心点 + // 如果 clickCenter 为 true,点击元素的中心点 + { x: Math.round(found.x + found.width / 2), y: Math.round(found.y + found.height / 2) - } - : { x: Math.round(found.x), y: Math.round(found.y) }; + } : + // 如果 clickCenter 为 false,点击元素的左上角 + { + x: Math.round(found.x), y: Math.round(found.y) + }; result.clickPosition = clickPos; @@ -221,118 +249,235 @@ async function findAndInteract(target, options = {}) { log.debug(`⏳ 识别尝试中... 次数: ${attemptCount}, 已用时: ${Date.now() - startTime}ms`); } } catch (error) { - lastError = error; - log.error(`识别异常,错误: ${error.message}`); + if (!silentOnFail) { + lastError = error; + log.error(`识别异常,错误: ${error.message}`); + } } finally { // 4. 资源清理 if (gameRegion?.dispose) { - try { gameRegion.dispose(); } catch (e) { } + try { + gameRegion.dispose(); + } catch (error) { + log.error(`释放游戏区域资源时出错: ${error.message}`); + } } } // 退出条件 if (singleAttempt || Date.now() - startTime >= timeout) { - log.warn(`识别失败 | 尝试: ${attemptCount}次,未能识别到目标图像`); - return { - ...resultTemplate, - attempts: attemptCount, - elapsed: Date.now() - startTime, - error: lastError?.message - }, - false; + if (!silentOnFail) { + log.warn(`模板匹配失败 | 尝试: ${attemptCount}次,未能识别到目标图像`); + } + return false; } await sleep(interval); } } -// 图像识别函数 -function recognizeImage(imagePath, x, y, searchWidth, searchHeight) { +// 通知日志:使用矿石提示 +function determineOre(oreType) { + let message = `将使用 ${OreChineseMap[oreType]} 锻造矿石`; + log.info(message); + return message; +} + +// 2.4.0*新增:过滤并排序列表函数 +/** + * 在锻造逻辑前,生成一个按优先级排序的矿石列表,数字为 0 的矿石不参与锻造: + * @param {Object} priorityConfig - 矿石优先级配置对象,键为矿石名称,值为优先级数字 // priorityConfig: { oreName: priorityNumber, ... } + * @param {Array} tieBreakOrder - 矿石昵称顺序数组,用于同优先级时的排序 // tieBreakOrder: 当优先级相同或重复时,按此数组的顺序决定先后(越靠前优先级越高) + * @returns {Array} 返回按优先级排序且去重的矿石名称数组 + */ +function getSortedOresByPriority(priorityConfig, tieBreakOrder = []) { try { - let template = file.ReadImageMatSync(imagePath); - let recognitionObject = RecognitionObject.TemplateMatch(template, x, y, searchWidth, searchHeight); - recognitionObject.threshold = 0.85; - recognitionObject.Use3Channels = true; - let ro = captureGameRegion(); - let result = ro.find(recognitionObject); - ro.dispose(); - return result.isExist() ? result : null; - } catch (error) { - if (notice) { - notification.error(`图像识别失败,路径: ${imagePath}, 错误: ${error.message}`); - } else { - log.error(`图像识别失败,路径: ${imagePath}, 错误: ${error.message}`); + if (!priorityConfig || typeof priorityConfig !== 'object') { + log.error('优先级配置无效'); + return []; } - return null; + // 调试日志:显示原始配置 + //log.info(`原始优先级配置: ${JSON.stringify(priorityConfig)}`); + const entries = Object.entries(priorityConfig || {}).filter(([_, priority]) => Number(priority) > 0); + // 调试日志:显示过滤后的条目 + //log.info(`[排序调试] 过滤后的矿石条目: ${JSON.stringify(entries)}`); + // 去重并稳定排序:先按 priority 升序,同优先级时按 tieBreakOrder 的索引升序 + entries.sort((a, b) => { + const pa = Number(a[1]); + const pb = Number(b[1]); + // 调试日志:显示每次比较 + //log.info(`[排序调试] 比较 ${a[0]}=${pa} vs ${b[0]}=${pb}`); + if (pb !== pa) + return pa - pb; // 数字小的排前面(升序) + const ai = tieBreakOrder.indexOf(a[0]); + const bi = tieBreakOrder.indexOf(b[0]); + const aIdx = ai === -1 ? Number.MAX_SAFE_INTEGER : ai; + const bIdx = bi === -1 ? Number.MAX_SAFE_INTEGER : bi; + return aIdx - bIdx; + }); + // 调试日志:显示排序后的条目 + //log.info(`[排序调试] 排序后的矿石条目: ${JSON.stringify(entries)}`); + // 返回去重后的名称列表(保持排序) + const seen = new Set(); + const result = []; + for (const [name] of entries) { + if (!seen.has(name)) { + seen.add(name); + result.push(name); + } + } + // 调试日志:显示最终结果 + log.info(`矿石使用排序: ${JSON.stringify(result)}`); + return result; + } catch (error) { + log.error(`生成排序矿石列表失败,错误: ${error.message}`); + return []; } } -// 检查是否需要跳过该矿石(若已属于备选中) -function shouldSkipOre(targetOre, compareOres) { - return compareOres.includes(targetOre); -} -// 通知日志:使用矿石提示 -function determineOre(oreType) { - let message = `将使用 ${OreChineseMap[oreType]} 锻造矿石`; - log.info(message); - return message; +/*********************** 封装函数 ***********************/ + +// 2.4.0*新增:封装启用通知与否日志记录 +function logMessage(level, message) { + if (notice) { + notification[level](message); + } else { + log[level](message); + } } -/*********************** 主逻辑函数 ***********************/ +// 2.4.0*新增:【背包界面】封装背包界面打开与检测 +async function openInventory() { + let maxAttempts = 3; // 最大尝试次数,防止无限循环 -// 模式一:自动识别背包中数量最多的矿石 -async function getMaxOreType() { try { - //开启背包 + // ========== 第一阶段:确保打开背包界面 ========== + log.info("开始尝试打开背包界面..."); await sleep(1000); - await genshin.returnMainUi(); - keyPress("B"); await sleep(1000); - //【背包】界面检测 - if (!await findAndInteract(InventoryInterFaceRo, { - singleAttempt: true - })) { - log.info("未检测到背包界面,尝试返回主界面并打开背包"); + + for (let bagAttempt = 1; bagAttempt <= maxAttempts; bagAttempt++) { + //log.info(`尝试打开背包 (第 ${bagAttempt}/${maxAttempts} 次)`); + + // 先执行一次标准的打开背包操作 await genshin.returnMainUi(); keyPress("B"); await sleep(1000); - } else { - log.info("检测到处于背包界面"); + + // 检测是否成功进入背包界面 + if (await findAndInteract(InventoryInterFaceRo, { singleAttempt: true })) { + log.info("✓ 成功进入背包界面"); + break; // 成功,跳出循环 + } + + //log.warn(`第 ${bagAttempt} 次打开背包失败`); + + // 如果这是最后一次尝试,直接失败返回 + if (bagAttempt === maxAttempts) { + log.error(`无法打开背包界面,已重试 ${maxAttempts} 次`); + return false; + } } - // 【材料】界面检测,多次尝试,避免过期道具卡弹窗 - let maxAttempts = 10; // 最大尝试次数,防止无限循环 - let attempts = 0; + // ========== 第二阶段:切换到材料标签页 ========== + log.info("准备切换到材料界面..."); - while (attempts < maxAttempts) { - if (await findAndInteract(MaterialsFaceRo, - { - singleAttempt: true - })) { - log.info("已经处于材料界面,准备识别矿物数量"); - break; // 成功进入界面,退出循环 - } else { - log.info("未处于材料界面,准备点击材料界面图标"); - await findAndInteract(DisabledMaterialsFaceRo, - { - useClick: true - }); - await sleep(600); // 等待界面响应 - attempts++; + for (let materialAttempt = 1; materialAttempt <= maxAttempts; materialAttempt++) { + //log.info(`尝试进入材料界面 (第 ${materialAttempt}/${maxAttempts} 次)`); + + // 先检测是否已经在材料界面 + if (await findAndInteract(MaterialsFaceRo, { singleAttempt: true })) { + log.info("✓ 已处于材料界面"); + return true; // 完全成功 + } + + // 点击材料标签页图标 + //log.info("点击材料标签页图标..."); + await findAndInteract(DisabledMaterialsFaceRo, { useClick: true }); + await sleep(600); // 等待界面响应 + + // 检测点击后是否成功切换 + if (await findAndInteract(MaterialsFaceRo, { singleAttempt: true })) { + log.info("✓ 成功切换到材料界面"); + return true; // 完全成功 + } + + //log.warn(`第 ${materialAttempt} 次切换材料界面失败`); + + // 最后一次尝试也失败 + if (materialAttempt === maxAttempts) { + log.error("多次尝试后仍未能进入材料界面,请检查界面状态"); + return false; } } - if (attempts === maxAttempts) { - log.error("多次尝试后仍未能进入材料界面,请检查界面状态或操作逻辑"); - } else { - log.info("成功进入材料界面"); + } catch (error) { + logMessage('error', `打开背包界面失败,错误: ${error.message}`); + return false; + } +} + +/*********************** 主逻辑函数 ***********************/ + +// 2.4.0*新增:【配置检查】模块:当所有矿石优先级均为 0 时,停止脚本并通知用户 +async function configurationCheck() { + // 详细的所有矿石优先级为0的检查 + //log.info(`[配置检查] 开始检查矿石优先级配置`); + try { + // 2.4.0*新增:当所有矿石优先级均为 0 时,停止脚本并通知用户 + const allOrePriorities = Object.values(orePriorityConfig || {}).map(v => Number(v) || 0); + const allZero = allOrePriorities.length > 0 && allOrePriorities.every(v => v === 0); + if (allZero) { + const msg = "所有矿石优先级均为0或无效 ,已停止脚本。请在设置中至少启用一种矿石。"; + logMessage('error', msg); + return false; // 配置无效,停止脚本 + } + return true; + } catch (error) { + logMessage('error', `[配置检查] 失败,错误: ${error.message}`); + return false; // 出现错误,停止脚本 + } +} + + + + +// 模式一:自动识别背包中数量最多的矿石 +async function getMaxOreType() { + try { + + if (!await openInventory()) { + return false; // 无法打开背包界面,停止操作 } const oreResults = [ - { name: "萃凝晶", ro: CondessenceCrystalRo }, + { name: "水晶块", ro: CrystalChunkRo }, { name: "紫晶块", ro: AmethystLumpRo }, - { name: "水晶块", ro: CrystalChunkRo } + { name: "萃凝晶", ro: CondessenceCrystalRo }, + { name: "虹滴晶", ro: RainbowdropCrystalRo } ]; + + // 定义日志收集对象 + const priorityLog = []; // 优先级检查日志 + // 过滤掉优先级为0的矿石 + //const validOres = oreResults.filter(ore => Number(orePriorityConfig[ore.name]) > 0); + const validOres = oreResults.filter(ore => { + const priority = Number(orePriorityConfig[ore.name]); + const isValid = priority > 0; + // 收集优先级检查日志 + priorityLog.push(`矿石 ${ore.name} 优先级: ${priority}, 是否使用对应矿: ${isValid}`); + //log.debug(`矿石 ${ore.name} 优先级: ${priority}, 是否有效: ${isValid}`); + return isValid; + + }); + // 在过滤完成后一次性输出所有日志 + if (priorityLog.length > 0) { + log.info(`矿石优先级检查详情:\n${priorityLog.join('\n')}`); + } + let maxOre = null; let maxCount = 0; - for (const ore of oreResults) { + // 定义日志收集对象 + const quantityLog = []; // 数量识别日志 + + for (const ore of validOres) { const result = await findAndInteract(ore.ro, { useClick: true, timeout: 5000, @@ -346,10 +491,11 @@ async function getMaxOreType() { let resList = ro.findMulti(RecognitionObject.ocr(ocrX, ocrY, 130, 55)); ro.dispose(); let oreNum = 0; - if (resList.count > 0) { + if (resList && resList.count > 0 && resList[0]?.text) { let text = resList[0].text.replace(/[^\d]/g, ""); oreNum = parseInt(text, 10) || 0; - log.info(`识别到 ${OreChineseMap[ore.name]} 数量: ${oreNum}`); + quantityLog.push(`识别到 ${OreChineseMap[ore.name]} 数量: ${oreNum}`); + //log.info(`识别到 ${OreChineseMap[ore.name]} 数量: ${oreNum}`); } if (oreNum > maxCount) { maxCount = oreNum; @@ -363,18 +509,20 @@ async function getMaxOreType() { */ } } + + // 数量识别日志 + if (quantityLog.length > 0) { + log.info(`矿石数量识别:\n${quantityLog.join('\n')}`); + } return maxOre ? { name: maxOre, count: maxCount } : null; // 修改返回值 + } catch (error) { - if (notice) { - notification.error(`自动识别背包中数量最多的矿石失败,错误: ${error.message}`); - } else { - log.error(`识自动识别背包中数量最多的矿石失败,错误: ${error.message}`); - } + logMessage('error', `自动识别背包中数量最多的矿石失败,错误: ${error.message}`); return null; } } -// 自动前往铁匠铺 +// 【路径函数】自动前往铁匠铺 async function autoSmithy(smithyName) { await genshin.returnMainUi(); await sleep(1000); @@ -391,120 +539,121 @@ async function autoSmithy(smithyName) { } let filePath = `assets/Pathing/${smithyName}.json`; await pathingScript.runFile(filePath); - if (notice) { - notification.send(`已抵达 ${smithyName}`); - } else { - log.info(`已抵达 ${smithyName}`); - } } catch (error) { - if (notice) { - notification.error(`执行 ${smithyName} 路径时发生错误: ${error.toString()}`); - } else { - log.error(`执行 ${smithyName} 路径时发生错误: ${error.toString()}`); - } + logMessage('error', `执行 ${smithyName} 路径时发生错误: ${error.toString()}`); + return false; // 出现错误,停止脚本 } + if (notice) { + notification.send(`已抵达 ${smithyName}`); + } else { + log.info(`已抵达 ${smithyName}`); + } + return true; // 成功抵达 } -// 尝试识别并锻造矿石 -async function tryForgeOre(oreType, skipCheckOres = []) { - // 若矿石在跳过列表中则直接返回 - if (shouldSkipOre(oreType, skipCheckOres)) { - if (notice) { - //notification.send(`跳过 ${OreChineseMap[oreType]},因为已存在于优先选择中`); - } - return false; - } +// 【锻造界面】识别并进行锻造矿石 +async function tryForgeOre(oreType) { // 获取矿石图像路径 const imagePath = ingredientImageMap[oreType]; if (!imagePath) { - if (notice) { + /*if (notice) { notification.error(`未找到矿石图像路径: ${OreChineseMap[oreType]}`); } else { log.error(`未找到矿石图像路径: ${OreChineseMap[oreType]}`); } +*/ + logMessage('error', `未找到矿石图像路径: ${OreChineseMap[oreType]}`); return false; } - log.info(`开始识别矿石: ${OreChineseMap[oreType]}`); - const scanOffset = { x: -35, y: -35 }; + log.info(`【锻造界面】开始寻找矿石: ${OreChineseMap[oreType]}`); const maxAttempts = 3; for (let attempt = 0; attempt < maxAttempts; attempt++) { let found = false; - for (const coordinate of gridCoordinates) { - const scanX = coordinate.x + scanOffset.x; - const scanY = coordinate.y + scanOffset.y; - const imageResult = recognizeImage(imagePath, scanX, scanY, 70, 70); - if (imageResult) { - found = true; - imageResult.click(); - await sleep(250); - // if (notice) { - // notification.send(`找到矿石: ${OreChineseMap[oreType]}`); - // } else { - log.info(`找到矿石: ${OreChineseMap[oreType]}`); - // } - determineOre(oreType); - - // 点击“开始锻造”按钮并进行OCR识别 - const ocrRegion = { x: 660, y: 495, width: 1250 - 660, height: 550 - 495 }; - let clickAttempts = 0; - let forgingTriggered = false; - while (clickAttempts < 4 && !forgingTriggered) { - let ro1 = captureGameRegion(); - let ConfirmButton = ro1.find(ConfirmDeployButtonRo); - ro1.dispose(); - if (ConfirmButton.isExist()) { - ConfirmButton.click(); - clickAttempts++; + + // 使用模板匹配替代图像识别 + const templateMatchResult = await findAndInteract( + RecognitionObject.TemplateMatch( + file.ReadImageMatSync(imagePath), + 40, 200, 770, 720 + ), + { + useClick: true, // 是否点击 + timeout: 3000, // 超时时间 + interval: 500, // 重试间隔 + } + ); + await sleep(250); + + if (templateMatchResult && templateMatchResult.success) { + found = true; + log.info(`找到矿石: ${OreChineseMap[oreType]}`); + determineOre(oreType); + + // 点击“开始锻造”按钮并进行OCR识别 + const ocrRegion = { x: 660, y: 495, width: 1250 - 660, height: 550 - 495 }; + let clickAttempts = 0; + let forgingTriggered = false; + while (clickAttempts < 4 && !forgingTriggered) { + + if (await findAndInteract(ConfirmDeployButtonRo, + { + useClick: true, // 是否点击 + singleAttempt: true } - await sleep(200); - let ro2 = captureGameRegion(); - let ocrResults = ro2.find( - RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height) - ); - ro2.dispose(); - if (ocrResults) { - // log.info(`${ocrResults.text}`); - if (ocrResults.text.includes("今日已无法锻造")) { - if (notice) { - notification.send("检测到 今日已无法锻造 结束锻造"); - } else { - log.info("检测到 今日已无法锻造 结束锻造"); - } - await sleep(250); - await click(960, 1042); - await sleep(200); - await click(960, 1042);// 多次点击结束弹窗 - return true; // 终止锻造流程 - } - else if (ocrResults.text.includes("材料不足")) { - if (notice) { - notification.send("检测到 材料不足 跳过当前矿物。请检查背包,及时补充矿物。"); - } else { - log.info("检测到 材料不足 跳过当前矿物。请检查背包,及时补充矿物。"); - } - clickAttempts--; // 出现材料不足时减去一次点击计数 - await sleep(250); - await click(960, 1042); - await sleep(200); - await click(960, 1042);// 多次点击结束弹窗 - return false; // 跳过当前矿石 + )) { + clickAttempts++; + } else { + log.warn("未能识别到确认按钮,尝试重新识别"); + } + await sleep(200); + + let ro2 = captureGameRegion(); + let ocrResults = ro2.find( + RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height) + ); + ro2.dispose(); + if (ocrResults) { + // log.info(`${ocrResults.text}`); + if (ocrResults.text.includes("今日已无法锻造")) { + if (notice) { + notification.send("检测到 今日已无法锻造 结束锻造"); + } else { + log.info("检测到 今日已无法锻造 结束锻造"); } + await sleep(250); + await click(960, 1042); + await sleep(200); + await click(960, 1042);// 多次点击结束弹窗 + return true; // 终止锻造流程 } - if (clickAttempts === 4) { - await sleep(1000); + else if (ocrResults.text.includes("材料不足")) { if (notice) { - notification.send(`锻造已完成,使用了 ${OreChineseMap[oreType]}`); + notification.send("检测到 材料不足 跳过当前矿物。请检查背包,及时补充矿物。"); } else { - // 偽造拾取 - log.info(`交互或拾取:"使用了[${OreChineseMap[oreType]}]"`); - log.info(`锻造已完成,使用了 ${OreChineseMap[oreType]}`); + log.info("检测到 材料不足 跳过当前矿物。请检查背包,及时补充矿物。"); } - return true; // 达到点击上限,终止锻造流程 + clickAttempts--; // 出现材料不足时减去一次点击计数 + await sleep(250); + await click(960, 1042); + await sleep(200); + await click(960, 1042);// 多次点击结束弹窗 + return false; // 跳过当前矿石 } } + if (clickAttempts === 4) { + await sleep(1000); + if (notice) { + notification.send(`锻造已完成,使用了 ${OreChineseMap[oreType]}`); + } else { + // 偽造拾取 + log.info(`交互或拾取:"使用了[${OreChineseMap[oreType]}]"`); + log.info(`锻造已完成,使用了 ${OreChineseMap[oreType]}`); + } + return true; // 达到点击上限,终止锻造流程 + } } } if (!found) { @@ -512,11 +661,13 @@ async function tryForgeOre(oreType, skipCheckOres = []) { await sleep(1000); } } - if (notice) { + /*if (notice) { notification.error(`未能识别到矿石: ${OreChineseMap[oreType]},停止尝试`); } else { log.error(`未能识别到矿石: ${OreChineseMap[oreType]},停止尝试`); } +*/ + logMessage('error', `未能识别到矿石: ${OreChineseMap[oreType]},停止尝试`); return false; } @@ -534,35 +685,43 @@ async function forgeOre(smithyName, maxOre = null) { const maxAttempts = 3; let dialogFound = false; for (let attempt = 0; attempt < maxAttempts; attempt++) { - for (let i = 0; i < 3; i++) { - let ro = captureGameRegion(); - let Forge = ro.find(ForgeRo); - ro.dispose(); - if (Forge.isExist()) { - log.info("已找到对话界面锻造图标"); - await sleep(1000); - Forge.click(); - dialogFound = true; - break; - } else { - await sleep(1000); - await click(960, 1042); - } + if (await findAndInteract(ForgeRo, { + useClick: true, + timeout: 2000, + interval: 500, + postClickDelay: 1000, + singleAttempt: true + })) { + log.info("已找到对话界面锻造图标并点击"); + await sleep(1000); + await click(960, 1042); + await sleep(1000); + await click(960, 1042); + dialogFound = true; + break; + } else { + log.warn("未能识别到对话界面锻造图标,尝试重新点击"); + await sleep(1000); + await click(960, 1042); } - if (!dialogFound) - log.error("多次尝试未能识别到对话界面锻造图标"); - break; } - // 检测锻造界面是否出现 + + if (!dialogFound) { + log.error("多次尝试未能识别到对话界面锻造图标"); + return; // 退出函数 + } + + // 对话框内的锻造图标识别到后,进入锻造界面 if (dialogFound) { let interFaceFound = false; for (let attempt = 0; attempt < maxAttempts; attempt++) { + // 检测锻造界面是否出现 let innerFound = false; for (let i = 0; i < 3; i++) { - let ro = captureGameRegion(); - let ForgingInterFace = ro.find(ForgingInterFaceRo); - ro.dispose(); - if (ForgingInterFace.isExist()) { + if (await findAndInteract(ForgingInterFaceRo, + { + + })) { log.info("已进入锻造界面"); innerFound = true; break; @@ -571,63 +730,82 @@ async function forgeOre(smithyName, maxOre = null) { await click(960, 1042); } } + + // 检测到锻造界面出现 if (innerFound) { interFaceFound = true; // 领取操作:点击全部领取及确认领取 - let ro1 = captureGameRegion(); - let ClaimAll = ro1.find(ClaimAllRo); - ro1.dispose(); - if (ClaimAll.isExist()) { - ClaimAll.click(); + if (await findAndInteract(ClaimAllRo, { + useClick: true, + timeout: 2000, + interval: 500, + postClickDelay: 1000, + singleAttempt: true, + silentOnFail: true // 失败时不报错(静默模式) + + })) { await sleep(500); - let ro = captureGameRegion(); - let ConfirmButton = ro.find(ConfirmDeployButtonRo); - ro.dispose(); - if (ConfirmButton.isExist()) { - ConfirmButton.click(); + if (await findAndInteract(ConfirmDeployButtonRo, { + useClick: true, + timeout: 2000, + interval: 500, + postClickDelay: 1000, + singleAttempt: true + })) { await sleep(500); - if (forgedOrNot === "是") { - await click(220, 150); - await sleep(1000); // 点击进入锻造界面 - } } else { log.warn("未能识别到确定按钮"); } } + if (forgedOrNot === "是") { let forgeSuccess = false; + await click(220, 150); + await sleep(1000); // 点击进入锻造界面 // 模式一:自动模式:自动选择数量最多的矿石锻造 if (model === "模式一" && maxOre) { - primaryOre = maxOre; - log.info(`自动选择数量最多的矿石为: ${primaryOre}`); - forgeSuccess = await tryForgeOre(primaryOre, []); + //log.info(`自动选择数量最多的矿石为: ${maxOre}`); + forgeSuccess = await tryForgeOre(maxOre); if (!forgeSuccess) { - log.warn("自动模式锻造未成功,切换到手动备选矿石模式"); + log.warn("自动模式锻造未成功,切换到手动排序选矿模式"); } } - // 模式二或模式一失败时,依次尝试主选及备选矿石 + // 处于模式二或模式一失败时,则按配置优先级依次尝试 if (model === "模式二" || !forgeSuccess) { - if (await tryForgeOre(primaryOre, [])) { - forgeSuccess = true; - } else if (await tryForgeOre(secondaryOre, [primaryOre])) { - forgeSuccess = true; - } else if (await tryForgeOre(tertiaryOre, [primaryOre, secondaryOre])) { - forgeSuccess = true; - } else { - if (notice) { - notification.error("所有备选矿石都未能识别,结束锻造"); - } else { - log.error("所有备选矿石都未能识别,结束锻造"); + // 生成按优先级的候选矿石列表(只包含优先级大于0的项) + let orderedOres = getSortedOresByPriority(orePriorityConfig, nicknameOrder); + // 如果之前已尝试过 maxOre,则从列表中过滤掉它,避免重复尝试 + if (maxOre) { + orderedOres = orderedOres.filter(o => o !== maxOre); + } + + // 按顺序逐个尝试 + for (const oreName of orderedOres) { + //if (!oreName) continue;//如果 oreName 是假值,就跳过当前循环迭代,继续下一个 + log.info(`按优先级尝试使用矿石: ${oreName} 锻造`); + try { + if (await tryForgeOre(oreName)) { + forgeSuccess = true; + break; + } else { + log.info(`${oreName} 尝试未成功,继续下一个`); + } + } catch (error) { + log.error(`tryForgeOre(${oreName}) 报错: ${error.message}`); } } + + if (!forgeSuccess) { + logMessage('error', "所有候选矿石均未能成功锻造,结束锻造"); + } } } // 退出锻造前判断配方,点击“锻造队列”,再次确认使用的矿物已经锻造结果 const ocrRegionAfter = { x: 185, y: 125, width: 670 - 185, height: 175 - 125 }; - let ro2 =captureGameRegion(); + let ro2 = captureGameRegion(); let ocrResultsAfter = ro2.find( RecognitionObject.ocr(ocrRegionAfter.x, ocrRegionAfter.y, ocrRegionAfter.width, ocrRegionAfter.height) ); @@ -658,10 +836,13 @@ async function forgeOre(smithyName, maxOre = null) { await genshin.returnMainUi(); } + + + /*********************** 主执行入口 ***********************/ (async function () { - // 初始化及前往铁匠铺 + // 1. 初始化 setGameMetrics(1920, 1080, 1.25); await genshin.returnMainUi(); if (notice) { @@ -670,62 +851,82 @@ async function forgeOre(smithyName, maxOre = null) { log.info("自动锻造矿石脚本开始"); } - let maxOre = null; - if (forgedOrNot === "是") { - if (model === "模式一") { - const maxOreResult = await getMaxOreType(); - if (maxOreResult) { - maxOre = maxOreResult.name; - primaryOre = maxOre; - //log.info(`自动选择数量最多的矿石为: ${maxOre}`); - if (notice) { - notification.send(`当前最多矿石为: ${OreChineseMap[maxOre]},数量: ${maxOreResult.count}`); + // 2. 主流程 + try { + // 2.1 配置检查 + if (!(await configurationCheck())) return; + + // 2.2.1 锻造选择-是 + let maxOre = null; + if (forgedOrNot === "是") { + if (model === "模式一") { + const maxOreResult = await getMaxOreType(); + if (maxOreResult) { + maxOre = maxOreResult.name; + //log.info(`自动选择数量最多的矿石为: ${maxOre}`); + if (notice) { + notification.send(`自动选择当前数量最多矿石: ${OreChineseMap[maxOre]},数量: ${maxOreResult.count}`); + } else { + log.info(`自动选择当前数量最多矿石: ${OreChineseMap[maxOre]},数量: ${maxOreResult.count}`); + } } else { - log.info(`当前最多矿石为: ${OreChineseMap[maxOre]},数量: ${maxOreResult.count}`); + log.warn("自动识别矿石失败,将使用默认配置"); } - } else { - log.warn("自动识别矿石失败,将使用默认配置"); } + await genshin.returnMainUi(); + if (!await autoSmithy(smithyName)) return; + await forgeOre(smithyName, maxOre); } - await genshin.returnMainUi(); - await autoSmithy(smithyName); - await forgeOre(smithyName, maxOre); - } - if (forgedOrNot === "否") { - keyPress("M"); await sleep(1000); + // 2.2.2 锻造选择-否 + if (forgedOrNot === "否") { + keyPress("M"); await sleep(1000); - if (!await findAndInteract(MapRo, - { - singleAttempt: true - })) { - const smithyInfo = smithyMap[smithyName]; - if (smithyInfo) { - await genshin.moveMapTo(smithyInfo.x, smithyInfo.y, smithyInfo.country); + if (!await findAndInteract(MapRo, + { + singleAttempt: true + })) { + const smithyInfo = smithyMap[smithyName]; + if (smithyInfo) { + await genshin.moveMapTo(smithyInfo.x, smithyInfo.y, smithyInfo.country); + } } - } - if (!await findAndInteract(MapForgeRo, - { - })) { - await genshin.returnMainUi(); - log.info("未能识别到锻造完成图标,无需前往领取。结束脚本"); - return; // 若没有锻造图标则跳出 + if (!await findAndInteract(MapForgeRo, + { + })) { + await genshin.returnMainUi(); + log.info("未能识别到锻造完成图标,无需前往领取。结束脚本"); + return; // 若没有锻造图标则跳出 + } + + if (!await autoSmithy(smithyName)) return; //路径函数,前往铁匠铺 + await forgeOre(smithyName); } - await autoSmithy(smithyName);//路径函数,前往铁匠铺 - await forgeOre(smithyName); + } catch (error) { + logMessage('error', `脚本执行过程中发生错误: ${error.message}`); } - await genshin.returnMainUi(); + // 3. 结束流程 + finally { + // 3.1 返回主界面 + await genshin.returnMainUi(); - //后退两步 - { keyDown("S"); await sleep(1000); keyUp("S"); await sleep(1000); } + //3.2 后退两步 + { keyDown("S"); await sleep(1000); keyUp("S"); await sleep(1000); } + + //if (notice) { + // notification.send("自动锻造矿石脚本结束"); + //} + //else { + + //3.3 发送通知 + log.info("自动锻造矿石脚本结束"); + //} + + } - //if (notice) { - // notification.send("自动锻造矿石脚本结束"); - //} - //else { - log.info("自动锻造矿石脚本结束"); - //} })(); + + diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/manifest.json" "b/repo/js/\351\223\201\345\214\240\351\223\272/manifest.json" index d64a7b2733..35784e7896 100644 --- "a/repo/js/\351\223\201\345\214\240\351\223\272/manifest.json" +++ "b/repo/js/\351\223\201\345\214\240\351\223\272/manifest.json" @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "自动锻造魔矿(识图版)", - "version": "2.3.2", + "version": "2.4.0", "bgi_version": "0.48.2", "description": "自动选择铁匠铺和使用矿物去锻造精锻矿。\n \n使用前请阅读“readme”文件以获取更多详细信息。 \n---更新说明--- \n- 请查阅readme", "tags": [ @@ -27,4 +27,4 @@ ], "settings_ui": "settings.json", "main": "main.js" -} \ No newline at end of file +} diff --git "a/repo/js/\351\223\201\345\214\240\351\223\272/settings.json" "b/repo/js/\351\223\201\345\214\240\351\223\272/settings.json" index 8c1243c16d..ec99c58cac 100644 --- "a/repo/js/\351\223\201\345\214\240\351\223\272/settings.json" +++ "b/repo/js/\351\223\201\345\214\240\351\223\272/settings.json" @@ -7,7 +7,7 @@ { "name": "model", "type": "select", - "label": "模式选择(默认:模式一)\n【模式一】自动模式:自动选择数量最多的矿石锻造\n【模式二】手动模式:依次尝试主选及备选矿石\n注:模式一可能会因OCR识别数量不准导致选择错误", + "label": "模式选择(默认:模式一)\n【模式一】自动模式:\n自动选择数量最多的矿石锻造\n【模式二】手动模式:\n依次按照矿石定义顺序尝试\n注:模式一可能会因OCR识别数量不准导致选择错误", "options": [ "模式一", "模式二" @@ -24,7 +24,8 @@ "稻妻铁匠铺", "须弥铁匠铺", "枫丹铁匠铺", - "纳塔铁匠铺" + "纳塔铁匠铺", + "挪德卡莱铁匠铺" ], "default": "蒙德铁匠铺" }, @@ -35,45 +36,59 @@ "options": [ "是", "否" - ] + ], + "default": "是" }, { - "name": "ore", + "name": "CondessenceCrystal", "type": "select", - "label": "当矿石未能识别/模式二时依次使用\n锻造用矿(默认:水晶块)", + "label": "【锻造用矿排序】\n1.当矿石未能识别或处于模式二时,按照定义顺序使用\n2.设置为0的矿石自动排除\n3.相同排序时,按照定义框从上至下优先\n\n萃凝晶", "options": [ - "水晶块", - "萃凝晶", - //"星银矿石", - //"白铁块", - //"铁块", - "紫晶块" - ] + "0", + "1", + "2", + "3", + "4" + ], + "default": "1" }, { - "name": "secondaryOre", + "name": "CrystalChunk", "type": "select", - "label": "备用矿石1(默认:萃凝晶)", + "label": "水晶块", "options": [ - "水晶块", - "萃凝晶", - //"星银矿石", - //"白铁块", - //"铁块", - "紫晶块" - ] + "0", + "1", + "2", + "3", + "4" + ], + "default": "2" }, { - "name": "tertiaryOre", + "name": "AmethystLump", "type": "select", - "label": "备用矿石2(默认:紫晶块)", + "label": "紫晶块", "options": [ - "水晶块", - "萃凝晶", - //"星银矿石", - //"白铁块", - //"铁块", - "紫晶块" - ] + "0", + "1", + "2", + "3", + "4" + ], + "default": "3" + }, + { + "name": "RainbowdropCrystal", + "type": "select", + "label": "虹滴晶", + "options": [ + "0", + "1", + "2", + "3", + "4" + ], + "default": "4" } ] \ No newline at end of file