From b39e6cca65ca46d5ad046dd0d32c9681e42e76fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E4=BA=91?= Date: Sat, 31 May 2025 20:53:54 +0800 Subject: [PATCH] =?UTF-8?q?add:=20=E9=B3=84=E9=B1=BC=E5=A5=BD=E6=84=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/js/AutoFriendshipFight/README.md | 7 + .../assets/AutoPath/愚人众-触发点.json | 2 +- .../assets/AutoPath/盗宝团-准备.json | 12 +- .../assets/AutoPath/盗宝团-战斗点.json | 6 +- .../assets/AutoPath/盗宝团-触发点.json | 13 +- .../assets/AutoPath/鳄鱼-准备.json | 39 ++ .../assets/AutoPath/鳄鱼-战斗点.json | 30 ++ .../assets/AutoPath/鳄鱼-拾取.json | 30 ++ .../assets/AutoPath/鳄鱼-触发点.json | 30 ++ repo/js/AutoFriendshipFight/main.js | 503 +++++++++++------- repo/js/AutoFriendshipFight/manifest.json | 7 +- repo/js/AutoFriendshipFight/settings.json | 9 +- 12 files changed, 484 insertions(+), 204 deletions(-) create mode 100644 repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-准备.json create mode 100644 repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-战斗点.json create mode 100644 repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-拾取.json create mode 100644 repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-触发点.json diff --git a/repo/js/AutoFriendshipFight/README.md b/repo/js/AutoFriendshipFight/README.md index 3b95aa61..3830914c 100644 --- a/repo/js/AutoFriendshipFight/README.md +++ b/repo/js/AutoFriendshipFight/README.md @@ -40,6 +40,13 @@ | **活动地区** | 须弥地区 | | **敌人强度** | ⭐⭐⭐⭐ (较高) | + +#### 鳄鱼 +| 属性 | 详情 | +|------|------| +| **触发任务** | 张牙舞爪的恶党 - 打倒所有鳄鱼 | +| **活动地区** | 须弥地区 | +| **敌人强度** | ⭐⭐ (较低) | ### 快速开始 #### 环境准备 ✅ 安装 BetterGI,版本大于 0.44.7 diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json index 6f1b4de7..ae1e0df9 100644 --- a/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/愚人众-触发点.json @@ -15,7 +15,7 @@ "id": 1, "x": 4783.79, "y": -3065.62, - "action": "exit_and_relogin", + "action": "", "move_mode": "dash", "action_params": "", "type": "path" diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json index 74930bfa..d4e850b5 100644 --- a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-准备.json @@ -11,24 +11,24 @@ "positions": [ { "id": 1, - "x": -2740.6000000000004, - "y": -3410.6900000000005, + "x": -2740.60, + "y": -3410.69, "action": "", "move_mode": "walk", "type": "teleport" }, { "id": 2, - "x": -2764.4062480249577, - "y": -3465.687530315927, + "x": -2764.40, + "y": -3465.68, "action": "", "move_mode": "dash", "type": "path" }, { "id": 3, - "x": -2770.4169921875, - "y": -3450.8349609375, + "x": -2770.41, + "y": -3450.83, "type": "path", "move_mode": "dash", "action": "fight", diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json index 14a4a627..54434651 100644 --- a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-战斗点.json @@ -12,7 +12,7 @@ { "id": 1, "x": -2741.25, - "y": -3441, + "y": -3441.00, "action": "", "move_mode": "dash", "action_params": "", @@ -20,8 +20,8 @@ }, { "id": 2, - "x": -2757.28125, - "y": -3468.4376106653963, + "x": -2757.28, + "y": -3468.43, "action": "", "move_mode": "dash", "action_params": "", diff --git a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-触发点.json b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-触发点.json index 2d6ab148..425ed273 100644 --- a/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-触发点.json +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/盗宝团-触发点.json @@ -20,22 +20,13 @@ }, { "id": 2, - "x": -2736.6005859375, - "y": -3415.4443359375, + "x": -2736.60, + "y": -3415.44, "type": "path", "move_mode": "walk", "action": "combat_script", "action_params": "wait(0.5)", "locked": false - }, - { - "id": 3, - "x": -2736.6005859375, - "y": -3415.4443359375, - "type": "path", - "move_mode": "walk", - "action": "exit_and_relogin", - "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..a9237f6a --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-准备.json @@ -0,0 +1,39 @@ +{ + "info": { + "name": "鳄鱼-准备", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "bgi_version": "0.45.1" + }, + "positions": [ + { + "id": 1, + "action": "", + "move_mode": "walk", + "type": "teleport", + "x": 3705.53, + "y": -497.45, + "action_params": "" + }, + { + "id": 2, + "x": 3615.48, + "y": -521.27, + "type": "path", + "move_mode": "fly", + "action": "stop_flying", + "action_params": "2500" + }, + { + "id": 3, + "x": 3615.48, + "y": -521.27, + "type": "path", + "move_mode": "walk", + "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..bdda9cf2 --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-战斗点.json @@ -0,0 +1,30 @@ +{ + "info": { + "name": "鳄鱼-战斗点", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "bgi_version": "0.45.1" + }, + "positions": [ + { + "id": 1, + "x": 3614.63, + "y": -521.6, + "action": "", + "move_mode": "dash", + "action_params": "", + "type": "path" + }, + { + "id": 2, + "x": 3578.08, + "y": -500.75, + "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..9cc6480d --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-拾取.json @@ -0,0 +1,30 @@ +{ + "info": { + "name": "鳄鱼-拾取", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "bgiVersion": "0.42.3" + }, + "positions": [ + { + "id": 1, + "x": 3576.7568359375, + "y": -495.845703125, + "type": "path", + "move_mode": "walk", + "action": "", + "action_params": "" + }, + { + "id": 2, + "x": 3572.375, + "y": -502.4375, + "action": "", + "move_mode": "walk", + "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..583aaf3d --- /dev/null +++ b/repo/js/AutoFriendshipFight/assets/AutoPath/鳄鱼-触发点.json @@ -0,0 +1,30 @@ +{ + "info": { + "name": "鳄鱼-触发点", + "type": "collect", + "author": "秋云", + "version": "1.0", + "description": "", + "bgi_version": "0.45.1" + }, + "positions": [ + { + "id": 1, + "x": 3605.18, + "y": -513.31, + "type": "path", + "move_mode": "dash", + "action": "", + "action_params": "" + }, + { + "id": 1, + "x": 3614.63, + "y": -521.6, + "action": "", + "move_mode": "dash", + "action_params": "", + "type": "path" + } + ] +} \ No newline at end of file diff --git a/repo/js/AutoFriendshipFight/main.js b/repo/js/AutoFriendshipFight/main.js index ea32e737..717071d0 100644 --- a/repo/js/AutoFriendshipFight/main.js +++ b/repo/js/AutoFriendshipFight/main.js @@ -9,69 +9,34 @@ const DEFAULT_FIGHT_TIMEOUT_SECONDS = 120; // 启用自动拾取的实时任务 const startTime = Date.now(); dispatcher.addTimer(new RealtimeTimer("AutoPick")); - log.info(`'请确保队伍满员,并为队伍配置相应的战斗策略'`); - // 计算运行次数 - let runTimes = Number(settings.runTimes); - if(!isPositiveInteger(runTimes) && !settings.waitTimeMode){ - log.warn("请输入正确的次数,必须是正整数!"); - log.warn(`运行次数重置为 ${DEFAULT_RUNS} 次!`); - runTimes = DEFAULT_RUNS; - } - - if(settings.waitTimeMode){ - if(!isPositiveInteger(runTimes)){ - log.warn("运行次数必须是正整数,使用默认基准次数"); - log.warn(`运行次数重置为 ${DEFAULT_BASE_RUNS} 次!`); - runTimes = DEFAULT_BASE_RUNS; - } - - // 验证日期格式 - const waitTimeModeDay = settings.waitTimeModeDay; - if (!isValidDateFormat(waitTimeModeDay)) { - log.error("基准日期格式错误,请检查后重试!"); - log.error("参考格式:2025-01-01"); - log.error(`错误输入:${waitTimeModeDay}`); - await sleep(5000); - return; - } - - let period = Number(settings.waitTimeModePeriod); - if(!isPositiveInteger(period) || period > runTimes){ - period = DEFAULT_PERIOD < runTimes? DEFAULT_PERIOD : runTimes; - log.warn(`卡时间模式周期必须是 1-${runTimes} 之间的正整数!使用 ${period} 作为周期`); - } - runTimes = calculateWaitModeRuns(runTimes, waitTimeModeDay, period); - - // 添加日志输出,提醒用户当前使用的基准日期和周期 - log.info(`当前使用的基准日期: ${waitTimeModeDay}`); - log.info(`当前使用的周期: ${period} 天`); - log.info(`根据基准日期和周期计算,今日运行次数: ${runTimes}`); - } else { - log.info(`当前设置的运行次数: ${runTimes}`); - } + runTimes = await calulateRunTimes(); await switchPartyIfNeeded(settings.partyName); - // 获取敌人类型设置,默认为盗宝团 - const enemyType = settings.enemyType || "盗宝团"; - log.info(`当前选择的敌人类型: ${enemyType}`); - log.info(`${enemyType}好感开始...`); - - // 清理丘丘人(仅盗宝团需要) - if(settings.qiuQiuRen && enemyType === "盗宝团"){ - log.info(`清理原住民...`); - await AutoPath('盗宝团-准备'); - } - if(enemyType === "愚人众"){ + // 获取敌人类型设置,默认为盗宝团 + const enemyType = settings.enemyType || "盗宝团"; + log.info(`当前选择的敌人类型: ${enemyType}`); + log.info(`${enemyType}好感开始...`); + + // 清理丘丘人(仅盗宝团需要) + if (settings.qiuQiuRen && enemyType === "盗宝团") { + log.info(`清理原住民...`); + await AutoPath('盗宝团-准备'); + } + if (enemyType === "愚人众") { log.info(`导航到愚人众触发点...`); 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, enemyType); - log.info(`${enemyType}好感运行总时长:${LogTimeTaken(startTime)}`); + await AutoFriendshipDev(runTimes, ocrTimeout, fightTimeout, enemyType); + log.info(`${enemyType}好感运行总时长:${LogTimeTaken(startTime)}`); })(); @@ -106,108 +71,265 @@ function CalculateEstimatedCompletion(startTime, current, total) { return `${completionDate.toLocaleTimeString()} (约 ${Math.round(remainingTime / 60000)} 分钟)`; } -// 执行 N 次好感任务并输出日志 -async function AutoFriendshipDev(times, ocrTimeout, fightTimeout, enemyType = "盗宝团") { - let startFirstTime = Date.now(); - for (let i = 0; i < times; i++) { - await AutoPath(`${enemyType}-触发点`); - // 启动路径导航任务 - let pathTaskPromise = AutoPath(`${enemyType}-战斗点`); - - // 根据敌人类型设置不同的OCR检测关键词 - const ocrKeywords = getOcrKeywords(enemyType); - - // OCR检测 - let ocrStatus = false; - let ocrStartTime = Date.now(); - while (Date.now() - ocrStartTime < ocrTimeout * 1000 && !ocrStatus) { - let captureRegion = captureGameRegion(); - let resList = captureRegion.findMulti(RecognitionObject.ocr(0, 200, 300, 300)); - for (let o = 0; o < resList.count; o++) { - let res = resList[o]; - for (let keyword of ocrKeywords) { - if (res.text.includes(keyword)) { - ocrStatus = true; - log.info("检测到突发任务触发"); - break; - } - } - if (ocrStatus) break; - } - if (!ocrStatus) { - await sleep(1000); - } - } - - if(ocrStatus){ - const cts = new CancellationTokenSource(); - try { // 设置最大等待时间为15秒 - const maxWaitTime = 15000; - const waitStartTime = Date.now(); - - // 根据敌人类型设置不同的目标坐标 - const targetCoords = getTargetCoordinates(enemyType); - const maxDistance = 10; // 10米距离判定 - - // 等待角色到达指定位置附近 - let isNearTarget = false; - let pathTaskFinished = false; - - // 简单监控路径任务完成 - pathTaskPromise.then(() => { - pathTaskFinished = true; - log.info("路径任务已完成"); - }).catch(error => { - 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 - targetCoords.x, 2) + Math.pow(pos.y - targetCoords.y, 2)); - if (distance <= maxDistance) { - isNearTarget = true; - log.info(`已到达目标点附近,距离: ${distance.toFixed(2)}米`); - break; - } - } - await sleep(1000); - } log.info("开始战斗..."); - const battleTask = dispatcher.RunTask(new SoloTask("AutoFight"), cts); - const fightResultPromise = waitForBattleResult(fightTimeout * 1000, enemyType, cts); - - // 使用 Promise.all 等待两个任务完成 - const [battleResult, fightResult] = await Promise.all([ - battleTask.catch(error => { - return { success: false, error: error }; - }), - fightResultPromise // 不捕获超时错误,让它直接抛到外层 - ]); - await pathTaskPromise; // 等待路径任务完成 - cts.cancel(); - } 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(`预计完成时间:${estimatedCompletion}`); +// 检查并导航到触发点 +async function navigateToTriggerPoint(enemyType) { + await AutoPath(`${enemyType}-触发点`); + const triggerPoint = getTriggerPoint(enemyType); + const pos = genshin.getPositionFromMap(); + + if (pos) { + const distance = Math.sqrt(Math.pow(pos.x - triggerPoint.x, 2) + Math.pow(pos.y - triggerPoint.y, 2)); + if (distance <= 8) { + log.info(`已到达触发点附近,距离: ${distance.toFixed(2)}米`); } else { - notification.send(`未识别到突发任务,${enemyType}好感结束`); - log.info(`未识别到突发任务,${enemyType}好感结束`); - break; + log.info(`未到达触发点,当前距离: ${distance.toFixed(2)}米,正在导航...`); + await AutoPath(`${enemyType}-触发点`); } } +} + +// OCR检测突发任务 +async function detectTaskTrigger(ocrTimeout, enemyType) { + const ocrKeywords = getOcrKeywords(enemyType); + let ocrStatus = false; + let ocrStartTime = Date.now(); + + while (Date.now() - ocrStartTime < ocrTimeout * 1000 && !ocrStatus) { + let captureRegion = captureGameRegion(); + let resList = captureRegion.findMulti(RecognitionObject.ocr(0, 200, 300, 300)); + + for (let o = 0; o < resList.count; o++) { + let res = resList[o]; + for (let keyword of ocrKeywords) { + if (res.text.includes(keyword)) { + ocrStatus = true; + log.info("检测到突发任务触发"); + break; + } + } + if (ocrStatus) break; + } + + if (!ocrStatus) { + await sleep(1000); + } + } + + return ocrStatus; +} + +// 等待角色到达目标位置 +async function waitForTargetPosition(pathTask, targetCoords, maxWaitTime = 15000) { + const waitStartTime = Date.now(); + const maxDistance = 5; + let isNearTarget = false; + let pathTaskFinished = false; + + // 监控路径任务完成 + pathTask.then(() => { + pathTaskFinished = true; + log.info("路径任务已完成"); + }).catch(error => { + 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 - targetCoords.x, 2) + Math.pow(pos.y - targetCoords.y, 2)); + if (distance <= maxDistance) { + isNearTarget = true; + log.info(`已到达目标点附近,距离: ${distance.toFixed(2)}米`); + break; + } + } + await sleep(1000); + } + + return { isNearTarget, pathTaskFinished }; +} + +// 执行战斗任务(并发执行战斗和结果检测) +async function executeBattleTasks(fightTimeout, enemyType, cts) { + log.info("开始战斗!"); + + let battleTask; + let battleResult = null; + let fightResult = null; + + try { + battleTask = dispatcher.RunTask(new SoloTask("AutoFight"), cts); + const battleDetectTask = waitForBattleResult(fightTimeout * 1000, enemyType, cts); + + // 使用 Promise.allSettled 而不是 Promise.all,这样可以处理部分成功的情况 + const results = await Promise.allSettled([ + battleTask.catch(error => { + // 如果是取消错误(成功检测后的正常取消),不算真正的错误 + if (error.message && error.message.includes("取消自动任务")) { + log.info("战斗任务已被成功取消"); + return { cancelled: true }; + } + throw error; // 其他错误继续抛出 + }), + battleDetectTask + ]); + + battleResult = results[0]; + fightResult = results[1]; + + // 检查检测任务是否成功 + if (fightResult.status === 'fulfilled') { + log.info("战斗检测任务完成"); + return { success: true, battleResult: battleResult.value, fightResult: fightResult.value }; + } else if (fightResult.status === 'rejected') { + throw fightResult.reason; + } + + } catch (error) { + if (error.message && error.message.includes("战斗超时")) { + log.error(`战斗超时,终止整个任务: ${error.message}`); + await genshin.tpToStatueOfTheSeven(); + throw error; + } + + // 过滤掉正常的取消错误 + if (error.message && error.message.includes("取消自动任务")) { + log.info("战斗任务正常取消(战斗检测成功)"); + return { success: true, cancelled: true }; + } + + log.error(`战斗执行过程中出错: ${error.message}`); + throw error; + } finally { + // 确保战斗任务被等待完成(即使被取消) + if (battleTask) { + try { + await battleTask; + } catch (error) { + // 忽略 finally 块中的取消错误 + if (!error.message || !error.message.includes("取消自动任务")) { + log.warn(`清理战斗任务时出错: ${error.message}`); + } + } + } + } +} + +// 执行单次好感任务循环 +async function executeSingleFriendshipRound(roundIndex, ocrTimeout, fightTimeout, enemyType) { + // 导航到触发点 + await navigateToTriggerPoint(enemyType); + if (roundIndex === 0 && enemyType === "鳄鱼") { + await sleep(5000); + } + + // 重新登录 + await genshin.relogin(); + + // 启动路径导航任务(异步) + let pathTask = AutoPath(`${enemyType}-战斗点`); + const ocrStatus = await detectTaskTrigger(ocrTimeout, enemyType); + + if (!ocrStatus) { + notification.send(`未识别到突发任务,${enemyType}好感结束`); + log.info(`未识别到突发任务,${enemyType}好感结束`); + await pathTask; // 防止报错 + return false; // 返回 false 表示需要终止循环 + } + + const cts = new CancellationTokenSource(); + + const targetCoords = getTargetCoordinates(enemyType); + await waitForTargetPosition(pathTask, targetCoords); + await executeBattleTasks(fightTimeout, enemyType, cts); + await pathTask; + + // 特殊处理:鳄鱼战斗后需要拾取 + if (enemyType === "鳄鱼") { + await AutoPath('鳄鱼-拾取'); + } + + // 返回 true 表示成功完成这一轮 + return true; +} + +// 记录进度信息 +function logProgress(startTime, currentRound, totalRounds) { + const estimatedCompletion = CalculateEstimatedCompletion(startTime, currentRound + 1, totalRounds); + const currentTime = LogTimeTaken(startTime); + log.info(`当前进度:${currentRound + 1}/${totalRounds} (${((currentRound + 1) / totalRounds * 100).toFixed(1)}%)`); + log.info(`当前运行总时长:${currentTime}`); + log.info(`预计完成时间:${estimatedCompletion}`); +} + +// 执行 N 次好感任务并输出日志 - 重构后的主函数 +async function AutoFriendshipDev(times, ocrTimeout, fightTimeout, enemyType = "盗宝团") { + const startFirstTime = Date.now(); + for (let i = 0; i < times; i++) { + try { + const success = await executeSingleFriendshipRound(i, ocrTimeout, fightTimeout, enemyType); + if (!success) + break; + logProgress(startFirstTime, i, times); + } catch (error) { + log.error(`第 ${i + 1} 轮好感任务失败: ${error.message}`); + + // 如果是战斗超时错误,直接终止整个任务 + if (error.message && error.message.includes("战斗超时")) { + throw error; + } + continue; + } + } + log.info(`${enemyType}好感已完成`); - // await genshin.tpToStatueOfTheSeven(); // 虽然不知道什么原因,但是不加这句会报错 +} + + +async function calulateRunTimes() { + log.info(`'请确保队伍满员,并为队伍配置相应的战斗策略'`); + // 计算运行次数 + let runTimes = Number(settings.runTimes); + if (!isPositiveInteger(runTimes) && !settings.waitTimeMode) { + log.warn("请输入正确的次数,必须是正整数!"); + log.warn(`运行次数重置为 ${DEFAULT_RUNS} 次!`); + runTimes = DEFAULT_RUNS; + } + + if (settings.waitTimeMode) { + if (!isPositiveInteger(runTimes)) { + log.warn("运行次数必须是正整数,使用默认基准次数"); + log.warn(`运行次数重置为 ${DEFAULT_BASE_RUNS} 次!`); + runTimes = DEFAULT_BASE_RUNS; + } + + // 验证日期格式 + const waitTimeModeDay = settings.waitTimeModeDay; + if (!isValidDateFormat(waitTimeModeDay)) { + log.error("基准日期格式错误,请检查后重试!"); + log.error("参考格式:2025-01-01"); + log.error(`错误输入:${waitTimeModeDay}`); + await sleep(5000); + return; + } + + let period = Number(settings.waitTimeModePeriod); + if (!isPositiveInteger(period) || period > runTimes) { + period = DEFAULT_PERIOD < runTimes ? DEFAULT_PERIOD : runTimes; + log.warn(`卡时间模式周期必须是 1-${runTimes} 之间的正整数!使用 ${period} 作为周期`); + } + runTimes = calculateWaitModeRuns(runTimes, waitTimeModeDay, period); + + // 添加日志输出,提醒用户当前使用的基准日期和周期 + log.info(`当前使用的基准日期: ${waitTimeModeDay}`); + log.info(`当前使用的周期: ${period} 天`); + log.info(`根据基准日期和周期计算,今日运行次数: ${runTimes}`); + } else { + log.info(`当前设置的运行次数: ${runTimes}`); + } + return runTimes; } // 验证输入是否是正整数 @@ -218,21 +340,41 @@ function isPositiveInteger(value) { // 根据敌人类型获取OCR关键词 function getOcrKeywords(enemyType) { if (enemyType === "愚人众") { - return ["买卖", "不成", "正义存", "愚人众","禁止","危险","运输","打倒","盗宝团"]; - } + return ["买卖", "不成", "正义存", "愚人众", "禁止", "危险", "运输", "打倒", "盗宝团", "丘丘人", "今晚", "伙食", "所有人"]; + } else if (enemyType === "盗宝团") { return ["岛上", "无贼", "消灭", "鬼鬼祟祟", "盗宝团"]; } + else if (enemyType === "鳄鱼") { + return ["张牙", "舞爪", "恶党", "鳄鱼", "打倒", "所有", "鳄鱼"]; + } + else { + return ["突发", "任务", "打倒", "消灭", "敌人", "所有"]; // 兜底关键词 + } } // 根据敌人类型获取目标战斗点坐标 function getTargetCoordinates(enemyType) { if (enemyType === "愚人众") { - // 愚人众战斗点坐标(需要根据实际位置调整) - return { x: 4840.55, y: -3078.01 }; // 这里需要替换为实际的愚人众战斗点坐标 - } else { + return { x: 4840.55, y: -3078.01 }; + } else if (enemyType === "盗宝团") { // 盗宝团战斗点坐标 - return { x: -2757.281, y: -3468.437 }; + return { x: -2757.28, y: -3468.43 }; + } else if (enemyType === "鳄鱼") { + // 鳄鱼战斗点坐标 + return { x: 3578.08, y: -500.75 }; + } +} + +function getTriggerPoint(enemyType) { + if (enemyType === "愚人众") { + return { x: 4783.79, y: -3065.62 }; // 愚人众触发点坐标 + } + else if (enemyType === "盗宝团") { + return { x: -2736.60, y: -3415.44 }; // 盗宝团触发点坐标 + } + else if (enemyType === "鳄鱼") { + return { x: 3614.63, y: -521.60 }; // 鳄鱼触发点坐标 } } @@ -267,7 +409,7 @@ async function switchPartyIfNeeded(partyName) { } try { log.info("正在尝试切换至" + partyName); - if(!await genshin.switchParty(partyName)){ + if (!await genshin.switchParty(partyName)) { log.info("切换队伍失败,前往七天神像重试"); await genshin.tpToStatueOfTheSeven(); await genshin.switchParty(partyName); @@ -285,7 +427,7 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝 const failureKeywords = ["失败"]; const eventKeywords = getOcrKeywords(enemyType); let notFind = 0; - + while (Date.now() - fightStartTime < timeout) { try { // 简化OCR检测,只使用一个try-catch块 @@ -293,7 +435,7 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝 let result2 = captureGameRegion().find(RecognitionObject.ocr(0, 200, 300, 300)); let text = result.text; let text2 = result2.text; - + // 检查成功关键词 for (let keyword of successKeywords) { if (text.includes(keyword)) { @@ -303,7 +445,7 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝 return true; } } - + // 检查失败关键词 for (let keyword of failureKeywords) { if (text.includes(keyword)) { @@ -311,48 +453,53 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝 log.warn("战斗结果:失败,回到七天神像重试"); cts.cancel(); // 取消任务 await genshin.tpToStatueOfTheSeven(); - if(enemyType=== "愚人众"){ - await AutoPath('愚人众-准备'); + if (enemyType === "愚人众") { + await AutoPath('愚人众-准备'); } return false; } } - + // 检查事件关键词 let find = 0; - for(let keyword of eventKeywords) { + for (let keyword of eventKeywords) { if (text2.includes(keyword)) { find++; } } - - if(find === 0) { + + if (find === 0) { notFind++; log.info("未检测到任务触发关键词:{0} 次", notFind); - }else{ + } else { notFind = 0; } - + if (notFind > 10) { log.warn("不在任务触发区域,战斗失败"); cts.cancel(); // 取消任务 - if(enemyType=== "愚人众"){ + if (enemyType === "愚人众") { log.warn("回到愚人众准备点"); - await AutoPath('愚人众-准备'); + await AutoPath('愚人众-准备'); + } + if (enemyType === "鳄鱼") { + log.warn("回到鳄鱼准备点"); + await AutoPath('鳄鱼-准备'); + await sleep(5000); } return false; - + } } catch (error) { log.error("OCR过程中出错: {0}", error); // 出错后继续循环,不进行额外嵌套处理 } - + // 统一的检查间隔 await sleep(1000); } - + log.warn("在超时时间内未检测到战斗结果"); cts.cancel(); // 取消任务 throw new Error("战斗超时,未检测到结果"); @@ -368,13 +515,13 @@ async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝 function validateTimeoutSetting(value, defaultValue, timeoutType) { // 转换为数字 const timeout = Number(value); - + // 检查是否为有效数字且大于0 if (!isFinite(timeout) || timeout <= 0) { log.warn(`${timeoutType}超时设置无效,必须是大于0的数字,将使用默认值 ${defaultValue} 秒`); return defaultValue; } - + log.info(`${timeoutType}超时设置为 ${timeout} 秒`); return timeout; } diff --git a/repo/js/AutoFriendshipFight/manifest.json b/repo/js/AutoFriendshipFight/manifest.json index 60b58abf..81c8d45e 100644 --- a/repo/js/AutoFriendshipFight/manifest.json +++ b/repo/js/AutoFriendshipFight/manifest.json @@ -1,12 +1,15 @@ { "manifest_version": 1, "name": "战斗好感:自动好感度&卡时间", - "version": "1.3.3", + "version": "1.4", + "bgi_version": "0.45.1", "tags": [ "好感", "盗宝团", "突发事件", - "愚人众" + "愚人众", + "鳄鱼", + "兽肉" ], "description": "通过战斗类突发事件刷好感度,刷盗宝团、愚人众材料,概率掉落兽肉,小怪锄地,卡时间等,请配合战斗策略使用。盗宝团部分在 HZYgrandma & 愚溪的原始脚本上改编。当前版本可能无法以任意方式拾取掉落物,如果有拾取掉落物需求,请勿更新。", "authors": [ diff --git a/repo/js/AutoFriendshipFight/settings.json b/repo/js/AutoFriendshipFight/settings.json index 09cac2db..1b471e43 100644 --- a/repo/js/AutoFriendshipFight/settings.json +++ b/repo/js/AutoFriendshipFight/settings.json @@ -5,7 +5,8 @@ "label": "选择敌人类型", "options": [ "盗宝团", - "愚人众" + "愚人众", + "鳄鱼" ], "default": "盗宝团" }, @@ -42,11 +43,13 @@ { "name": "ocrTimeout", "type": "input-text", - "label": "OCR超时时间\n【选填,默认为30秒,如果经常提前判定未识别到任务,请适当调大】" + "label": "OCR超时时间\n【选填,默认为10秒,如果经常提前判定未识别到任务,请适当调大】", + "default": 10 }, { "name": "fightTimeout", "type": "input-text", - "label": "战斗超时时间\n【选填,默认为120秒】" + "label": "战斗超时时间\n【选填,默认为120秒】", + "default": 120 } ] \ No newline at end of file