diff --git a/archive/js/AutoArtifactsPro/records/默认账户.txt b/archive/js/AutoArtifactsPro/records/默认账户.txt deleted file mode 100644 index 75ca200d2..000000000 --- a/archive/js/AutoArtifactsPro/records/默认账户.txt +++ /dev/null @@ -1,19 +0,0 @@ -上次运行完成日期: 2025/08/15 -上次结束时间: 2025-08-15T10:09:35.440Z -上次运行路线: A -上次运行是否完成: true -js版本: 1.7.9.2 -历史收益: -日期:2025/08/15,运行路线A,狗粮经验99120,摩拉24274 -日期:2025/07/13,运行路线B,狗粮经验0,摩拉178 -日期:2025/07/13,运行路线B,狗粮经验3780,摩拉0 -日期:2025/05/25,运行路线F,狗粮经验-1,摩拉-1 -日期:2025/05/24,运行路线F,狗粮经验-1,摩拉-1 -日期:2025/05/23,运行路线F,狗粮经验-1,摩拉-1 -日期:2025/05/22,运行路线F,狗粮经验-1,摩拉-1 -日期:2025/05/21,运行路线F,狗粮经验-1,摩拉-1 - - - - - diff --git a/repo/js/AutoArtifactsPro/main.js b/repo/js/AutoArtifactsPro/main.js deleted file mode 100644 index 2a0b5ad80..000000000 --- a/repo/js/AutoArtifactsPro/main.js +++ /dev/null @@ -1,1375 +0,0 @@ -const DEFAULT_OCR_TIMEOUT_SECONDS = 10; -const DEFAULT_FIGHT_TIMEOUT_SECONDS = 120; -//执行进度枚举 -const ProgressEnum = { - //无 - None: 1, - //捡狗粮前的操作 - Operation: 2, - //激活路线 - PathActivate: 4, - //准备 - PathPreparation: 8, - //普通 - PathNormal: 16, - //收尾 - PathEnding: 32, - //额外 - PathExtra: 64 -}; -// 初始化变量并赋予默认值 -let lastRunDate = "未知"; //准备跑狗粮的时间,时间格式是YYYY/MM/DD -let lastEndTime = new Date(); //准备跑狗粮的时间,时间格式是2025-08-17T01:08:51.997Z -let lastRunRoute = "未知"; // 默认值 -let lastRunProgress = "未知"; //最后执行进度 -let lastRunProgressEnum = ProgressEnum.None;//执行进度枚举,默认无 -let lastRunProgressCount = 0;//执行进度数量,默认0 -let lastRunFriendshipCount = 0;//最后执行好感次数 -let records = new Array(14).fill(""); -let finished = false; -const accountName = settings.accountName || "默认账户"; -let version = "default"; -let runnedToday = false;//今日是否运行过脚本的标记 -let artifactExperienceDiff = 0; -let moraDiff = 0; - -//预处理 -//最短运行捡狗粮间隔时间 -const minIntervalTime = settings.minIntervalTime || "5"; -//卡时间 -const waitTimePeriod = settings.waitTimePeriod || "4:05-4:45"; -const friendshipPartyName = settings.friendshipPartyName || "好感"; -const grindPartyName = settings.grindPartyName || "狗粮"; -//捡狗粮前的操作 -const operationType = settings.operationType || "不卡时间,ab交替运行"; -//是否运行激活路线 -const runActivatePath = settings.runActivatePath || false; -let enemyType = "无"; - -(async function () { - setGameMetrics(1920, 1080, 1); - //伪造js结束记录 - await fakeLog("自动狗粮加强版", true, true, 0); - - //处理操作模式信息 - switch (operationType) { - case "盗宝团好感卡时间": - enemyType = "盗宝团"; - break; - - case "愚人众好感卡时间": - enemyType = "愚人众"; - break; - - case "鳄鱼好感卡时间": - enemyType = "鳄鱼"; - break; - - case "干等卡时间": - // 干等卡时间的逻辑 - break; - - case "不卡时间,ab交替运行": - // 不卡时间,ab交替运行的逻辑 - break; - - case "不卡时间,尽可能跑A": - // 不卡时间,尽可能跑A的逻辑 - break; - - default: - // 其他情况的逻辑 - log.error("未知的操作类型: " + operationType); - break; - } - - //处理记录文件路径 - // 获取子文件夹路径 - - // Windows文件名非法字符列表 - const illegalCharacters = /[\\/:*?"<>|]/; - // Windows保留设备名称列表 - const reservedNames = [ - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" - ]; - - // 检查accountName是否为空字符串 - if (accountName === "") { - log.error(`账户名 "${accountName}" 不合法,为空字符串。`); - log.error(`将终止程序,请使用合法的名称`); - await sleep(5000); - return; - } - // 检查accountName是否以空格开头 - else if (accountName.startsWith(" ")) { - log.error(`账户名 "${accountName}" 不合法,以空格开头。`); - log.error(`将终止程序,请使用合法的名称`); - await sleep(5000); - return; - } - // 检查accountName是否以空格结尾 - else if (accountName.endsWith(" ")) { - log.error(`账户名 "${accountName}" 不合法,以空格结尾。`); - log.error(`将终止程序,请使用合法的名称`); - await sleep(5000); - return; - } - // 检查accountName是否包含非法字符 - else if (illegalCharacters.test(accountName)) { - log.error(`账户名 "${accountName}" 不合法,包含非法字符。`); - log.error(`将终止程序,请使用合法的名称`); - await sleep(5000); - return; - } - // 检查accountName是否是保留设备名称 - else if (reservedNames.includes(accountName.toUpperCase())) { - log.error(`账户名 "${accountName}" 不合法,是保留设备名称。`); - log.error(`将终止程序,请使用合法的名称`); - await sleep(5000); - return; - } - // 检查accountName长度是否超过255字符 - else if (accountName.length > 255) { - log.error(`账户名 "${accountName}" 不合法,账户名过长。`); - log.error(`将终止程序,请使用合法的名称`); - await sleep(5000); - return; - } - else { - log.info(`账户名 "${accountName}" 合法。`); - } - let subFolderPath = `records/`; - let recordFilePath = `records/${accountName}.txt`; - // 读取子文件夹中的所有文件路径 - const filesInSubFolder = file.ReadPathSync(subFolderPath); - // 检查记录文件是否存在 - let indexDoExist = false; - for (const filePath of filesInSubFolder) { - if (filePath === `records\\${accountName}.txt`) { - indexDoExist = true; - break; - } - } - if (indexDoExist) { - log.info(`records\\${accountName}.txt 存在`); - } else { - recordFilePath = `record.txt`; - subFolderPath = ``; - // 读取子文件夹中的所有文件路径 - const filesInSubFolder = file.ReadPathSync(subFolderPath); - // 检查记录文件是否存在 - for (const filePath of filesInSubFolder) { - if (filePath === `record.txt`) { - indexDoExist = true; - break; - } - } - if (indexDoExist) { - log.info(`record.txt 存在`); - } else { - log.warn(`无记录文件,将使用默认数据`); - recordFilePath = `assets\\BackUp\\record.txt`; - } - } - await sleep(1000); - - //处理卡时间信息 - // 异步读取文件内容 - const content = await file.readText(recordFilePath); - - // 按行分割内容 - const lines = content.split('\n'); - let recordIndex = 0; - - // 逐行处理 - for (const line of lines) { - // 跳过空行 - if (line.trim() === '') continue; - - // 检查每行的起始部分 - if (line.startsWith("上次运行完成日期:")) { - lastRunDate = line.substring("上次运行完成日期:".length).trim(); - } - - if (line.startsWith("上次结束时间:")) { - const timeString = line.substring("上次结束时间:".length).trim(); - if (timeString) { - lastEndTime = new Date(timeString); - if (isNaN(lastEndTime.getTime())) { - throw new Error(`无效的时间值: ${timeString}`); - } - } - } - - if (line.startsWith("上次运行路线:")) { - lastRunRoute = line.substring("上次运行路线:".length).trim(); - } - - if (line.startsWith("上次执行进度:")) { - lastRunProgress = line.substring("上次执行进度:".length).trim(); - //如果进度不为空 - if (lastRunProgress !== null && lastRunProgress !== undefined && lastRunProgress !== '') { - const splitString = lastRunProgress.split('/'); - lastRunFriendshipCount = parseInt(splitString[0], 10); - lastRunProgressEnum = parseInt(splitString[1], 10); - lastRunProgressCount = parseInt(splitString[2], 10); - } - } - - if (line.startsWith("上次运行是否完成: t")) { - finished = true; - } - - if (line.startsWith("日期") && recordIndex < records.length) { - records[recordIndex] = line.trim(); // 直接使用 line.trim() - recordIndex++; - } - } - - // 输出变量值 - log.info(`上次运行完成日期: ${lastRunDate}`); - log.info(`上次狗粮开始时间: ${lastEndTime.toISOString()}`); - log.info(`上次运行路线: ${lastRunRoute}`); - log.info(`上次运行进度: ${lastRunProgress}`); - log.info(`上次运行是否完成: ${finished}`); - - try { - // 读取 manifest.json 文件的内容 - const content = await file.readText("manifest.json"); - - // 解析 JSON 内容为对象 - const manifest = JSON.parse(content); - - // 获取 version 字段的值 - version = manifest.version; - - log.info(`当前js版本:${version},更新日期25.8.17,如果太久远,要经常检查更新`); - - } catch (error) { - // 如果发生错误,记录错误信息 - log.error("读取或解析 manifest.json 文件时出错:", error); - } - - - // 拆分 lastRunDate 为年、月、日,获取最后运行时间的年月日 - const [year, month, day] = lastRunDate.split('/').map(Number); - - // 生成这个日期凌晨四点的时间 - const lastRunMidnight = new Date(year, month - 1, day, 4, 0, 0); - - // 获取当前时间 - const now = new Date(); - - // 计算当前时间与 lastRunMidnight 之间的时间差(单位:毫秒) - const timeDifference = now - lastRunMidnight; - - - // 如果当前时间减去 lastRunMidnight 小于 24 小时(24 * 60 * 60 * 1000 毫秒),则说明天程序今天运行过 - if (timeDifference < 24 * 60 * 60 * 1000) { - log.info("今日已经运行过狗粮路线"); - runnedToday = true; - } - - //最后运行时间+24小时+间隔时间,只要运行时间大于此时间,那么所有的狗粮都已经刷新了,那么就可以重新设置路线了 - let endTime = await getEndTime(minIntervalTime, lastEndTime); - - // 解析 waitTimePeriod - const [startTimeStr, endTimeStr] = waitTimePeriod.split('-').map(time => time.trim()); - - // 将时间字符串转换为小时和分钟 - const [startHour, startMinute] = startTimeStr.split(':').map(Number); - const [endHour, endMinute] = endTimeStr.split(':').map(Number); - - // 获取当前日期 - const today = new Date(); - today.setHours(0, 0, 0, 0); // 将时间设置为当天的午夜 - let todayMidNight = new Date(today); - todayMidNight.setHours(4, 0, 0, 0); // 将时间设置为当天的午夜 - - // 创建等待时间段的开始时间和结束时间的 Date 对象 - let waitStartTime = new Date(today); - waitStartTime.setHours(startHour, startMinute, 0, 0); - let waitEndTime = new Date(today); - waitEndTime.setHours(endHour, endMinute, 0, 0); - - let runRouteA = lastRunRoute === "A"; - - log.info(`卡时间时间段为${waitStartTime.toTimeString()}-${waitEndTime.toTimeString()}`); - - //有些人是每天固定时间段启动,有些人是昨天晚九点启动了,但是第二天早八点就启动了,此时上个路线跑了一半没跑了,要么已经完成,而脚本只知道机械化操作,所以直接跑另一个路线反而收益最大化 - //只有今天没运行过或者最后运行时间小于今天的卡时间开始时间才设置路线A或B,设置激活路线,如果不满足上述条件,就继续用保存的配置,将未完成的继续完成 - if (!runnedToday || (lastEndTime < todayMidNight && now > todayMidNight)) { - //顺便刷新下运行记录 - lastRunProgress = '1/0'; - lastRunProgressEnum = ProgressEnum.None; - lastRunProgressCount = 0; - lastRunFriendshipCount = 0; - finished = false; - - // 如果最后运行时间+24小时大于当前运行的卡时间结束时间 - //狗粮刷新时间分为12小时和24小时,大多数是24小时的,刷新时间以后最后获取时间+24小时 - //如果一个人电脑凌晨4点零五启动,路线为A,然后启动卡时间,那么endTime为第二天的凌晨六点左右,但第二天仍然会以凌晨四点启动,但此时A路线的时间没有刷新完成 - //如果是这种时间还要运行的话,只能走另一条路线,才能确保最大收益 - if (endTime > waitEndTime) { - // 如果 endTime 晚于当天的结束时间,则将其改为当天的开始时间 - endTime = new Date(waitStartTime); - //直接使用反路线 - runRouteA = !runRouteA; - } - - if (operationType === "不卡时间,尽可能跑A") { - // 根据当前时间与上次运行时间给布尔变量 runRouteA 赋值 - runRouteA = endTime <= now; - } - - // 检查 lastRunRoute 是否为 "B" - if (lastRunRoute === "B" && operationType !== "不卡时间,ab交替运行") { - // 如果 lastRunRoute 为 "B",则将 endTime 改为当天的开始时间 - endTime = new Date(waitStartTime); - // 同时将 runRouteA 改为 true - runRouteA = true; - } - - if (operationType === "不卡时间,ab交替运行") { - // 定义 1970-01-01T20:00:00.000Z 的时间对象 - const epochTime = new Date('1970-01-01T20:00:00.000Z'); - - // 根据当前时间与 1970-01-01T20:00:00.000Z 的天数差的奇偶性给布尔变量 runRouteA 赋值 - runRouteA = Math.floor((now - epochTime) / (24 * 60 * 60 * 1000)) % 2 === 0; - } - } - //先更新相关值,免得保存进度时再调用 - //根据 runRouteA 的值更新 lastRunRoute - lastRunRoute = runRouteA ? "A" : "B"; - - //切换至好感队 - await switchPartyIfNeeded(friendshipPartyName); - - - wait: { - //如果已完成,则跳过,或者当前正在执行获取狗粮的进度 - if (lastRunProgressEnum > ProgressEnum.Operation) - break wait; - else - lastRunProgressEnum = ProgressEnum.Operation; - - if (operationType !== "不卡时间,ab交替运行" && operationType !== "不卡时间,尽可能跑A") { - // 输出结果 - log.info(`预期开始狗粮时间: ${endTime.toTimeString().slice(0, 8)}`); - // 检查当前时间是否晚于 endTime - if (new Date() > endTime) { - log.warn('无需卡时间') - } else { - if (operationType !== "干等卡时间") { - //准备环节 - if (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(50 - lastRunFriendshipCount, ocrTimeout, fightTimeout, enemyType, endTime); - } - } - - // 获取当前时间 - const waitStartNow = new Date(); - - // 计算 endTime 与当前时间的差值(单位:毫秒),以防好感度运行完了还没到时间 - const timeDiff = endTime - waitStartNow; - if (timeDiff > 0) { - log.info(`当前时间与预期时间的差值为 ${timeDiff} 毫秒,等待该时间`); - await sleep(timeDiff); - } else { - log.info("当前时间已晚于预期时间,无需等待"); - } - } - } - - //更新运行数据 - refresh: { - if ((runnedToday && finished) || (runnedToday && runActivatePath)) { - break refresh; - } - // 获取当前日期和时间 - const finishDate = new Date(); - - // 格式化当前日期为 "YYYY/MM/DD" 格式 - const currentDateString = `${finishDate.getFullYear()}/${String(finishDate.getMonth() + 1).padStart(2, '0')}/${String(finishDate.getDate()).padStart(2, '0')}`; - - // 根据 runRouteA 的值更新 lastRunRoute - lastRunRoute = runRouteA ? "A" : "B"; - - // 更新 lastRunDate 为当前日期 - lastRunDate = currentDateString; - - // 更新 lastEndTime 为当前时间 - lastEndTime = new Date(); // 使用 new Date() 获取当前时间 - - //按格式输出今日狗粮路线信息 - log.info(`今日运行狗粮路线:${settings.useABE ? 'abe' : ''}${runRouteA ? 'A' : 'B'},开始时间:${lastEndTime.toLocaleString()}`); - } - - //运行前按自定义配置清理狗粮 - if (settings.decomposeMode === "分解(经验瓶)") { - await processArtifacts(21); - } else { - artifactExperienceDiff -= await processArtifacts(21); - } - - moraDiff -= await mora(); - artifacts: { - if (runnedToday && finished) { - break artifacts; - } - - // 开始运行狗粮路线 - let runArtifactsResult = true; - runArtifactsResult = await runArtifactsPaths(runRouteA, grindPartyName, settings.useABE); - await genshin.returnMainUi(); - await sleep(2000); - artifactExperienceDiff += await processArtifacts(21); - moraDiff += await mora(); - log.info(`狗粮路线获取摩拉: ${moraDiff}`); - log.info(`狗粮路线获取狗粮经验: ${artifactExperienceDiff}`); - - //修改records - for (let i = records.length - 1; i > 0; i--) { - records[i] = records[i - 1]; - } - records[0] = `日期:${lastRunDate},运行路线${lastRunRoute},狗粮经验${artifactExperienceDiff},摩拉${moraDiff}`; - if (settings.notify) { - notification.Send(`日期:${lastRunDate},运行路线${lastRunRoute},狗粮经验${artifactExperienceDiff},摩拉${moraDiff}`); - } - if (runArtifactsResult) { - //修改文件内容 - log.info('修改记录文件'); - await writeRecordFile(lastRunDate, lastEndTime, lastRunRoute, `${lastRunFriendshipCount}/${lastRunProgressEnum}/${lastRunProgressCount}`, records, `records/${accountName}.txt`, version, true); - } - } - - //完成剩下好感 - - if (lastRunFriendshipCount < settings.minTimesForFirendship) { - - //切换至好感队 - await switchPartyIfNeeded(friendshipPartyName); - - // 验证超时设置 - const ocrTimeout = validateTimeoutSetting(settings.ocrTimeout, DEFAULT_OCR_TIMEOUT_SECONDS, "OCR"); - const fightTimeout = validateTimeoutSetting(settings.fightTimeout, DEFAULT_FIGHT_TIMEOUT_SECONDS, "战斗"); - //准备环节 - if (enemyType === "盗宝团") { - log.info(`清理原住民...`); - await AutoPath('盗宝团-准备'); - } - if (enemyType === "愚人众") { - log.info(`导航到愚人众触发点...`); - await AutoPath('愚人众-准备'); - } - if (enemyType === "鳄鱼") { - log.info(`导航到鳄鱼触发点...`); - await AutoPath('鳄鱼-准备'); - } - // 好感循环开始 - await AutoFriendshipDev(settings.minTimesForFirendship - lastRunFriendshipCount, ocrTimeout, fightTimeout, enemyType, endTime + 24 * 60 * 60 * 1000); - } - - //伪造js开始记录 - await fakeLog("自动狗粮加强版", true, false, 0); -})(); - -//异步函数,保存进度,只是更新目前进度,大多信息不变,方便调用,就封装了下 -async function saveProgress() { - //如果今天运行过,或者已完成,那么跳过保存记录 - if (runnedToday && finished) { - return; - } - - log.info('修改记录文件'); - //写入日志 - await writeRecordFile(lastRunDate, lastEndTime, lastRunRoute, `${lastRunFriendshipCount}/${lastRunProgressEnum}/${lastRunProgressCount}`, records, `records/${accountName}.txt`, version, false); -} - -// 异步函数,用于将变量内容写回到文件 -async function writeRecordFile(lastRunDate, lastEndTime, lastRunRoute, lastRunProgress, records, recordFilePath, version, finished) { - try { - // 构造要写入文件的内容 - const content = [ - `上次运行完成日期: ${lastRunDate}`, - `上次结束时间: ${lastEndTime.toISOString()}`, - `上次运行路线: ${lastRunRoute}`, - `上次执行进度: ${lastRunProgress}`, - `上次运行是否完成: ${finished}`, - `js版本: ${version}`, - "历史收益:" - ].concat(records).join('\n'); - - // 异步写入文件 - const result = await file.writeText(recordFilePath, content, false); // 覆盖写入 - if (result) { - log.info("文件写入成功"); - } else { - log.error("文件写入失败"); - } - } catch (error) { - log.error(`写入文件时出错: ${error}`); - } -} - -//运行狗粮路线的逻辑 -async function runArtifactsPaths(runRouteA, grindPartyName, useABE) { - // 根据 runRouteA 的值给 runningRoute 赋值 - const runningRoute = runRouteA ? "A" : "B"; - - // 定义文件夹路径 - const folderName = `${runningRoute}路线`; - - let ArtifactsPath = "abeArtifactsPath"; - - if (!useABE) { - ArtifactsPath = "ArtifactsPath"; - log.info("使用新路线中"); - } else { - log.warn("使用老abe路线中"); - } - - const filePathNormal = `assets/${ArtifactsPath}/${folderName}/01普通`; - const filePathEnding = `assets/${ArtifactsPath}/${folderName}/02收尾`; - const filePathExtra = `assets/${ArtifactsPath}/${folderName}/03额外`; - const filePathPreparation = `assets/${ArtifactsPath}/${folderName}/00准备`; - const filePathActivate = `assets/${ArtifactsPath}/${folderName}/-1激活`; - - // 将每组路线的逻辑抽取为公用函数 - async function runPathGroups(filePathDir, subTaskName) { - // 读取文件夹中的文件名并处理 - const filePaths = file.readPathSync(filePathDir); - const jsonFilePaths = []; - - for (const filePath of filePaths) { - if (filePath.endsWith('.json')) { // 检查文件名是否以 .json 结尾 - jsonFilePaths.push(filePath); // 存储文件名 - } - } - - - // 执行地图追踪文件 - for (let j = lastRunProgressCount; j < jsonFilePaths.length; j++) { - if ((j % 5 === 0) && settings.autoSalvage && settings.decomposeMode != "保留") { - artifactExperienceDiff += await processArtifacts(1); - } - const fullPath = jsonFilePaths[j]; - await fakeLog(fullPath, false, true, 0); - log.info(`当前进度:${fullPath}为${subTaskName}${folderName}第${j + 1}/${jsonFilePaths.length}个`); - await pathingScript.runFile(fullPath); - //捕获任务取消的信息并跳出循环 - try { - await sleep(10); // 假设 sleep 是一个异步函数,休眠 10 毫秒 - } catch (error) { - log.error(`发生错误: ${error}`); - throw new Error("任务被取消"); - } - await fakeLog(fullPath, false, false, 0); - //刷新进度 - lastRunProgressCount += 1; - await saveProgress(); - } - } - - //运行激活路线 - if (runActivatePath && lastRunProgressEnum < ProgressEnum.PathActivate) { - //刷新进度 - lastRunProgressEnum = ProgressEnum.PathActivate; - await runPathGroups(filePathActivate, "激活"); - //执行完就重置进度,避免影响下一个,但不保存到文件中,避免中途暂停,又再重复执行 - lastRunProgressCount = 0; - } - - if (!((runnedToday && finished) || (runnedToday && runActivatePath))) { - //修改文件内容 - log.info('修改记录文件'); - await writeRecordFile(lastRunDate, lastEndTime, lastRunRoute, `${lastRunFriendshipCount}/${lastRunProgressEnum}/${lastRunProgressCount}`, records, `records/${accountName}.txt`, version, false); - } - // 运行准备路线(关闭拾取) - dispatcher.ClearAllTriggers(); - if (lastRunProgressEnum <= ProgressEnum.PathPreparation) { - lastRunProgressEnum = ProgressEnum.PathPreparation; - await runPathGroups(filePathPreparation, "准备"); - lastRunProgressCount = 0; - } - - // 启用自动拾取的实时任务 - dispatcher.addTimer(new RealtimeTimer("AutoPick")); - - //切换至狗粮队 - await switchPartyIfNeeded(grindPartyName); - - if (lastRunProgressEnum <= ProgressEnum.PathNormal) { - lastRunProgressEnum = ProgressEnum.PathNormal; - // 运行普通路线 - await runPathGroups(filePathNormal, "普通"); - lastRunProgressCount = 0; - } - - await genshin.tpToStatueOfTheSeven(); - - if (lastRunProgressEnum <= ProgressEnum.PathEnding) { - lastRunProgressEnum = ProgressEnum.PathEnding; - // 运行收尾路线 - await runPathGroups(filePathEnding, "收尾"); - lastRunProgressCount = 0; - } - - if (lastRunProgressEnum <= ProgressEnum.PathExtra) { - lastRunProgressEnum = ProgressEnum.PathExtra; - // 运行额外路线 - await runPathGroups(filePathExtra, "额外"); - } - - dispatcher.ClearAllTriggers(); - - return true; -} - -// fakeLog 函数,使用方法:将本函数放在主函数前,调用时请务必使用await,否则可能出现v8白框报错 -//在js开头处伪造该js结束运行的日志信息,如 await fakeLog("js脚本", true, true, 0); -//在js结尾处伪造该js开始运行的日志信息,如 await fakeLog("js脚本", true, false, 2333); -//duration项目仅在伪造结束信息时有效,且无实际作用,可以任意填写,当你需要在日志中输出特定值时才需要,单位为毫秒 -//在调用地图追踪前伪造该地图追踪开始运行的日志信息,如 await fakeLog(`地图追踪.json`, false, true, 0); -//在调用地图追踪后伪造该地图追踪结束运行的日志信息,如 await fakeLog(`地图追踪.json`, false, false, 0); -//如此便可以在js运行过程中伪造地图追踪的日志信息,可以在日志分析等中查看 - -async function fakeLog(name, isJs, isStart, duration) { - await sleep(10); - const currentTime = Date.now(); - // 参数检查 - if (typeof name !== 'string') { - log.error("参数 'name' 必须是字符串类型!"); - return; - } - if (typeof isJs !== 'boolean') { - log.error("参数 'isJs' 必须是布尔型!"); - return; - } - if (typeof isStart !== 'boolean') { - log.error("参数 'isStart' 必须是布尔型!"); - return; - } - if (typeof currentTime !== 'number' || !Number.isInteger(currentTime)) { - log.error("参数 'currentTime' 必须是整数!"); - return; - } - if (typeof duration !== 'number' || !Number.isInteger(duration)) { - log.error("参数 'duration' 必须是整数!"); - return; - } - - // 将 currentTime 转换为 Date 对象并格式化为 HH:mm:ss.sss - const date = new Date(currentTime); - const hours = String(date.getHours()).padStart(2, '0'); - const minutes = String(date.getMinutes()).padStart(2, '0'); - const seconds = String(date.getSeconds()).padStart(2, '0'); - const milliseconds = String(date.getMilliseconds()).padStart(3, '0'); - const formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`; - - // 将 duration 转换为分钟和秒,并保留三位小数 - const durationInSeconds = duration / 1000; // 转换为秒 - const durationMinutes = Math.floor(durationInSeconds / 60); - const durationSeconds = (durationInSeconds % 60).toFixed(3); // 保留三位小数 - - // 使用四个独立的 if 语句处理四种情况 - if (isJs && isStart) { - // 处理 isJs = true 且 isStart = true 的情况 - const logMessage = `正在伪造js开始的日志记录\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `------------------------------\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `→ 开始执行JS脚本: "${name}"`; - log.debug(logMessage); - } - if (isJs && !isStart) { - // 处理 isJs = true 且 isStart = false 的情况 - const logMessage = `正在伪造js结束的日志记录\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}分${durationSeconds}秒\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `------------------------------`; - log.debug(logMessage); - } - if (!isJs && isStart) { - // 处理 isJs = false 且 isStart = true 的情况 - const logMessage = `正在伪造地图追踪开始的日志记录\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `------------------------------\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `→ 开始执行地图追踪任务: "${name}"`; - log.debug(logMessage); - } - if (!isJs && !isStart) { - // 处理 isJs = false 且 isStart = false 的情况 - const logMessage = `正在伪造地图追踪结束的日志记录\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}分${durationSeconds}秒\n\n` + - `[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` + - `------------------------------`; - log.debug(logMessage); - } -} - -//用于获取结束时间,最小间隔时间+最后一次运行时间+24小时 -async function getEndTime(minIntervalTime, lastEndTime) { - const minIntervalTimeInMs = minIntervalTime * 60 * 1000; // 将分钟转换为毫秒 - return new Date(lastEndTime.getTime() + 24 * 60 * 60 * 1000 + minIntervalTimeInMs); -} - -// 执行 好感度的 path 任务 -async function AutoPath(locationName) { - try { - const filePath = `assets/AutoPath/${locationName}.json`; - await pathingScript.runFile(filePath); - } catch (error) { - log.error(`执行 ${locationName} 路径时发生错误: ${error.message}`); - } -} - -//好感度任务的逻辑,返回实际运行次数 -async function AutoFriendshipDev(times, ocrTimeout, fightTimeout, enemyType = "盗宝团", endTime) { - // 启用自动拾取的实时任务(只有鳄鱼好感启用) - //if (enemyType === "盗宝团") { - //if (enemyType === "愚人众") { - if (enemyType === "鳄鱼") { - dispatcher.addTimer(new RealtimeTimer("AutoPick")); - } - //记录此次运行实际运行数量 - let friendTimes = 0; - //记录当前进度数量 - let progressCnt = lastRunFriendshipCount; - for (let i = progressCnt; i < progressCnt + times; i++) { - - if (enemyType === "无") { - log.info(`不进行好感`); - return 0; - } - - // 获取当前时间 - const now = new Date(); - - // 比较当前时间与 endTime,若晚于 endTime 则跳出循环 - if (now > endTime) { - log.info("当前时间已晚于预期时间,终止好感任务"); - break; - } - - await fakeLog(`第${i + 1}次好感`, false, true, 0); - friendTimes = friendTimes + 1; - 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}`); - } - } else { - notification.send(`未识别到突发任务,${enemyType}好感结束`); - log.info(`未识别到突发任务,${enemyType}好感结束`); - return false; - } - - // 特殊处理:鳄鱼战斗后需要拾取 - if (enemyType === "鳄鱼") { - await AutoPath('鳄鱼-拾取'); - } - - await fakeLog(`第${i + 1}次好感`, false, false, 0); - //更新次数 - lastRunFriendshipCount += 1; - //保存进度 - await saveProgress(); - } - log.info(`${enemyType}好感运行了${friendTimes}次`); - await genshin.tpToStatueOfTheSeven(); - - return friendTimes; -} - -// 验证输入是否是正整数 -function isPositiveInteger(value) { - return Number.isInteger(value) && value > 0; -} - -// 根据敌人类型获取OCR关键词 -function getOcrKeywords(enemyType) { - if (enemyType === "愚人众") { - return ["买卖", "不成", "正义存", "愚人众", "禁止", "危险", "运输", "打倒", "盗宝团", "丘丘人", "今晚", "伙食", "所有人"]; - } - else if (enemyType === "盗宝团") { - return ["岛上", "无贼", "消灭", "鬼鬼祟祟", "盗宝团"]; - } - else if (enemyType === "鳄鱼") { - return ["张牙", "舞爪", "恶党", "鳄鱼", "打倒", "所有", "鳄鱼"]; - } - else { - return ["突发", "任务", "打倒", "消灭", "敌人", "所有"]; // 兜底关键词 - } -} - -// 根据敌人类型获取目标战斗点坐标 -function getTargetCoordinates(enemyType) { - if (enemyType === "愚人众") { - return { x: 4840.55, y: -3078.01 }; - } else if (enemyType === "盗宝团") { - // 盗宝团战斗点坐标 - return { x: -2757.28, y: -3468.43 }; - } else if (enemyType === "鳄鱼") { - // 鳄鱼战斗点坐标 - return { x: 3578.08, y: -500.75 }; - } -} - -//切换队伍 -async function switchPartyIfNeeded(partyName) { - if (!partyName) { - await genshin.returnMainUi(); - return; - } - try { - log.info("正在尝试切换至" + partyName); - if (!await genshin.switchParty(partyName)) { - log.info("切换队伍失败,前往七天神像重试"); - await genshin.tpToStatueOfTheSeven(); - await genshin.switchParty(partyName); - } - } catch { - log.error("队伍切换失败,可能处于联机模式或其他不可切换状态"); - notification.error(`队伍切换失败,可能处于联机模式或其他不可切换状态`); - await genshin.returnMainUi(); - } -} - -//等待战斗结果 -async function waitForBattleResult(timeout = 2 * 60 * 1000, enemyType = "盗宝团", cts = new CancellationTokenSource()) { - let fightStartTime = Date.now(); - const successKeywords = ["事件", "完成"]; - const failureKeywords = ["失败"]; - const eventKeywords = getOcrKeywords(enemyType); - let notFind = 0; - - while (Date.now() - fightStartTime < timeout) { - try { - // 简化OCR检测,只使用一个try-catch块 - let captureRegion = captureGameRegion(); - let result = captureRegion.find(RecognitionObject.ocr(850, 150, 200, 80)); - let result2 = captureRegion.find(RecognitionObject.ocr(0, 200, 300, 300)); - let text = result.text; - let text2 = result2.text; - - // 检查成功关键词 - for (let keyword of successKeywords) { - if (text.includes(keyword)) { - log.info("检测到战斗成功关键词: {0}", keyword); - log.info("战斗结果:成功"); - cts.cancel(); // 取消任务 - return true; - } - } - - // 检查失败关键词 - 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 eventKeywords) { - if (text2.includes(keyword)) { - find++; - } - } - - if (find === 0) { - notFind++; - log.info("未检测到任务触发关键词:{0} 次", notFind); - } else { - notFind = 0; - } - - if (notFind > 10) { - log.warn("不在任务触发区域,战斗失败"); - cts.cancel(); // 取消任务 - if (enemyType === "愚人众") { - log.warn("回到愚人众准备点"); - await AutoPath('愚人众-准备'); - } - return false; - - } - } - catch (error) { - log.error("OCR过程中出错: {0}", error); - // 出错后继续循环,不进行额外嵌套处理 - } - - // 统一的检查间隔 - await sleep(1000); - } - - log.warn("在超时时间内未检测到战斗结果"); - cts.cancel(); // 取消任务 -} - -/** - * 验证超时时间设置 - * @param {number|string} value - 用户设置的超时时间(秒) - * @param {number} defaultValue - 默认超时时间(秒) - * @param {string} timeoutType - 超时类型名称 - * @returns {number} - 验证后的超时时间(秒) - */ -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; -} - -// 定义所有图标的图像识别对象,每个图片都有自己的识别区域 -let CharacterMenuRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/CharacterMenu.png"), 60, 991, 38, 38); - -// 定义一个函数用于识别图像 -async function recognizeImage(recognitionObject, timeout = 5000) { - let startTime = Date.now(); - while (Date.now() - startTime < timeout) { - try { - // 尝试识别图像 - let imageResult = captureGameRegion().find(recognitionObject); - if (imageResult) { - // log.info(`成功识别图像,坐标: x=${imageResult.x}, y=${imageResult.y}`); - // log.info(`图像尺寸: width=${imageResult.width}, height=${imageResult.height}`); - return { success: true, x: imageResult.x, y: imageResult.y }; - } - } catch (error) { - log.error(`识别图像时发生异常: ${error.message}`); - } - await sleep(500); // 短暂延迟,避免过快循环 - } - log.warn(`经过多次尝试,仍然无法识别图像`); - return { success: false }; -} - -// 定义一个函数用于识别文字并点击 -async function recognizeTextAndClick(targetText, ocrRegion, timeout = 3000) { - let startTime = Date.now(); - let retryCount = 0; // 重试计数 - while (Date.now() - startTime < timeout) { - try { - // 尝试 OCR 识别 - let resList = captureGameRegion().findMulti(RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height)); // 指定识别区域 - // 遍历识别结果,检查是否找到目标文本 - for (let res of resList) { - let correctedText = res.text; - if (correctedText.includes(targetText)) { - // 如果找到目标文本,计算并点击文字的中心坐标 - let centerX = Math.round(res.x + res.width / 2); - let centerY = Math.round(res.y + res.height / 2); - await click(centerX, centerY); - await sleep(500); // 确保点击后有足够的时间等待 - return { success: true, x: centerX, y: centerY }; - } - } - } catch (error) { - retryCount++; // 增加重试计数 - log.warn(`页面标志识别失败,正在进行第 ${retryCount} 次重试...`); - } - await sleep(1000); // 短暂延迟,避免过快循环 - } - log.warn(`经过多次尝试,仍然无法识别文字: ${targetText},尝试点击默认中心位置`); - let centerX = Math.round(ocrRegion.x + ocrRegion.width / 2); - let centerY = Math.round(ocrRegion.y + ocrRegion.height / 2); - await click(centerX, centerY); - await sleep(1000); - return { success: false }; -} - -// 定义一个独立的函数用于在指定区域进行 OCR 识别并输出识别内容 -async function recognizeTextInRegion(ocrRegion, timeout = 5000) { - let startTime = Date.now(); - let retryCount = 0; // 重试计数 - while (Date.now() - startTime < timeout) { - try { - // 在指定区域进行 OCR 识别 - let ocrResult = captureGameRegion().find(RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height)); - if (ocrResult) { - let correctedText = ocrResult.text; - return correctedText; // 返回识别到的内容 - } else { - log.warn(`OCR 识别区域未找到内容`); - return null; // 如果 OCR 未识别到内容,返回 null - } - } catch (error) { - retryCount++; // 增加重试计数 - log.warn(`OCR 识别失败,正在进行第 ${retryCount} 次重试...`); - } - await sleep(500); // 短暂延迟,避免过快循环 - } - log.warn(`经过多次尝试,仍然无法在指定区域识别到文字`); - return null; // 如果未识别到文字,返回 null -} - -async function decomposeArtifacts() { - keyPress("B"); - await sleep(1000); - await click(670, 45); - await sleep(500); - - await recognizeTextAndClick("分解", { x: 635, y: 991, width: 81, height: 57 }); - await sleep(1000); - - //识别已储存经验(1570-880-1650-930) - let regionToCheck1 = { x: 1570, y: 880, width: 80, height: 50 }; - let initialNum = await recognizeTextInRegion(regionToCheck1); - let initialValue = 0; - - if (initialNum && !isNaN(parseInt(initialNum, 10))) { - initialValue = parseInt(initialNum, 10); - log.info(`已储存经验识别成功: ${initialValue}`); - } else { - log.warn(`在指定区域未识别到有效数字: ${initialValue}`); - } - let regionToCheck3 = { x: 100, y: 885, width: 170, height: 50 }; - let decomposedNum = 0; - let firstNumber = 0; - let firstNumber2 = 0; - - if (settings.keep4Star) { - await recognizeTextAndClick("快速选择", { x: 248, y: 996, width: 121, height: 49 }); - moveMouseTo(960, 540); - await sleep(1000); - - await click(370, 1020); // 点击“确认选择”按钮 - await sleep(1500); - - decomposedNum = await recognizeTextInRegion(regionToCheck3); - - // 使用正则表达式提取第一个数字 - const match = decomposedNum.match(/已选(\d+)/); - - // 检查是否匹配成功 - if (match) { - // 将匹配到的第一个数字转换为数字类型并存储在变量中 - firstNumber = Number(match[1]); - log.info(`1-4星总数量: ${firstNumber}`); - } else { - log.info("识别失败"); - } - keyPress("VK_ESCAPE"); - - - await recognizeTextAndClick("分解", { x: 635, y: 991, width: 81, height: 57 }); - await sleep(1000); - } - await recognizeTextAndClick("快速选择", { x: 248, y: 996, width: 121, height: 49 }); - moveMouseTo(960, 540); - await sleep(1000); - - if (settings.keep4Star) { - await click(370, 370);//取消选择四星 - await sleep(1000); - } - await click(370, 1020); // 点击“确认选择”按钮 - await sleep(1500); - - let decomposedNum2 = await recognizeTextInRegion(regionToCheck3); - - // 使用正则表达式提取第一个数字 - const match2 = decomposedNum2.match(/已选(\d+)/); - - // 检查是否匹配成功 - if (match2) { - // 将匹配到的第一个数字转换为数字类型并存储在变量中 - firstNumber2 = Number(match2[1]); - log.info(`分解总数是: ${firstNumber2}`); - } else { - log.info("识别失败"); - } - //识别当前总经验 - let regionToCheck2 = { x: 1500, y: 900, width: 150, height: 100 }; - let newNum = await recognizeTextInRegion(regionToCheck2); - let newValue = 0; - - if (newNum && !isNaN(parseInt(newNum, 10))) { - newValue = parseInt(newNum, 10); - log.info(`当前总经验识别成功: ${newValue}`); - } else { - log.warn(`在指定区域未识别到有效数字: ${newValue}`); - } - - if (settings.decomposeMode === "分解(经验瓶)") { - log.info(`用户选择了分解,执行分解`); - // 根据用户配置,分解狗粮 - await sleep(1000); - await click(1620, 1020); // 点击分解按钮 - await sleep(1000); - - // 4. 识别"进行分解"按钮 - await click(1340, 755); // 点击进行分解按钮 - - await sleep(1000); - - // 5. 关闭确认界面 - await click(1340, 755); - await sleep(1000); - } - else { - log.info(`用户未选择分解,不执行分解`); - } - - // 7. 计算分解获得经验=总经验-上次剩余 - const resinExperience = Math.max(newValue - initialValue, 0); - log.info(`分解可获得经验: ${resinExperience}`); - let fourStarNum = firstNumber - firstNumber2; - if (settings.keep4Star) { - log.info(`保留的四星数量: ${fourStarNum}`); - } - let resultExperience = resinExperience; - if (resultExperience === 0) { - resultExperience = initialValue; - } - const result = resultExperience; - await genshin.returnMainUi(); - return result; -} - -/** - * 摧毁圣遗物换摩拉 - */ -async function destroyArtifacts(times = 1) { - const ArtifactsButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/ArtifactsButton.png")); - const DeleteButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/DeleteButton.png")); - const AutoAddButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/AutoAddButton.png")); - const ConfirmButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/ConfirmButton.png")); - const DestoryButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/DestoryButton.png")); - const MidDestoryButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("Assets/RecognitionObject/DestoryButton.png"), 900, 600, 500, 300); - await genshin.returnMainUi(); - keyPress("B"); - await sleep(1500); - - let ArtifactsButton = captureGameRegion().find(ArtifactsButtonRo); - if (ArtifactsButton.isExist()) { - log.info("识别到圣遗物按钮"); - ArtifactsButton.click(); - await sleep(1500); - } - - try { - for (let i = 0; i < times; i++) { - captureGameRegion().find(DeleteButtonRo).click();// 点击摧毁 - await sleep(600); - captureGameRegion().find(AutoAddButtonRo).click();// 点击自动添加 - await sleep(600); - await sleep(300); - click(150, 150); - await sleep(300); - click(150, 220); - await sleep(300); - click(150, 300); - if (!settings.keep4Star) { - await sleep(300); - click(150, 370); - } - captureGameRegion().find(ConfirmButtonRo).click();// 点击快捷放入 - await sleep(600); - captureGameRegion().find(DestoryButtonRo).click();// 点击摧毁 - await sleep(600); - captureGameRegion().find(MidDestoryButtonRo).click();// 弹出页面点击摧毁 - await sleep(600); - click(960, 1000);// 点击空白处 - await sleep(1000); - } - } catch (ex) { - log.info("背包里的圣遗物已摧毁完毕,提前结束") - } finally { - await genshin.returnMainUi(); - } - -} - -async function processArtifacts(times = 1) { - await genshin.returnMainUi(); - let result = 0; - try { - if (settings.decomposeMode === "销毁(摩拉)") { - result = await destroyArtifacts(times); - } else { - result = await decomposeArtifacts(); - } - } catch (error) { - log.error(`处理狗粮分解时发生异常: ${error.message}`); - } - await genshin.returnMainUi(); - return result; -} - -async function mora() { - let result = 0; - let tryTimes = 0; - while (result === 0 && tryTimes < 3) { - await genshin.returnMainUi(); - log.info("开始尝试识别摩拉"); - // 按下 C 键 - keyPress("C"); - await sleep(1500); - let recognized = false; - // 识别“角色菜单”图标或“天赋”文字 - let startTime = Date.now(); - while (Date.now() - startTime < 5000) { - // 尝试识别“角色菜单”图标 - let characterMenuResult = await recognizeImage(CharacterMenuRo, 5000); - if (characterMenuResult.success) { - await click(177, 433); - await sleep(500); - recognized = true; - break; - } - - // 尝试识别“天赋”文字 - let targetText = "天赋"; - let ocrRegion = { x: 133, y: 395, width: 115, height: 70 }; // 设置对应的识别区域 - let talentResult = await recognizeTextAndClick(targetText, ocrRegion); - if (talentResult.success) { - log.info(`点击天赋文字,坐标: x=${talentResult.x}, y=${talentResult.y}`); - recognized = true; - break; - } - - await sleep(1000); // 短暂延迟,避免过快循环 - } - - let recognizedText = ""; - - // 如果识别到了“角色菜单”或“天赋”,则识别“摩拉数值” - if (recognized) { - let ocrRegionMora = { x: 1620, y: 25, width: 152, height: 46 }; // 设置对应的识别区域 - recognizedText = await recognizeTextInRegion(ocrRegionMora); - if (recognizedText) { - log.info(`成功识别到摩拉数值: ${recognizedText}`); - result = recognizedText; - } else { - log.warn("未能识别到摩拉数值。"); - } - } else { - log.warn("未能识别到角色菜单或天赋"); - } - await sleep(500); - tryTimes++; - await genshin.returnMainUi(); - } - return Number(result); -}