diff --git a/repo/js/AutoFriendshipFight/README.md b/repo/js/AutoFriendshipFight/README.md new file mode 100644 index 00000000..b530697f --- /dev/null +++ b/repo/js/AutoFriendshipFight/README.md @@ -0,0 +1,98 @@ +# 自动好感度刷取脚本 (AutoFriendshipTreasureHunters) + +## 功能简介 + +自动化刷取角色好感度的脚本,支持盗宝团和愚人众两种敌人类型。通过自动触发和完成突发任务来获得好感度经验。 + +## 敌人类型支持 + +### 盗宝团好感 +- **触发任务**: 岛上无贼 - 消灭鬼鬼祟祟的盗宝团 +- **特殊功能**: 支持清理原住民(丘丘人) +- **战斗坐标**: 稻妻地区指定位置 + +### 愚人众好感 +- **触发任务**: + - 丘丘人今晚的伙食 - 打倒所有丘丘人 + - 买卖不成正义存 - 打倒愚人众与镀金旅团 + - 禁止危险运输! - 打倒所有盗宝团 +- **战斗坐标**: 须弥地区专门位置 + +## 使用前准备 + +### 1. BetterGI配置 +- **地图追踪行走配置**: + - ✅ 勾选"只在传送点时回复" + - ✅ 勾选"允许在JsScript使用" + - ✅ 勾选"覆盖JS中的自动战斗配置" + +- **战斗配置** + - **战斗策略**: 根据队伍选择合适的战斗策略文件 + - **练度要求**: 建议较高练度,纯好感队可能无法击败敌人 + - **重要设置**: ❌ 关闭"自动检测战斗结束" + +### 3. 队伍推荐 +- 至少1-2个主力输出角色 +- 建议携带治疗角色保证生存 +- 避免使用纯好感队伍(练度不足) + +## 脚本设置 + +### 基础设置 +- **敌人类型**: 选择"盗宝团"或"愚人众" +- **运行次数**: 设置刷取次数(默认10次) +- **OCR超时**: OCR检测超时时间(默认30秒) +- **战斗超时**: 单次战斗超时时间(默认120秒) + +### 高级设置 +- **清理原住民**: 仅盗宝团模式有效,清理丘丘人 +- **队伍名称**: 指定使用的队伍配置 +- **卡时间模式**: 支持按日期周期运行 + +## 运行流程 + +1. **队伍切换**: 自动切换到指定队伍 +2. **准备阶段**: + - 盗宝团: 可选清理丘丘人 → 前往触发点 + - 愚人众: 直接前往触发点 +3. **任务检测**: 使用OCR检测突发任务触发 +4. **战斗执行**: 自动导航到战斗点并开始战斗 +5. **结果判定**: 检测战斗结果并记录 +6. **循环执行**: 重复上述流程直到完成设定次数 + +## 注意事项 + +### ⚠️ 重要提醒 +- 脚本运行时请勿手动操作游戏 +- 确保网络稳定,避免OCR检测失败 +- 建议在非高峰时段运行,减少延迟影响 + +### 🔧 故障排除 +- **OCR检测失败**: 检查游戏界面是否清晰,调整OCR超时时间 +- **战斗失败**: 检查队伍配置和战斗策略,提升角色练度 +- **路径错误**: 确保相关路径文件存在于assets/AutoPath目录 + +### 📊 效率优化 +- 使用高练度队伍可提高战斗效率 +- 合理设置超时时间避免卡死 +- 选择合适的敌人类型以匹配队伍配置 + +## 文件依赖 + +- `main.js`: 主要脚本逻辑 +- `settings.json`: 配置文件 +- `assets/AutoPath/`: 路径文件目录 + - `盗宝团-准备.json`: 盗宝团准备阶段路径,清理丘丘人 + - `盗宝团-触发点.json`: 盗宝团触发点路径 + - `盗宝团-战斗点.json`: 盗宝团战斗点路径 + - `愚人众-准备.json`: 愚人众准备阶段路径,从传送点到触发点 + - `愚人众-触发点.json`: 愚人众触发点路径 + - `愚人众-战斗点.json`: 愚人众战斗点路径 + + +## 更新日志 + +- **1.3**: 新增愚人众支持,优化敌人类型切换 +- **1.2**: 战斗触发检测,异步检测战斗结束 +- **1.1**: 盗宝团重登功能优化 +- **1.0**: 基础盗宝团好感度刷取功能 \ No newline at end of file diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-准备.json b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-准备.json new file mode 100644 index 00000000..abccfa7e --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-准备.json @@ -0,0 +1,42 @@ +{ + "info": { + "name": "愚人众-准备", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "map_name": "Teyvat", + "bgi_version": "0.45.0", + "tags": [], + "last_modified_time": 1748154261518 + }, + "positions": [ + { + "id": 1, + "x": 4790.77, + "y": -3182.48, + "action": "", + "move_mode": "walk", + "action_params": "", + "type": "teleport" + }, + { + "id": 2, + "x": 4771.75, + "y": -3135.45, + "action": "", + "move_mode": "dash", + "action_params": "", + "type": "path" + }, + { + "id": 3, + "x": 4783.79, + "y": -3065.62, + "action": "", + "move_mode": "dash", + "action_params": "", + "type": "path" + } + ] +} \ No newline at end of file diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-战斗点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-战斗点.json new file mode 100644 index 00000000..01cc31ba --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-战斗点.json @@ -0,0 +1,24 @@ +{ + "info": { + "name": "愚人众-战斗点", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "map_name": "Teyvat", + "bgi_version": "0.45.0", + "tags": [], + "last_modified_time": 1748154217767 + }, + "positions": [ + { + "id": 1, + "x": 4840.55, + "y": -3078.01, + "type": "path", + "move_mode": "dash", + "action": "", + "action_params": "" + } + ] +} \ No newline at end of file diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json new file mode 100644 index 00000000..6f1b4de7 --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json @@ -0,0 +1,24 @@ +{ + "info": { + "name": "愚人众-触发点", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "map_name": "Teyvat", + "bgi_version": "0.45.0", + "tags": [], + "last_modified_time": 1748154261518 + }, + "positions": [ + { + "id": 1, + "x": 4783.79, + "y": -3065.62, + "action": "exit_and_relogin", + "move_mode": "dash", + "action_params": "", + "type": "path" + } + ] +} \ No newline at end of file diff --git a/repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/清理原住民.json b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json similarity index 89% rename from repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/清理原住民.json rename to repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json index da775f24..91ba4e1d 100644 --- a/repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/清理原住民.json +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json @@ -1,16 +1,16 @@ { "info": { - "name": "清理原住民", + "name": "盗宝团-准备", "type": "collect", "author": "HZYgrandma", "version": "1.0", "description": "", - "bgi_version": "0.35.1" + "bgi_version": "0.42.0" }, "positions": [ { "id": 1, - "x": -2740.60, + "x": -2740.6, "y": -3410.69, "action": "", "move_mode": "walk", diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json new file mode 100644 index 00000000..d4c793bb --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json @@ -0,0 +1,20 @@ +{ + "info": { + "name": "盗宝团-战斗点", + "type": "collect", + "author": "HZYgrandma", + "version": "1.0", + "description": "", + "bgi_version": "0.43.4" + }, + "positions": [ + { + "id": 1, + "x": -2756.67, + "y": -3467.63, + "type": "path", + "move_mode": "dash", + "action": "" + } + ] +} \ No newline at end of file diff --git a/repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/触发点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-触发点.json similarity index 100% rename from repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/触发点.json rename to repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-触发点.json diff --git a/repo/js/AutoFriendshipTreasureHunters/main.js b/repo/js/AutoFriendshipFight/main.js similarity index 69% rename from repo/js/AutoFriendshipTreasureHunters/main.js rename to repo/js/AutoFriendshipFight/main.js index fb4cbf58..87fabe3b 100644 --- a/repo/js/AutoFriendshipTreasureHunters/main.js +++ b/repo/js/AutoFriendshipFight/main.js @@ -49,23 +49,29 @@ const DEFAULT_FIGHT_TIMEOUT_SECONDS = 120; } else { log.info(`当前设置的运行次数: ${runTimes}`); } - - await switchPartyIfNeeded(settings.partyName); + await switchPartyIfNeeded(settings.partyName); - log.info('盗宝团好感开始...'); + // 获取敌人类型设置,默认为盗宝团 + const enemyType = settings.enemyType || "盗宝团"; + log.info(`当前选择的敌人类型: ${enemyType}`); + log.info(`${enemyType}好感开始...`); - // 清理丘丘人 - if(settings.qiuQiuRen){ + // 清理丘丘人(仅盗宝团需要) + if(settings.qiuQiuRen && enemyType === "盗宝团"){ log.info(`清理原住民...`); - await AutoPath('清理原住民'); + await AutoPath('盗宝团-准备'); } - // 验证超时设置 + if(enemyType === "愚人众"){ + log.info(`导航到愚人众触发点...`); + await AutoPath('愚人众-准备'); + } + // 验证超时设置 const ocrTimeout = validateTimeoutSetting(settings.ocrTimeout, DEFAULT_OCR_TIMEOUT_SECONDS, "OCR"); const fightTimeout = validateTimeoutSetting(settings.fightTimeout, DEFAULT_FIGHT_TIMEOUT_SECONDS, "战斗"); - // 盗宝团好感循环开始 - await AutoFriendshipDev(runTimes, ocrTimeout, fightTimeout); - log.info(`盗宝团好感运行总时长:${LogTimeTaken(startTime)}`); + // 好感循环开始 + await AutoFriendshipDev(runTimes, ocrTimeout, fightTimeout, enemyType); + log.info(`${enemyType}好感运行总时长:${LogTimeTaken(startTime)}`); })(); @@ -100,13 +106,16 @@ function CalculateEstimatedCompletion(startTime, current, total) { return `${completionDate.toLocaleTimeString()} (约 ${Math.round(remainingTime / 60000)} 分钟)`; } -// 执行 N 次盗宝团任务并输出日志 -async function AutoFriendshipDev(times, ocrTimeout, fightTimeout) { +// 执行 N 次好感任务并输出日志 +async function AutoFriendshipDev(times, ocrTimeout, fightTimeout, enemyType = "盗宝团") { let startFirstTime = Date.now(); for (let i = 0; i < times; i++) { - await AutoPath('触发点'); + await AutoPath(`${enemyType}-触发点`); // 启动路径导航任务 - let pathTaskPromise = AutoPath('盗宝团'); + let pathTaskPromise = AutoPath(`${enemyType}-战斗点`); + + // 根据敌人类型设置不同的OCR检测关键词 + const ocrKeywords = getOcrKeywords(enemyType); // OCR检测 let ocrStatus = false; @@ -116,15 +125,14 @@ async function AutoFriendshipDev(times, ocrTimeout, fightTimeout) { let resList = captureRegion.findMulti(RecognitionObject.ocr(0, 200, 300, 300)); for (let o = 0; o < resList.count; o++) { let res = resList[o]; - if (res.text.includes("岛上") - || res.text.includes("无贼") - || res.text.includes("消灭") - || res.text.includes("鬼鬼祟祟") - || res.text.includes("盗宝团")) { - ocrStatus = true; - log.info("检测到突发任务触发"); - break; + for (let keyword of ocrKeywords) { + if (res.text.includes(keyword)) { + ocrStatus = true; + log.info("检测到突发任务触发"); + break; + } } + if (ocrStatus) break; } if (!ocrStatus) { await sleep(1000); @@ -133,14 +141,12 @@ async function AutoFriendshipDev(times, ocrTimeout, fightTimeout) { if(ocrStatus){ const cts = new CancellationTokenSource(); - try { - // 设置最大等待时间为15秒 + try { // 设置最大等待时间为15秒 const maxWaitTime = 15000; const waitStartTime = Date.now(); - // 校验距离,如果距离小于10米,则认为已经到达目的地 - const targetX = -2756.67; - const targetY = -3467.63; + // 根据敌人类型设置不同的目标坐标 + const targetCoords = getTargetCoordinates(enemyType); const maxDistance = 10; // 10米距离判定 // 等待角色到达指定位置附近 @@ -155,12 +161,11 @@ async function AutoFriendshipDev(times, ocrTimeout, fightTimeout) { pathTaskFinished = true; log.error(`路径任务出错: ${error}`); }); - - // 等待角色到达目标位置或超时 + // 等待角色到达目标位置或超时 while (!isNearTarget && !pathTaskFinished && (Date.now() - waitStartTime < maxWaitTime)) { const pos = genshin.getPositionFromMap(); if (pos) { - const distance = Math.sqrt(Math.pow(pos.x - targetX, 2) + Math.pow(pos.y - targetY, 2)); + const distance = Math.sqrt(Math.pow(pos.x - targetCoords.x, 2) + Math.pow(pos.y - targetCoords.y, 2)); if (distance <= maxDistance) { isNearTarget = true; log.info(`已到达目标点附近,距离: ${distance.toFixed(2)}米`); @@ -168,32 +173,40 @@ async function AutoFriendshipDev(times, ocrTimeout, fightTimeout) { } } await sleep(1000); - } - - log.info("开始战斗..."); + } log.info("开始战斗..."); const battleTask = dispatcher.RunTask(new SoloTask("AutoFight"), cts); + const fightResultPromise = waitForBattleResult(fightTimeout * 1000, enemyType, cts); - let fightResult = await waitForBattleResult(fightTimeout * 1000) ? "成功" : "失败"; - log.info(`战斗任务已结束,战斗结果:${fightResult}`); + // 使用 Promise.all 等待两个任务完成 + const [battleResult, fightResult] = await Promise.all([ + battleTask.catch(error => { + return { success: false, error: error }; + }), + fightResultPromise // 不捕获超时错误,让它直接抛到外层 + ]); cts.cancel(); - await battleTask; } catch (error) { cts.cancel(); + if (error.message && error.message.includes("战斗超时")) { + log.error(`战斗超时,终止整个任务: ${error.message}`); + await genshin.tpToStatueOfTheSeven(); // 超时回到七天神像终止任务 + throw error; // 重新抛出超时错误,终止整个任务 + } log.error(`执行过程中出错: ${error}`); } const estimatedCompletion = CalculateEstimatedCompletion(startFirstTime, i + 1, times); const currentTime = LogTimeTaken(startFirstTime); log.info(`当前进度:${i + 1}/${times} (${((i + 1) / times * 100).toFixed(1)}%)`); - log.info(`当前运行总时长:${currentTime}`); + log.info(`当前运行总时长:${currentTime}`); log.info(`预计完成时间:${estimatedCompletion}`); } else { - notification.send(`未识别到突发任务(岛上无贼),盗宝团好感结束`); - log.info(`未识别到突发任务(岛上无贼),盗宝团好感结束`); + notification.send(`未识别到突发任务,${enemyType}好感结束`); + log.info(`未识别到突发任务,${enemyType}好感结束`); break; } } - log.info('盗宝团好感已完成'); - await genshin.tpToStatueOfTheSeven(); // 虽然不知道什么原因,但是不加这句会报错 + log.info(`${enemyType}好感已完成`); + // await genshin.tpToStatueOfTheSeven(); // 虽然不知道什么原因,但是不加这句会报错 } // 验证输入是否是正整数 @@ -201,6 +214,27 @@ function isPositiveInteger(value) { return Number.isInteger(value) && value > 0; } +// 根据敌人类型获取OCR关键词 +function getOcrKeywords(enemyType) { + if (enemyType === "愚人众") { + return ["买卖", "不成", "正义存", "愚人众","禁止","危险","运输","打倒","盗宝团"]; + } + else if (enemyType === "盗宝团") { + return ["岛上", "无贼", "消灭", "鬼鬼祟祟", "盗宝团"]; + } +} + +// 根据敌人类型获取目标战斗点坐标 +function getTargetCoordinates(enemyType) { + if (enemyType === "愚人众") { + // 愚人众战斗点坐标(需要根据实际位置调整) + return { x: 4840.55, y: -3078.01 }; // 这里需要替换为实际的愚人众战斗点坐标 + } else { + // 盗宝团战斗点坐标 + return { x: -2756.67, y: -3467.63 }; + } +} + // 验证日期格式 function isValidDateFormat(dateStr) { if (!dateStr) return false; @@ -244,11 +278,11 @@ async function switchPartyIfNeeded(partyName) { } } -async function waitForBattleResult(timeout = 2 * 60 * 1000) { +async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝团", cts = new CancellationTokenSource()) { let fightStartTime = Date.now(); const successKeywords = ["事件", "完成"]; const failureKeywords = ["失败"]; - const eventKeyword = ["岛上", "无贼","消灭","鬼鬼祟祟","盗宝团"]; + const eventKeywords = getOcrKeywords(enemyType); let notFind = 0; while (Date.now() - fightStartTime < timeout) { @@ -263,6 +297,8 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000) { for (let keyword of successKeywords) { if (text.includes(keyword)) { log.info("检测到战斗成功关键词: {0}", keyword); + log.info("战斗结果:成功"); + cts.cancel(); // 取消任务 return true; } } @@ -271,13 +307,19 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000) { for (let keyword of failureKeywords) { if (text.includes(keyword)) { log.warn("检测到战斗失败关键词: {0}", keyword); + log.warn("战斗结果:失败,回到七天神像重试"); + cts.cancel(); // 取消任务 + await genshin.tpToStatueOfTheSeven(); + if(enemyType=== "愚人众"){ + await AutoPath('愚人众-准备'); + } return false; } } // 检查事件关键词 let find = 0; - for(let keyword of eventKeyword) { + for(let keyword of eventKeywords) { if (text2.includes(keyword)) { find++; } @@ -292,7 +334,13 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000) { if (notFind > 10) { log.warn("不在任务触发区域,战斗失败"); + cts.cancel(); // 取消任务 + if(enemyType=== "愚人众"){ + log.warn("回到愚人众准备点"); + await AutoPath('愚人众-准备'); + } return false; + } } catch (error) { @@ -305,7 +353,8 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000) { } log.warn("在超时时间内未检测到战斗结果"); - return false; + cts.cancel(); // 取消任务 + throw new Error("战斗超时,未检测到结果"); } /** diff --git a/repo/js/AutoFriendshipFight/manifest.json b/repo/js/AutoFriendshipFight/manifest.json new file mode 100644 index 00000000..01a149bc --- /dev/null +++ b/repo/js/AutoFriendshipFight/manifest.json @@ -0,0 +1,19 @@ +{ + "manifest_version": 1, + "name": "战斗好感:自动好感度&卡时间", + "version": "1.3.0", + "tags": [ + "好感", + "盗宝团", + "突发事件", + "愚人众" + ], + "description": "通过战斗类突发事件刷好感度,刷盗宝团、愚人众材料,概率掉落兽肉,小怪锄地,卡时间等,请配合战斗策略使用。盗宝团部分在 HZYgrandma & 愚溪的原始脚本上改编。当前版本可能无法以任意方式拾取掉落物,如果有拾取掉落物需求,请勿更新。", + "authors": [ + { + "name": "秋云" + } + ], + "settings_ui": "settings.json", + "main": "main.js" +} \ No newline at end of file diff --git a/repo/js/AutoFriendshipTreasureHunters/settings.json b/repo/js/AutoFriendshipFight/settings.json similarity index 75% rename from repo/js/AutoFriendshipTreasureHunters/settings.json rename to repo/js/AutoFriendshipFight/settings.json index 489652e3..09cac2db 100644 --- a/repo/js/AutoFriendshipTreasureHunters/settings.json +++ b/repo/js/AutoFriendshipFight/settings.json @@ -1,8 +1,18 @@ [ + { + "name": "enemyType", + "type": "select", + "label": "选择敌人类型", + "options": [ + "盗宝团", + "愚人众" + ], + "default": "盗宝团" + }, { "name": "qiuQiuRen", "type": "checkbox", - "label": "是否清理丘丘人\n【默认否,选是会清理丘丘人之后再开始盗宝团好感】" + "label": "是否清理丘丘人\n【默认否,仅对盗宝团有效,选是会清理丘丘人之后再开始任务】" }, { "name": "partyName", @@ -35,7 +45,7 @@ "label": "OCR超时时间\n【选填,默认为30秒,如果经常提前判定未识别到任务,请适当调大】" }, { - "name": "fightTimeOut", + "name": "fightTimeout", "type": "input-text", "label": "战斗超时时间\n【选填,默认为120秒】" } diff --git a/repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/盗宝团.json b/repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/盗宝团.json deleted file mode 100644 index 1313987f..00000000 --- a/repo/js/AutoFriendshipTreasureHunters/assets/AutoPath/盗宝团.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "info": { - "name": "盗宝团", - "type": "collect", - "author": "HZYgrandma", - "version": "1.0", - "description": "", - "bgi_version": "0.43.4" - }, - "positions": [ - { - "id": 1, - "x": -2756.67, - "y": -3467.63, - "type": "path", - "move_mode": "dash", - "action": "" - } - ] - } \ No newline at end of file diff --git a/repo/js/AutoFriendshipTreasureHunters/manifest.json b/repo/js/AutoFriendshipTreasureHunters/manifest.json deleted file mode 100644 index 13a6c53b..00000000 --- a/repo/js/AutoFriendshipTreasureHunters/manifest.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "manifest_version": 1, - "name": "盗宝团好感:自动好感度&刷盗宝团&卡时间", - "version": "1.2.2", - "tags": ["好感", "盗宝团", "突发事件"], - "description": "通过突发事件【岛上无贼】刷好感度,刷盗宝团材料,小怪锄地,卡时间等,请配合战斗脚本使用。在 HZYgrandma & 愚溪的原始脚本上改编。当前版本可能无法以任意方式拾取掉落物,如果有拾取掉落物需求,请勿更新。", - "authors": [ - { - "name": "秋云" - } - ], - "settings_ui": "settings.json", - "main": "main.js" -} \ No newline at end of file