diff --git a/repo/js/AutoEntrust/README.md b/repo/js/AutoEntrust/README.md index cb71f732..a6e605f1 100644 --- a/repo/js/AutoEntrust/README.md +++ b/repo/js/AutoEntrust/README.md @@ -7,11 +7,21 @@ - 自动识别每日委托内容 - OCR技术识别委托地点和状态 - 自动追踪委托路径 +- 智能对话选项处理 +- 自动完成对话类委托 ## 更新日志 -### 当前版本:0.95 -- 完成了对话委托 +### 当前版本:0.96 +- 优化对话委托处理逻辑 +- 优化战斗委托处理逻辑 +- 增强NPC对话选项智能识别 +- 支持自定义对话优先选项 +- 新增NPC白名单功能,提高交互精确度 +- 完善对话委托文档,增加JSON和简易格式说明 + +### 上一版本:0.95 +- 完成了对话委托功能实现 ### 上一版本:0.94.1 - 修复攀高危险问题 @@ -40,18 +50,15 @@ - 基础的委托路径追踪功能 ## 使用说明 -1. 确保已安装BetterGI并正确配置,推荐在自动战斗中关闭拾取掉落物,因为会浪费大量时间。 +1. 确保已安装BetterGI并正确配置(可参考AutoEntrust/配置组配置参考.png),推荐在自动战斗中关闭拾取掉落物,因为会浪费大量时间。 2. 在脚本设置中可配置以下选项: - - 跳过识别(直接从Data文件夹加载委托数据进行追踪) - - 最小文本长度(小于此长度的文本将被过滤) - - 调试模式(输出更多日志信息) - - 战斗的队伍名称 -3. 常见问题:1.Q:明明这个地方没有委托,却跑回去打怪。A:脚本是这样写的,未来会增加检测功能,优化脚本。2.Q:为什么有的蒙德打怪委托未完成?A:因为请将脚本data/文件夹下面的数据,log/日志下今日日志,每日委托任务截图。反馈给3302871403 + - 跳过识别(直接从Data文件夹加载委托数据进行追踪)为关闭 + - 战斗的队伍名称为您日常打怪的队伍名称 +3. 常见问题:1.Q:明明这个地方没有委托,却跑回去打怪(此问题已经解决)。A:未来会增加检测功能,优化脚本。2.Q:为什么有的蒙德打怪委托未完成?A:因为请将脚本data/文件夹下面的数据,log/日志下今日日志,每日委托任务截图。反馈给作者 -## 主要功能 -- 1.加3302871403QQ好友 +## 反馈方法 +- 1.加群1053392753反馈 - 2.发送截图,日志给3302871403@qq.com -- 3.加bgi v8群@星野(bgi v7群也可以) ## 注意事项 - 部分委托可能无法正确识别或追踪,遇到问题请手动完成 diff --git a/repo/js/AutoEntrust/assets/丘丘人的一小步/低语森林-1.json b/repo/js/AutoEntrust/assets/丘丘人的一小步/低语森林-1.json index 1615b94d..150fb2e0 100644 --- a/repo/js/AutoEntrust/assets/丘丘人的一小步/低语森林-1.json +++ b/repo/js/AutoEntrust/assets/丘丘人的一小步/低语森林-1.json @@ -5,35 +5,36 @@ "author": "LX666-666", "version": "1.0", "description": "", - "bgi_version": "0.42.3" + "map_name": "Teyvat", + "bgi_version": "0.45.0" }, "positions": [ { "id": 1, + "x": -1332, + "y": 2559.5, "action": "", "move_mode": "walk", - "type": "teleport", - "x": -1332.0, - "y": 2559.5, - "action_params": "" + "action_params": "", + "type": "teleport" }, { "id": 2, + "x": -1296.5, + "y": 2549, "action": "", "move_mode": "fly", - "type": "path", - "x": -1296.5, - "y": 2549.0, - "action_params": "" + "action_params": "", + "type": "path" }, { "id": 3, - "action": "fight", - "move_mode": "run", - "type": "path", "x": -1108.62, "y": 2459.38, - "action_params": "" + "action": "fight", + "move_mode": "dash", + "action_params": "", + "type": "path" } ] } \ No newline at end of file diff --git a/repo/js/AutoEntrust/assets/丘丘人的一小步/苍风高地-1.json b/repo/js/AutoEntrust/assets/丘丘人的一小步/苍风高地-1.json index b5a4291d..002257f2 100644 --- a/repo/js/AutoEntrust/assets/丘丘人的一小步/苍风高地-1.json +++ b/repo/js/AutoEntrust/assets/丘丘人的一小步/苍风高地-1.json @@ -5,56 +5,58 @@ "author": "1", "version": "1.0", "description": "", - "bgi_version": "0.42.0" + "map_name": "Teyvat", + "bgi_version": "0.45.0" }, "positions": [ { "id": 1, - "x": -601.43, - "y": 1842.75, - "action": "", + "x": -601.430, + "y": 1842.750, + "type": "teleport", "move_mode": "walk", - "type": "teleport" + "action": "", + "action_params": "" }, { "id": 2, - "x": -627.88, - "y": 1820.25, + "x": -604, + "y": 1827.25, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "walk", - "action_params": "", - "type": "path" + "action_params": "" }, { "id": 3, - "x": -645.52, - "y": 1810.12, + "x": -627.8799999999992, + "y": 1820.25, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "walk", - "action_params": "", - "type": "path" + "action_params": "" }, { "id": 4, - "x": -656.0, - "y": 1801.0, + "x": -645.5200000000004, + "y": 1810.119999999999, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "run", - "type": "path" + "action_params": "" }, { "id": 5, - "x": -668.0, - "y": 1789.0, - "action": "fight", - "move_mode": "walk", - "action_params": "", + "x": -656, + "y": 1801, + "action": "", + "move_mode": "dash", "type": "path" }, { "id": 6, - "x": -673.44, - "y": 1782.56, + "x": -668, + "y": 1789, "action": "fight", "move_mode": "walk", "action_params": "", @@ -62,88 +64,8 @@ }, { "id": 7, - "x": -688.0, - "y": 1783.75, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 8, - "x": -715.75, - "y": 1764.0, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 9, - "x": -732.0, - "y": 1752.5, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 10, - "x": -765.74, - "y": 1754.0, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 11, - "x": -795.49, - "y": 1752.75, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 12, - "x": -811.0, - "y": 1719.25, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 13, - "x": -815.01, - "y": 1684.25, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 14, - "x": -812.25, - "y": 1659.0, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 15, - "x": -793.64, - "y": 1651.5, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 16, - "x": -760.0, - "y": 1649.5, - "action": "fight", - "move_mode": "walk", - "type": "target" - }, - { - "id": 17, - "x": -767.25, - "y": 1643.5, + "x": -673.4400000000005, + "y": 1782.5599999999995, "type": "path", "move_mode": "walk", "action": "fight", diff --git a/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-1.json b/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-1.json index b5a4291d..78c41328 100644 --- a/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-1.json +++ b/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-1.json @@ -5,145 +5,86 @@ "author": "1", "version": "1.0", "description": "", - "bgi_version": "0.42.0" + "map_name": "Teyvat", + "bgi_version": "0.45.0" }, "positions": [ { "id": 1, - "x": -601.43, - "y": 1842.75, - "action": "", + "x": -887.46875, + "y": 1679.154296875, + "type": "teleport", "move_mode": "walk", - "type": "teleport" + "action": "", + "action_params": "" }, { "id": 2, - "x": -627.88, - "y": 1820.25, + "x": -882.4853515625, + "y": 1682.982421875, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "walk", - "action_params": "", - "type": "path" + "action_params": "" }, { "id": 3, - "x": -645.52, - "y": 1810.12, + "x": -868.8515625, + "y": 1684.669921875, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "walk", - "action_params": "", - "type": "path" + "action_params": "" }, { "id": 4, - "x": -656.0, - "y": 1801.0, + "x": -853.2958984375, + "y": 1688.7705078125, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "run", - "type": "path" + "action_params": "" }, { "id": 5, - "x": -668.0, - "y": 1789.0, - "action": "fight", - "move_mode": "walk", - "action_params": "", - "type": "path" + "x": -838.123046875, + "y": 1672.68896484375, + "type": "path", + "move_mode": "dash", + "action": "", + "action_params": "" }, { "id": 6, - "x": -673.44, - "y": 1782.56, - "action": "fight", - "move_mode": "walk", - "action_params": "", - "type": "path" + "x": -819.5341796875, + "y": 1665.2978515625, + "type": "path", + "move_mode": "dash", + "action": "", + "action_params": "" }, { "id": 7, - "x": -688.0, - "y": 1783.75, + "x": -807.578125, + "y": 1655.23486328125, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "run", - "type": "path" + "action_params": "" }, { "id": 8, - "x": -715.75, - "y": 1764.0, + "x": -793.1875, + "y": 1650.57177734375, + "type": "path", + "move_mode": "dash", "action": "", - "move_mode": "run", - "type": "path" + "action_params": "" }, { "id": 9, - "x": -732.0, - "y": 1752.5, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 10, - "x": -765.74, - "y": 1754.0, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 11, - "x": -795.49, - "y": 1752.75, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 12, - "x": -811.0, - "y": 1719.25, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 13, - "x": -815.01, - "y": 1684.25, - "action": "", - "move_mode": "run", - "type": "path" - }, - { - "id": 14, - "x": -812.25, - "y": 1659.0, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 15, - "x": -793.64, - "y": 1651.5, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 16, - "x": -760.0, - "y": 1649.5, - "action": "fight", - "move_mode": "walk", - "type": "target" - }, - { - "id": 17, - "x": -767.25, - "y": 1643.5, + "x": -780.1943359375, + "y": 1655.14013671875, "type": "path", "move_mode": "walk", "action": "fight", diff --git a/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-2.json b/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-2.json new file mode 100644 index 00000000..82b98b51 --- /dev/null +++ b/repo/js/AutoEntrust/assets/圆滚滚的易爆品/苍风高地-2.json @@ -0,0 +1,153 @@ +{ + "info": { + "name": "苍风高地-1", + "type": "collect", + "author": "1", + "version": "1.0", + "description": "", + "bgi_version": "0.42.0" + }, + "positions": [ + { + "id": 1, + "x": -601.43, + "y": 1842.75, + "action": "", + "move_mode": "walk", + "type": "teleport" + }, + { + "id": 2, + "x": -627.88, + "y": 1820.25, + "action": "", + "move_mode": "walk", + "action_params": "", + "type": "path" + }, + { + "id": 3, + "x": -645.52, + "y": 1810.12, + "action": "", + "move_mode": "walk", + "action_params": "", + "type": "path" + }, + { + "id": 4, + "x": -656.0, + "y": 1801.0, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 5, + "x": -668.0, + "y": 1789.0, + "action": "fight", + "move_mode": "walk", + "action_params": "", + "type": "path" + }, + { + "id": 6, + "x": -673.44, + "y": 1782.56, + "action": "fight", + "move_mode": "walk", + "action_params": "", + "type": "path" + }, + { + "id": 7, + "x": -688.0, + "y": 1783.75, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 8, + "x": -715.75, + "y": 1764.0, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 9, + "x": -732.0, + "y": 1752.5, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 10, + "x": -765.74, + "y": 1754.0, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 11, + "x": -795.49, + "y": 1752.75, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 12, + "x": -811.0, + "y": 1719.25, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 13, + "x": -815.01, + "y": 1684.25, + "action": "", + "move_mode": "run", + "type": "path" + }, + { + "id": 14, + "x": -812.25, + "y": 1659.0, + "action": "", + "move_mode": "walk", + "type": "path" + }, + { + "id": 15, + "x": -793.64, + "y": 1651.5, + "action": "", + "move_mode": "walk", + "type": "path" + }, + { + "id": 16, + "x": -760.0, + "y": 1649.5, + "action": "fight", + "move_mode": "walk", + "type": "target" + }, + { + "id": 17, + "x": -7670.25, + "y": 1643.5, + "type": "path", + "move_mode": "walk", + "action": "fight", + "action_params": "" + } + ] +} \ No newline at end of file diff --git a/repo/js/AutoEntrust/assets/攀高危险/坠星山谷-1.json b/repo/js/AutoEntrust/assets/攀高危险/坠星山谷-1.json index f3b9c5fc..cf9075a8 100644 --- a/repo/js/AutoEntrust/assets/攀高危险/坠星山谷-1.json +++ b/repo/js/AutoEntrust/assets/攀高危险/坠星山谷-1.json @@ -2,16 +2,17 @@ "info": { "name": "坠星山谷-1", "type": "collect", - "author": "1", + "author": "星野", "version": "", "description": "", - "bgi_version": "0.42.3" + "map_name": "Teyvat", + "bgi_version": "0.45.0" }, "positions": [ { "id": 1, "x": -1536.75, - "y": 1978.66, + "y": 1978.6599999999999, "action": "stop_flying", "move_mode": "fly", "action_params": "", @@ -19,8 +20,8 @@ }, { "id": 2, - "x": -1531.85, - "y": 1989.93, + "x": -1531.5419921875, + "y": 1990.71240234375, "action": "stop_flying", "move_mode": "fly", "action_params": "", @@ -28,77 +29,48 @@ }, { "id": 3, - "x": -1518.41, - "y": 1993.52, + "x": -1505.109375, + "y": 1994.671875, "action": "", - "move_mode": "run", + "move_mode": "dash", + "action_params": "", "type": "path" }, { "id": 4, - "x": -1506.54, - "y": 1996.81, + "x": -1482.9208984375, + "y": 1986.97314453125, "action": "", - "move_mode": "walk", + "move_mode": "dash", + "action_params": "", "type": "path" }, { "id": 5, - "x": -1499.14, - "y": 1998.92, - "action": "", + "x": -1467.96484375, + "y": 1978.60009765625, + "action": "fight", "move_mode": "walk", + "action_params": "", "type": "path" }, { "id": 6, - "x": -1492.21, - "y": 1999.46, - "action": "fight", - "move_mode": "walk", + "x": -1477.75, + "y": 1988.5, + "action": "", + "move_mode": "dash", "action_params": "", "type": "path" }, { "id": 7, - "x": -1501.38, - "y": 1991.94, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 8, - "x": -1488.15, - "y": 1990.18, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 9, - "x": -1478.8, - "y": 1988.12, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 10, - "x": -1470.07, - "y": 1977.84, + "x": -1490.609375, + "y": 1996.587890625, "action": "fight", "move_mode": "walk", "action_params": "", "type": "path" - }, - { - "id": 11, - "x": -1471.99, - "y": 1998.49, - "action": "", - "move_mode": "walk", - "type": "path" } ] } \ No newline at end of file diff --git a/repo/js/AutoEntrust/assets/攀高危险/苍风高地-1.json b/repo/js/AutoEntrust/assets/攀高危险/苍风高地-1.json index 5b323580..f7adeacd 100644 --- a/repo/js/AutoEntrust/assets/攀高危险/苍风高地-1.json +++ b/repo/js/AutoEntrust/assets/攀高危险/苍风高地-1.json @@ -5,7 +5,8 @@ "author": "1", "version": "", "description": "", - "bgi_version": "0.42.3" + "map_name": "Teyvat", + "bgi_version": "0.45.0" }, "positions": [ { @@ -101,50 +102,26 @@ "x": -1086.32, "y": 1849.53, "action": "", - "move_mode": "walk", + "move_mode": "dash", "type": "path" }, { "id": 13, "x": -1099.39, "y": 1843.03, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 14, - "x": -1106.38, - "y": 1846.4, "action": "fight", "move_mode": "walk", "action_params": "", "type": "path" }, { - "id": 15, - "x": -1103.79, - "y": 1859.5, - "action": "", - "move_mode": "walk", - "type": "path" - }, - { - "id": 16, + "id": 14, "x": -1106.37, "y": 1867.04, "action": "fight", "move_mode": "walk", "action_params": "", "type": "path" - }, - { - "id": 17, - "x": -1096.21, - "y": 1867.18, - "action": "", - "move_mode": "walk", - "type": "path" } ] } \ No newline at end of file diff --git a/repo/js/AutoEntrust/main.js b/repo/js/AutoEntrust/main.js index eff0f914..f62bd76a 100644 --- a/repo/js/AutoEntrust/main.js +++ b/repo/js/AutoEntrust/main.js @@ -1,4 +1,4 @@ -(function () { +(async function () { // 定义常量 const OCR_REGION_X = 750; const OCR_REGION_Y = 250; @@ -51,19 +51,22 @@ // 获取设置 const skipRecognition = settings.skipRecognition || false; - const debugMode = settings.debugMode || false; + //const debugMode = settings.debugMode || false; const minTextLength = parseInt(settings.minTextLength || "4"); const team = settings.team || ""; + + // 存储当前委托位置 + let currentPlayerPosition = null; - async function prepareForLeyLineRun(settings) { + async function prepareForLeyLineRun() { // 开局传送到七天神像 await genshin.returnMainUi() await genshin.tpToStatueOfTheSeven(); - + dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false })); // 切换战斗队伍 - if (settings.team) { - log.info(`切换至队伍 ${settings.team}`); - await genshin.switchParty(settings.team); + if (team) { + log.info(`切换至队伍 ${team}`); + await genshin.switchParty(team); } } @@ -80,7 +83,7 @@ async function detectCommissionStatusByImage(buttonIndex) { return "unknown"; } - log.info("检测委托{id}的完成状态(图像识别)", button.id); + //log.info("检测委托{id}的完成状态(图像识别)", button.id); // 截图 let captureRegion = captureGameRegion(); @@ -127,7 +130,7 @@ async function detectCommissionStatusByImage(buttonIndex) { return "uncompleted"; } - // 尝试使用更低的阈值再次检测 + /* 尝试使用更低的阈值再次检测 log.info("使用更低阈值再次检测委托{id}状态", button.id); completedRo.threshold = 0.6; uncompletedRo.threshold = 0.6; @@ -142,7 +145,7 @@ async function detectCommissionStatusByImage(buttonIndex) { if (!uncompletedResult2.isEmpty()) { log.info("委托{id}未完成(低阈值检测)", button.id); return "uncompleted"; - } + }*/ log.warn("委托{id}状态识别失败", button.id); return "unknown"; @@ -387,7 +390,7 @@ async function detectCommissionStatusByImage(buttonIndex) { // 检测是否进入委托详情界面 async function checkDetailPageEntered() { try { - log.info("检测是否进入委托详情界面..."); + //log.info("检测是否进入委托详情界面..."); // 创建OCR识别对象 - 检测区域(1480,100)到(1535,130) const detailOcrRo = RecognitionObject.Ocr( @@ -407,7 +410,7 @@ async function detectCommissionStatusByImage(buttonIndex) { // 检查OCR结果 for (let j = 0; j < results.count; j++) { const text = results[j].text.trim(); - log.info(`检测到文本: "${text}"`); + //log.info(`检测到文本: "${text}"`); // 如果有"蒙德",表示进入了详情界面 if (text.includes("蒙德")) { @@ -478,8 +481,6 @@ async function Identification() { OCR_REGION_HEIGHT ); - await sleep(2000); - // 第一次截图识别 log.info("执行第一次OCR识别 ({x}, {y}) ({width}, {height})", OCR_REGION_X, OCR_REGION_Y, OCR_REGION_X + OCR_REGION_WIDTH, OCR_REGION_Y + OCR_REGION_HEIGHT); @@ -559,13 +560,21 @@ async function Identification() { log.info("委托 {name} 的地点: {location}", commission.name, location); } - // 退出详情页面 + // 退出详情页面并获取地图坐标 if (detailStatus !== "未知") { log.info("退出详情页面 - 按ESC"); keyDown("VK_ESCAPE"); await sleep(300); keyUp("VK_ESCAPE"); await sleep(1200); + + // 获取地图坐标并保存 + const bigMapPosition = genshin.getPositionFromBigMap(); + if (bigMapPosition) { + currentPlayerPosition = bigMapPosition; + commission.playerPosition = bigMapPosition; + log.info("当前委托位置: ({x}, {y})", bigMapPosition.x, bigMapPosition.y); + } keyDown("VK_ESCAPE"); await sleep(300); @@ -649,13 +658,21 @@ async function Identification() { log.info("委托 {name} 的地点: {location}", fourthCommission.name, location); } - // 退出详情页面 + // 退出详情页面并获取地图坐标 if (detailStatus !== "未知") { log.info("退出详情页面 - 按ESC"); keyDown("VK_ESCAPE"); await sleep(300); keyUp("VK_ESCAPE"); await sleep(1200); + + // 获取地图坐标并保存 + const bigMapPosition = genshin.getPositionFromBigMap(); + if (bigMapPosition) { + currentPlayerPosition = bigMapPosition; + fourthCommission.playerPosition = bigMapPosition; + log.info("当前委托位置: ({x}, {y})", bigMapPosition.x, bigMapPosition.y); + } keyDown("VK_ESCAPE"); await sleep(300); @@ -690,8 +707,398 @@ async function Identification() { } } + /** + * 计算两点之间的距离 + * @param {Object} pos1 - 位置1 {x, y} + * @param {Object} pos2 - 位置2 {x, y} + * @returns {number} 距离 + */ + function calculateDistance(point1, point2) { + if (!point1 || !point2 || !point1.X || !point1.Y || !point2.x || !point2.y) { + log.warn("无效的位置数据"); + return Infinity; + } + return Math.sqrt(Math.pow(point1.X - point2.x, 2) + Math.pow(point1.Y - point2.y, 2)); + } + + /** + * 获取委托的目标坐标(从路径追踪文件中获取最后一个坐标) + * @param {string} scriptPath - 委托的脚本路径 + * @returns {Object|null} 目标坐标 {x, y} 或 null + */ + async function getCommissionTargetPosition(scriptPath) { + try { + const scriptContent = await file.readText(scriptPath); + const pathData = JSON.parse(scriptContent); + + if (!pathData.positions || pathData.positions.length === 0) { + log.warn("路径追踪文件 {path} 中没有有效的坐标数据", scriptPath); + return null; + } + + const lastPosition = pathData.positions[pathData.positions.length - 1]; + if (!lastPosition.x || !lastPosition.y) { + log.warn("路径追踪文件 {path} 的最后一个路径点缺少坐标数据", scriptPath); + return null; + } + + log.debug("从脚本路径 {path} 获取到目标坐标: ({x}, {y})", scriptPath, lastPosition.x, lastPosition.y); + return { + x: lastPosition.x, + y: lastPosition.y + }; + } catch (error) { + log.error("获取委托目标位置时出错: {error}", error); + return null; + } + } + + /** + * 统一的对话委托流程处理器 + * @param {Array} processSteps - 处理步骤数组 + * @param {string} commissionName - 委托名称 + * @param {string} location - 委托地点 + * @returns {Promise} - 执行结果 + */ + async function executeUnifiedTalkProcess(processSteps, commissionName, location) { + try { + log.info("执行统一对话委托流程: {name}", commissionName); + + // 加载优先选项和白名单NPC名称的默认值 + let priorityOptions = []; + let npcWhiteList = []; + let clickedExtractedName = false; + + // 定义识别对象 + const paimonMenuRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync("Data/RecognitionObject/paimon_menu.png"), + 0, 0, genshin.width / 3.0, genshin.width / 5.0 + ); + + // 判断是否在主界面的函数 + const isInMainUI = () => { + let captureRegion = captureGameRegion(); + let res = captureRegion.Find(paimonMenuRo); + return !res.isEmpty(); + }; + + // 人名提取函数 + const extractName = (text) => { + const patterns = [ + /与(.+?)对话/, /与(.+?)一起/, /同(.+?)交谈/, /向(.+?)打听/, /向(.+?)回报/, + /陪同(.+?)\S+/, /找到(.+?)\S+/, /询问(.+?)\S+/, /拜访(.+?)\S+/, /寻找(.+?)\S+/, + /告诉(.+?)\S+/, /带(.+?)去\S+/, /跟随(.+?)\S+/, /协助(.+?)\S+/, /请教(.+?)\S+/, + /拜托(.+?)\S+/, /委托(.+?)\S+/ + ]; + + for (const pattern of patterns) { + const match = text.match(pattern); + if (match && match[1]) { + return match[1].trim(); + } + } + return null; + }; + + // 自动剧情函数 + const executeOptimizedAutoTalk = async ( + extractedName = null, + skipCount = 5, + customPriorityOptions = null, + customNpcWhiteList = null + ) => { + // 使用传入的参数,不再加载默认配置 + const effectivePriorityOptions = customPriorityOptions || []; + const effectiveNpcWhiteList = customNpcWhiteList || []; + + // 初始化 + keyPress("V"); + + // 初始触发剧情 - 识别人名并点击 + extractedName = []; + let captureRegion = captureGameRegion(); + let nameArea = captureRegion.DeriveCrop(75, 240, 225, 60); // 人名区域 + let nameOcrRo = RecognitionObject.Ocr(0, 0, nameArea.width, nameArea.height); + let nameResults = nameArea.FindMulti(nameOcrRo); + // 尝试提取任务人名 + for (let i = 0; i < nameResults.count; i++) { + let text = nameResults[i].text; + log.info(`任务区域识别文本: ${text}`); + + // 尝试提取任务人名 + let name = extractName(text); + if (name) { + extractedName = name; + log.info(`提取到人名: ${extractedName}`); + break; + } + } + + nameArea = captureRegion.DeriveCrop(1150, 300, 350, 400); + //nameArea = captureRegion.DeriveCrop(0, 0, 1920, 1080); + nameOcrRo = RecognitionObject.Ocr(0, 0, nameArea.width, nameArea.height); + nameResults = nameArea.FindMulti(nameOcrRo); + let clickedWhitelistNPC=false; + + + // 处理人名区域的OCR结果 + if (nameResults.count > 0) { + log.info(`人名区域识别到 ${nameResults.count} 个文本`); + + // 首先尝试点击白名单中的NPC + for (let i = 0; i < nameResults.count; i++) { + let text = nameResults[i].text; + let res = nameResults[i]; + log.info("人名区域识别到{text}:位置({x},{y},{h},{w})", res.text,res.x, res.y, res.width, res.Height); + // 检查是否包含白名单中的NPC名称 + for (let j = 0; j < effectiveNpcWhiteList.length; j++) { + if (text.includes(effectiveNpcWhiteList[j])) { + log.info(`找到白名单NPC: ${effectiveNpcWhiteList[j]},点击该NPC`); + keyDown("VK_MENU"); + await sleep(500); + click(res.x+1150, res.y+300); + leftButtonClick(); + keyUp("VK_MENU"); + clickedWhitelistNPC = true; + break; + } + } + if (clickedWhitelistNPC) break; + } + + // 如果没有点击白名单NPC,尝试点击包含提取到的人名的选项 + if (!clickedWhitelistNPC && extractedName) { + for (let i = 0; i < nameResults.count; i++) { + let text = nameResults[i].text; + let res = nameResults[i]; + if (text.includes(extractedName)) { + log.info(`点击包含提取到任务人名的选项: ${text}`); + keyDown("VK_MENU"); + await sleep(500); + click(res.x+1150, res.y+300); + leftButtonClick(); + keyUp("VK_MENU"); + clickedExtractedName = true; + break; + } + } + } + } + + // 如果没有找到NPC,使用默认触发 + if (!clickedWhitelistNPC && !clickedExtractedName) { + log.info("未找到匹配的NPC,使用默认触发方式"); + keyPress("F"); // 默认触发剧情 + await sleep(500); + } + + // 重复执行自动剧情,直到返回主界面 + let maxAttempts = 100; // 设置最大尝试次数,防止无限循环 + let attempts = 0; + await sleep(1000); + log.info("开始执行自动剧情"); + + while (!isInMainUI() && attempts < maxAttempts) { + attempts++; + + // 正常跳过对话 + await genshin.chooseTalkOption("纳西妲美貌举世无双", skipCount, false); + + if (isInMainUI()) { + log.info("检测到已返回主界面,结束循环"); + break; + } + + // 每skipCount次跳过后,进行OCR识别 + if (true) { + //log.info("执行OCR识别对话选项"); + + // 检查是否有匹配的优先选项 + let foundPriorityOption = false; + + // 获取对话区域截图并进行OCR识别 + let captureRegion = captureGameRegion(); + let dialogArea = captureRegion.DeriveCrop(1250, 450, 550, 400); // 对话选项区域 1250,450 到 1800,850 + + // 创建OCR识别对象并识别文本 + let ocrRo = RecognitionObject.Ocr(0, 0, dialogArea.width, dialogArea.height); + let ocrResults = dialogArea.FindMulti(ocrRo); + if(ocrResults.count>0){ + log.info(`识别到 ${ocrResults.count} 个选项`); + + for (let i = 0; i < ocrResults.count; i++) { + let ocrText = ocrResults[i].text; + + // 检查是否在优先选项列表中 + for (let j = 0; j < effectivePriorityOptions.length; j++) { + if (ocrText.includes(effectivePriorityOptions[j])) { + log.info(`找到优先选项: ${effectivePriorityOptions[j]},点击该选项`); + // 点击该选项 + ocrResults[i].click(); + await sleep(500); + foundPriorityOption = true; + break; + } + } + + if (foundPriorityOption) break; + } + + // 如果没有找到优先选项,则使用默认跳过 + if (!foundPriorityOption) { + await genshin.chooseTalkOption("", 1, false); + } + } + } + + // 检查是否已返回主界面 + if (isInMainUI()) { + log.info("检测到已返回主界面,结束循环"); + break; + } + } + + if (isInMainUI()) { + log.info("已返回主界面,自动剧情执行完成"); + keyPress("V"); + } else { + log.warn(`已达到最大尝试次数 ${maxAttempts},但未检测到返回主界面`); + } + + } + + // 执行处理步骤 + for (let i = 0; i < processSteps.length; i++) { + const step = processSteps[i]; + log.info("执行流程步骤 {step}: {type}", i + 1, step.type || step); + + // 重置为默认值 + priorityOptions = []; + npcWhiteList = []; + + // 如果步骤中包含自定义的优先选项和NPC白名单,则使用它们 + if (step.data && typeof step.data === "object") { + if (Array.isArray(step.data.priorityOptions)) { + priorityOptions = step.data.priorityOptions; + log.info("使用自定义优先选项: {options}", priorityOptions.join(", ")); + } + if (Array.isArray(step.data.npcWhiteList)) { + npcWhiteList = step.data.npcWhiteList; + log.info("使用自定义NPC白名单: {npcs}", npcWhiteList.join(", ")); + } + } + + if (typeof step === "string") { + // 简单格式处理 + if (step.endsWith(".json")) { + // 地图追踪文件 + const trackingPath = `${TALK_PROCESS_BASE_PATH}/${commissionName}/${location}/${step}`; + log.info("执行地图追踪: {path}", trackingPath); + try { + await pathingScript.runFile(trackingPath); + log.info("地图追踪执行完成"); + } catch (error) { + log.error("执行地图追踪时出错: {error}", error); + } + } else if (step === "F") { + // 按F键并执行优化的自动剧情 + log.info("执行自动剧情"); + await executeOptimizedAutoTalk(null, 5, priorityOptions, npcWhiteList); + } + } else if (typeof step === "object") { + // JSON格式处理 + if (step.note) { + log.info("步骤说明: {note}", step.note); + } + + switch (step.type) { + case "地图追踪": + log.info("执行地图追踪: {path}", step.data); + try { + const fullPath = `${TALK_PROCESS_BASE_PATH}/${commissionName}/${location}/${step.data}`; + await pathingScript.runFile(fullPath); + log.info("地图追踪执行完成"); + } catch (error) { + log.error("执行地图追踪时出错: {error}", error); + } + break; + + case "键鼠脚本": + log.info("执行键鼠脚本: {path}", step.data); + try { + const fullPath = `${TALK_PROCESS_BASE_PATH}/${commissionName}/${location}/${step.data}`; + await keyMouseScript.runFile(fullPath); + log.info("键鼠脚本执行完成"); + } catch (error) { + log.error("执行键鼠脚本时出错: {error}", error.message); + } + break; + + case "对话": + log.info("执行对话"); + let skipCount = 5; // 默认跳过5次 + + // 处理对话选项 + if (typeof step.data === "number") { + // 兼容旧版本,如果data是数字,则视为skipCount + skipCount = step.data; + } else if (typeof step.data === "object" && step.data.skipCount) { + // 新版本,data是对象,包含skipCount + skipCount = step.data.skipCount; + } + + // 执行对话,使用当前步骤的优先选项和NPC白名单 + await executeOptimizedAutoTalk(null, skipCount, priorityOptions, npcWhiteList); + break; + + case "按键": + if (typeof step.data === "string") { + log.info("执行按键: {key}", step.data); + keyPress(step.data); + } else if (typeof step.data === "object") { + if (step.data.action === "down") { + log.info("按下按键: {key}", step.data.key); + keyDown(step.data.key); + } else if (step.data.action === "up") { + log.info("释放按键: {key}", step.data.key); + keyUp(step.data.key); + } else if (step.data.action === "press") { + log.info("点击按键: {key}", step.data.key); + keyPress(step.data.key); + } + } + break; + + case "tp": + if (Array.isArray(step.data) && step.data.length >= 2) { + log.info("执行传送: {x}, {y}", step.data[0], step.data[1]); + const force = step.data.length > 2 ? step.data[2] : false; + await genshin.tp(step.data[0], step.data[1], force); + log.info("传送完成"); + } else { + log.error("传送参数格式错误"); + } + break; + + default: + log.warn("未知的流程类型: {type}", step.type); + } + } + + // 每个步骤之间等待一段时间 + await sleep(2000); + } + + log.info("统一对话委托流程执行完成: {name}", commissionName); + return true; + } catch (error) { + log.error("执行统一对话委托流程时出错: {error}", error.message); + return false; + } + } + /** - * 执行对话委托流程 + * 执行对话委托流程(优化版) * @param {string} commissionName - 委托名称 * @param {string} location - 委托地点 * @returns {Promise} - 执行结果 @@ -700,51 +1107,38 @@ async function executeTalkCommission(commissionName, location) { try { log.info("开始执行对话委托: {name} ({location})", commissionName, location); - // 构建可能的流程文件路径 - const processBasePath = `${TALK_PROCESS_BASE_PATH}/${commissionName}`; - const processFilePaths = [ - `${processBasePath}/process.json`, - `${processBasePath}/${location}/process.json` - ]; + // 优化的文件路径:固定使用 ${processBasePath}/${location}/process.json + const processBasePath = TALK_PROCESS_BASE_PATH; + const processFilePath = `${processBasePath}/${commissionName}/${location}/process.json`; - let processFilePath = null; - - // 查找流程文件 - for (const path of processFilePaths) { - try { - await file.readText(path); - processFilePath = path; - log.info("找到对话委托流程文件: {path}", path); - break; - } catch (error) { - log.info("对话委托流程文件不存在: {path}", path); - } - } - - if (!processFilePath) { - log.warn("未找到对话委托 {name} 在 {location} 的流程文件", commissionName, location); + let processContent; + try { + processContent = await file.readText(processFilePath); + log.info("找到对话委托流程文件: {path}", processFilePath); + } catch (error) { + log.warn("未找到对话委托 {name} 在 {location} 的流程文件: {path}", commissionName, location, processFilePath); return false; } - // 读取流程文件内容 - const processContent = await file.readText(processFilePath); - - // 判断是简单格式还是JSON格式 - let isJsonFormat = false; + // 解析流程内容 + let processSteps; try { - JSON.parse(processContent); - isJsonFormat = true; - } catch (error) { - isJsonFormat = false; + // 尝试解析为JSON格式 + const jsonData = JSON.parse(processContent); + if (Array.isArray(jsonData)) { + processSteps = jsonData; + } else { + log.error("JSON流程格式错误,应为数组"); + return false; + } + } catch (jsonError) { + // 如果不是JSON格式,按简单格式处理 + const lines = processContent.split('\n').map(line => line.trim()).filter(line => line.length > 0); + processSteps = lines; } - if (isJsonFormat) { - // 处理JSON格式的流程 - return await executeJsonProcess(processContent, commissionName, location); - } else { - // 处理简单格式的流程 - return await executeSimpleProcess(processContent, commissionName, location); - } + // 使用统一的处理器执行流程 + return await executeUnifiedTalkProcess(processSteps, commissionName, location); } catch (error) { log.error("执行对话委托时出错: {error}", error); @@ -753,364 +1147,39 @@ async function executeTalkCommission(commissionName, location) { } /** - * 执行简单格式的对话委托流程 - * @param {string} processContent - 流程内容 - * @param {string} commissionName - 委托名称 - * @param {string} location - 委托地点 - * @returns {Promise} - 执行结果 + * 检查委托状态 + * @param buttonIndex — 按钮索引(1-4) + * @returns — 返回 ture,false */ -async function executeSimpleProcess(processContent, commissionName, location) { - try { - log.info("执行简单格式的对话委托流程: {name}", commissionName); - - // 按行分割流程内容 - const lines = processContent.split('\n').map(line => line.trim()).filter(line => line.length > 0); - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - log.info("执行流程步骤 {step}: {line}", i + 1, line); - - if (line.endsWith(".json")) { - // 地图追踪文件 - const trackingPath = `assets/process/${commissionName}/${line}`; - log.info("执行地图追踪: {path}", trackingPath); - try { - await pathingScript.runFile(trackingPath); - log.info("地图追踪执行完成"); - } catch (error) { - log.error("执行地图追踪时出错: {error}", error); - } - } else if (line === "F") { - // 按F键并等待对话结束 - log.info("按下F键并等待对话结束"); - keyPress("F"); - - // 定义识别对象 - const paimonMenuRo = RecognitionObject.TemplateMatch( - file.ReadImageMatSync("Data/RecognitionObject/paimon_menu.png"), - 0, 0, genshin.width / 3.0, genshin.width / 5.0 - ); - - // 判断是否在主界面的函数 - const isInMainUI = () => { - let captureRegion = captureGameRegion(); // 获取一张截图 - let res = captureRegion.Find(paimonMenuRo); - return !res.isEmpty(); - }; - - // 重复执行自动剧情,直到返回主界面 - let maxAttempts = 100; // 设置最大尝试次数,防止无限循环 - let attempts = 0; - await sleep(1000); - log.info("开始执行自动剧情循环"); - - while (!isInMainUI() && attempts < maxAttempts) { - attempts++; - log.info(`执行第 ${attempts} 次自动剧情选择`); - await genshin.chooseTalkOption("", 10, false); - - // 检查是否已返回主界面 - if (isInMainUI()) { - log.info("检测到已返回主界面,结束循环"); - break; - } - } - - if (isInMainUI()) { - log.info("已返回主界面,自动剧情执行完成"); - } else { - log.warn(`已达到最大尝试次数 ${maxAttempts},但未检测到返回主界面`); - } - } else { - // 其他未知命令 - log.warn("未知的流程命令: {command}", line); - } - - // 每个步骤之间等待一段时间 - await sleep(2000); - } - - log.info("简单格式的对话委托流程执行完成: {name}", commissionName); - return true; - } catch (error) { - log.error("执行简单格式的对话委托流程时出错: {error}", error); +async function iscompleted(buttonIndex) { + const enterSuccess = await enterCommissionScreen(); + if (!enterSuccess) { + log.error("无法进入委托界面,脚本终止"); return false; - } -} - -/** - * 执行JSON格式的对话委托流程 - * @param {string} processContent - 流程内容 - * @param {string} commissionName - 委托名称 - * @param {string} location - 委托地点 - * @returns {Promise} - 执行结果 - */ -async function executeJsonProcess(processContent, commissionName, location) { - try { - log.info("执行JSON格式的对话委托流程: {name}", commissionName); - - // 解析JSON内容 - const processSteps = JSON.parse(processContent); - - // 如果是数组,则按顺序执行每个步骤 - if (Array.isArray(processSteps)) { - for (let i = 0; i < processSteps.length; i++) { - const step = processSteps[i]; - log.info("执行流程步骤 {step}: {type}", i + 1, step.type); - - // 如果有注释,输出注释 - if (step.note) { - log.info("步骤说明: {note}", step.note); - } - - switch (step.type) { - case "地图追踪": - // 执行地图追踪 - log.info("执行地图追踪: {path}", step.data); - try { - await pathingScript.runFile(step.data); - log.info("地图追踪执行完成"); - } catch (error) { - log.error("执行地图追踪时出错: {error}", error); - } - break; - - case "键鼠脚本": - // 执行键鼠脚本 - log.info("执行键鼠脚本: {path}", step.data); - try { - await runFile(step.data); - log.info("键鼠脚本执行完成"); - } catch (error) { - log.error("执行键鼠脚本时出错: {error}", error); - } - break; - - case "对话": - // 执行对话 - log.info("执行对话流程"); - keyPress("F"); - - // 定义识别对象 - const paimonMenuRo = RecognitionObject.TemplateMatch( - file.ReadImageMatSync("Data/RecognitionObject/paimon_menu.png"), - 0, 0, genshin.width / 3.0, genshin.width / 5.0 - ); - - // 判断是否在主界面的函数 - const isInMainUI = () => { - let captureRegion = captureGameRegion(); // 获取一张截图 - let res = captureRegion.Find(paimonMenuRo); - return !res.isEmpty(); - }; - - // 重复执行自动剧情,直到返回主界面 - let maxAttempts = 100; // 设置最大尝试次数,防止无限循环 - let attempts = 0; - await sleep(1000); - log.info("开始执行自动剧情循环"); - - const skipCount = step.data || 10; // 默认跳过10次对话 - - while (!isInMainUI() && attempts < maxAttempts) { - attempts++; - log.info(`执行第 ${attempts} 次自动剧情选择`); - await genshin.chooseTalkOption("", skipCount, false); - - // 检查是否已返回主界面 - if (isInMainUI()) { - log.info("检测到已返回主界面,结束循环"); - break; - } - } - - if (isInMainUI()) { - log.info("已返回主界面,自动剧情执行完成"); - } else { - log.warn(`已达到最大尝试次数 ${maxAttempts},但未检测到返回主界面`); - } - break; - - case "按键": - // 执行按键操作 - if (typeof step.data === "string") { - // 单个按键 - log.info("执行按键: {key}", step.data); - keyPress(step.data); - } else if (typeof step.data === "object") { - // 复杂按键操作 - if (step.data.action === "down") { - log.info("按下按键: {key}", step.data.key); - keyDown(step.data.key); - } else if (step.data.action === "up") { - log.info("释放按键: {key}", step.data.key); - keyUp(step.data.key); - } else if (step.data.action === "press") { - log.info("点击按键: {key}", step.data.key); - keyPress(step.data.key); - } - } - break; - - case "tp": - // 执行传送 - if (Array.isArray(step.data) && step.data.length >= 2) { - log.info("执行传送: {x}, {y}", step.data[0], step.data[1]); - const force = step.data.length > 2 ? step.data[2] : false; - await genshin.tp(step.data[0], step.data[1], force); - log.info("传送完成"); - } else { - log.error("传送参数格式错误"); - } - break; - - default: - log.warn("未知的流程类型: {type}", step.type); - } - - // 每个步骤之间等待一段时间 - await sleep(2000); - } + }/* + if (buttonIndex >= 2 && buttonIndex < 4) { + const status = await detectCommissionStatusByImage(buttonIndex-2); + if (status === "completed") { + return true; + } else { + return false; + } + }else{*/ + await PageScroll(1); + const status = await detectCommissionStatusByImage(3); + if (status === "completed") { + return true; } else { - log.error("JSON流程格式错误,应为数组"); return false; } - - log.info("JSON格式的对话委托流程执行完成: {name}", commissionName); - return true; - } catch (error) { - log.error("执行JSON格式的对话委托流程时出错: {error}", error); - return false; - } } /** - * 执行委托追踪 - * 新版本:执行所有委托而不检测触发状态 - */ -async function executeCommissionTracking_old() { - try { - log.info("开始执行委托追踪 - 全部执行模式"); - - // 获取已识别的委托列表 - let commissions = []; - try { - const commissionsData = JSON.parse(file.readTextSync(`${OUTPUT_DIR}/commissions_data.json`)); - commissions = commissionsData.commissions.filter(c => c.supported); - log.info("已加载支持的委托数据,共 {count} 个", commissions.length); - } catch (error) { - log.error("读取委托数据失败: {error}", error); - return false; - } - - if (commissions.length === 0) { - log.warn("没有找到支持的委托,请先运行识别脚本"); - return false; - } - - // 确保回到主界面 - await genshin.returnMainUi(); - - // 执行每个委托 - let completedCount = 0; - for (const commission of commissions) { - // 跳过已完成的委托 - if (commission.location === "已完成") { - log.info("委托 {name} 已完成,跳过", commission.name); - completedCount++; - continue; - } - - // 跳过没有地点信息的委托 - if (!commission.location || commission.location === "未知地点" || commission.location === "识别失败") { - log.warn("委托 {name} 缺少地点信息,跳过", commission.name); - continue; - } - - log.info("开始执行委托: {name} ({location}) [{type}]", - commission.name, commission.location, commission.type || "未知类型"); - - // 根据委托类型执行不同的处理逻辑 - if (commission.type === COMMISSION_TYPE.TALK) { - // 执行对话委托 - const success = await executeTalkCommission(commission.name, commission.location); - if (success) { - completedCount++; - log.info("对话委托 {name} 执行完成", commission.name); - } else { - log.warn("对话委托 {name} 执行失败", commission.name); - } - } else { - // 默认执行战斗委托 - // 构建可能的路径追踪脚本路径 - const location = commission.location.trim(); - const scriptPaths = [ - `assets/${commission.name}/${location}-1.json`, - `assets/${commission.name}/${location}-2.json`, - `assets/${commission.name}/${location}-3.json`, - ]; - - let scriptFound = false; - let scriptExecuted = false; - - // 尝试执行每个可能的脚本路径 - for (const scriptPath of scriptPaths) { - try { - // 检查脚本文件是否存在 - log.info("检查路径追踪脚本: {path}", scriptPath); - - try { - // 尝试读取文件内容来检查是否存在 - await file.readText(scriptPath); - log.info("找到路径追踪脚本: {path}", scriptPath); - scriptFound = true; - scriptExecuted = true; - } catch (readError) { - log.info("路径追踪脚本不存在: {path}", scriptPath); - continue; // 尝试下一个脚本路径 - } - - // 执行路径追踪脚本 - log.info("开始执行路径追踪脚本: {path}", scriptPath); - - // 执行脚本并等待完成 - await pathingScript.runFile(scriptPath); - log.info("路径追踪脚本执行完成"); - } catch (scriptError) { - log.error("执行路径追踪脚本时出错: {error}", scriptError); - } - } - - if (!scriptFound) { - log.warn("未找到委托 {name} 在 {location} 的路径追踪脚本", commission.name, location); - } else if (scriptExecuted) { - completedCount++; - log.info("战斗委托 {name} 执行完成", commission.name); - } - } - - // 每个委托之间等待一段时间 - log.info("等待5秒后执行下一个委托..."); - await sleep(5000); - } - - log.info("委托追踪全部执行完成,共执行 {count}/{total} 个委托", - completedCount, commissions.length); - - return completedCount > 0; - } catch (error) { - log.error("执行委托追踪时出错: {error}", error); - return false; - } -} - -/** - * 执行委托追踪 - * 新版本:执行所有委托而不检测触发状态 + * 执行委托追踪(优化版 - 按距离排序) */ async function executeCommissionTracking() { try { - log.info("开始执行委托追踪 - 全部执行模式"); + log.info("开始执行委托追踪 - 按距离排序模式"); // 获取已识别的委托列表 let commissions = []; @@ -1149,11 +1218,15 @@ async function executeCommissionTracking() { log.info("开始执行委托: {name} ({location}) [{type}]", commission.name, commission.location, commission.type || "未知类型"); + + log.info("当前委托位置: ({x}, {y})", commission.playerPosition.X, commission.playerPosition.Y); + + let success = false; // 根据委托类型执行不同的处理逻辑 if (commission.type === COMMISSION_TYPE.TALK) { // 执行对话委托 - const success = await executeTalkCommission(commission.name, commission.location); + success = await executeTalkCommission(commission.name, commission.location); if (success) { completedCount++; log.info("对话委托 {name} 执行完成", commission.name); @@ -1161,22 +1234,63 @@ async function executeCommissionTracking() { log.warn("对话委托 {name} 执行失败", commission.name); } } else { + + //dispatcher.addTimer(new RealtimeTimer("AutoPick", { "forceInteraction": false })); + // 默认执行战斗委托 - // 构建可能的路径追踪脚本路径 const location = commission.location.trim(); + + // 脚本路径 const scriptPaths = [ `assets/${commission.name}/${location}-1.json`, `assets/${commission.name}/${location}-2.json`, - `assets/${commission.name}/${location}-3.json`, + `assets/${commission.name}/${location}-3.json` ]; - let scriptFound = false; - let scriptExecuted = false; - - // 尝试执行每个可能的脚本路径 + // 获取每个脚本对应的目标位置和距离 + const scriptInfo = []; for (const scriptPath of scriptPaths) { try { - // 检查脚本文件是否存在 + await file.readText(scriptPath); + const targetPos = await getCommissionTargetPosition(scriptPath); + if (targetPos) { + const distance = calculateDistance(commission.playerPosition, targetPos); + scriptInfo.push({ + path: scriptPath, + distance: distance, + valid: true + }); + log.info("委托 {name} 目标位置: ({x}, {y}),距离: {distance}", + scriptPath, targetPos.x, targetPos.y, distance); + } else { + log.warn("委托 {name} 无法获取距离", scriptPath); + scriptInfo.push({ + path: scriptPath, + distance: Infinity, + valid: false + }); + } + } catch (readError) { + log.info("路径追踪脚本不存在: {path}", scriptPath); + continue; + } + } + + // 按距离从小到大排序 + scriptInfo.sort((a, b) => a.distance - b.distance); + + // 输出排序结果 + log.info("排序后的脚本执行顺序:"); + scriptInfo.forEach((info, index) => { + log.info("{index}. 脚本: {path}, 距离: {distance}", index + 1, info.path, info.distance); + }); + + // 尝试执行排序后的脚本路径 + let executed = false; + for (const info of scriptInfo) { + const scriptPath = info.path; + try { + /* 检查脚本文件是否存在 log.info("检查路径追踪脚本: {path}", scriptPath); try { @@ -1184,28 +1298,31 @@ async function executeCommissionTracking() { await file.readText(scriptPath); log.info("找到路径追踪脚本: {path}", scriptPath); scriptFound = true; - scriptExecuted = true; } catch (readError) { log.info("路径追踪脚本不存在: {path}", scriptPath); continue; // 尝试下一个脚本路径 - } + }*/ // 执行路径追踪脚本 log.info("开始执行路径追踪脚本: {path}", scriptPath); - - // 执行脚本并等待完成 await pathingScript.runFile(scriptPath); log.info("路径追踪脚本执行完成"); + if(await iscompleted(commission.id)){ + log.info("委托 {name} 已完成", commission.name); + completedCount++; + success = true; + break; + }else{ + log.info("委托 {name} 未完成,尝试下一个脚本", commission.name); + } } catch (scriptError) { log.error("执行路径追踪脚本时出错: {error}", scriptError); + break; } } - if (!scriptFound) { - log.warn("未找到委托 {name} 在 {location} 的路径追踪脚本", commission.name, location); - } else if (scriptExecuted) { - completedCount++; - log.info("战斗委托 {name} 执行完成", commission.name); + if (!success) { + log.warn("委托 {name} 执行失败", commission.name); } } @@ -1219,7 +1336,7 @@ async function executeCommissionTracking() { return completedCount > 0; } catch (error) { - log.error("执行委托追踪时出错: {error}", error); + log.error("执行委托追踪时出错: {error}", error.message); return false; } } @@ -1228,15 +1345,15 @@ async function executeCommissionTracking() { async function main() { //await Identification(); - - if(settings.skipRecognition){ + + if(skipRecognition){ log.info("跳过识别,直接加载数据"); }else{ await Identification(); }//识别委托 // 开局准备 - await prepareForLeyLineRun(settings); + await prepareForLeyLineRun(); // 执行自动委托 await executeCommissionTracking(); @@ -1244,7 +1361,7 @@ async function executeCommissionTracking() { log.info("每日委托执行完成,前往安全地点"); await genshin.tpToStatueOfTheSeven(); } - // 修改这里:使用 Promise 包装 main 函数的执行 + // 使用 Promise 包装 main 函数的执行 return main(); //log.info(""); -})(); +})(); \ No newline at end of file diff --git a/repo/js/AutoEntrust/manifest.json b/repo/js/AutoEntrust/manifest.json index 0ec64da8..a21d77d5 100644 --- a/repo/js/AutoEntrust/manifest.json +++ b/repo/js/AutoEntrust/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 1, - "name": "(Beta)全自动蒙德地区每日委托", - "version": "0.95", + "name": "(对话+成就)全自动蒙德地区每日委托", + "version": "0.96", "bgi_version": "0.44.3", - "description": "基于BetterGI的原神自动化脚本,可自动识别每日委托内容、OCR识别委托地点和状态、自动追踪委托路径。目前支持蒙德地区委托,未来将支持全国家委托。使用前请阅读README.md(可用记事本打开)获取详细说明和常见问题解答。请关闭战斗后自动拾取,不然部分委托会发生错误并且浪费过多时间。如果发生错误,如不支持的战斗委托,请将脚本data/文件夹下面的数据,log/日志下今日日志,每日委托任务包含地点截图反馈给作者。反馈方法1.加3302871403QQ好友2.发送截图,日志给3302871403@qq.com。目前项目正处于开发阶段急需人手,有意者可私信作者(会在地图上标点即可)。感谢云闲vsv & 换苏伟u的共同开发", + "description": "(Beta)自动完成每日委托、自动识别委托内容、自动追踪委托路径、自动完成对话类委托。支持自动获得到隐藏成就。使用前请阅读README.md(可用记事本打开)获取详细说明和常见问题解答。目前项目正处于开发阶段急需人手,有意者可加1053392753(会在地图上标点即可)。每天只需花 5 ~ 10 分钟做一个蒙德城对话委托路径,就当是顺路补下成就。感谢云闲vsv & 换苏伟u的共同开发", "authors": [ { "name": "星野", diff --git a/repo/js/AutoEntrust/settings.json b/repo/js/AutoEntrust/settings.json index ac13422e..f5344aa8 100644 --- a/repo/js/AutoEntrust/settings.json +++ b/repo/js/AutoEntrust/settings.json @@ -14,10 +14,5 @@ "name": "team", "type": "input-text", "label": "战斗的队伍名称,不填表示不切换队伍" - }, - { - "name": "fight", - "type": "input-text", - "label": "自定义战斗脚本(研究中)" } ] \ No newline at end of file diff --git a/repo/js/AutoEntrust/对话委托制作方法.md b/repo/js/AutoEntrust/对话委托制作方法.md new file mode 100644 index 00000000..36efc125 --- /dev/null +++ b/repo/js/AutoEntrust/对话委托制作方法.md @@ -0,0 +1,45 @@ +对话委托制作方法可分为初始配置和制作过程,加群看完整版1053392753 具体如下: + +### 初始配置 +- **绑定启动停止路径记录器**:设置添加路径点快捷键。 +- **启动录制编辑器**:确保熟悉地图追踪制作,若不熟悉可参考相关教程。 + +### 制作过程 +1. **查看委托信息**:了解当前对话委托的名称及地点。 +2. **检查重复**:查看`AutoEntrust\assets\process\{委托名称}\{委托地点}`,若已有同名同地点的对话委托则无需重复制作。 +3. **结合多种元素制作**:将地图追踪、对话和键鼠脚本结合,在流程文件中定义各步骤的类型、数据等。 +4. **特定格式使用**:不熟悉JSON时,可使用简单版格式,如`XXX-1.json`表示地图追踪,`F`表示对话。 + +### 流程文件创建方法 +- **文件位置**:`\AutoEntrust\assets\process\{委托名称}\{委托地点}\process.json`。 +- **流程步骤定义**:包括地图追踪、对话、按键和传送等类型,每个步骤可按需设置属性。 + +### 示例 +```json +[ + { + "type": "地图追踪", + "data": "assets/process/example/tracking1.json", + "note": "执行第一个地图追踪步骤" + }, + { + "type": "对话", + "data": { + "skipCount": 5, + "priorityOptions": ["同意", "接受委托", "我来帮忙"], + "npcWhiteList": ["派蒙", "凯亚", "安柏"] + }, + "note": "执行对话步骤,优先选择特定选项并与白名单NPC交互" + }, + { + "type": "按键", + "data": "F", + "note": "按下F键" + }, + { + "type": "tp", + "data": [100, 200, true], + "note": "传送到坐标(100, 200),强制传送" + } +] +``` \ No newline at end of file diff --git a/repo/js/AutoEntrust/配置组配置参考.png b/repo/js/AutoEntrust/配置组配置参考.png new file mode 100644 index 00000000..26f35373 Binary files /dev/null and b/repo/js/AutoEntrust/配置组配置参考.png differ