diff --git a/repo/js/AutoEnter/README.md b/repo/js/AutoEnter/README.md index 6165f980e..bb45d9b5f 100644 --- a/repo/js/AutoEnter/README.md +++ b/repo/js/AutoEnter/README.md @@ -1,4 +1,2 @@ -自动进入联机状态js测试版本,出什么bug都是正常的,加qq718135749去超市作者 - -组队和更多使用说明请加qq群1057307824 +进入特定uid世界或自动批准特定用户进入,填写的名字和放在targets文件夹中的截图都会被视为白名单用户放行,使用前务必将世界权限设置为确认后加入 diff --git a/repo/js/AutoEnter/assets/RecognitionObject/0P.png b/repo/js/AutoEnter/assets/RecognitionObject/0P.png new file mode 100644 index 000000000..3a5bf5ae4 Binary files /dev/null and b/repo/js/AutoEnter/assets/RecognitionObject/0P.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/1P.png b/repo/js/AutoEnter/assets/RecognitionObject/1P.png index b187e2032..db72c0b69 100644 Binary files a/repo/js/AutoEnter/assets/RecognitionObject/1P.png and b/repo/js/AutoEnter/assets/RecognitionObject/1P.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/2P.png b/repo/js/AutoEnter/assets/RecognitionObject/2P.png index 4f8edeef3..28978985b 100644 Binary files a/repo/js/AutoEnter/assets/RecognitionObject/2P.png and b/repo/js/AutoEnter/assets/RecognitionObject/2P.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/3P.png b/repo/js/AutoEnter/assets/RecognitionObject/3P.png index 04cf2d4bc..47907ac41 100644 Binary files a/repo/js/AutoEnter/assets/RecognitionObject/3P.png and b/repo/js/AutoEnter/assets/RecognitionObject/3P.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/4P.png b/repo/js/AutoEnter/assets/RecognitionObject/4P.png index 888043566..59473bab6 100644 Binary files a/repo/js/AutoEnter/assets/RecognitionObject/4P.png and b/repo/js/AutoEnter/assets/RecognitionObject/4P.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/allowEnter.png b/repo/js/AutoEnter/assets/RecognitionObject/allowEnter.png index 8c6cd7632..e3f5a941d 100644 Binary files a/repo/js/AutoEnter/assets/RecognitionObject/allowEnter.png and b/repo/js/AutoEnter/assets/RecognitionObject/allowEnter.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/confirmKick.png b/repo/js/AutoEnter/assets/RecognitionObject/confirmKick.png new file mode 100644 index 000000000..ee07b4550 Binary files /dev/null and b/repo/js/AutoEnter/assets/RecognitionObject/confirmKick.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/dialogueUI.png b/repo/js/AutoEnter/assets/RecognitionObject/dialogueUI.png new file mode 100644 index 000000000..620c2a014 Binary files /dev/null and b/repo/js/AutoEnter/assets/RecognitionObject/dialogueUI.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/kickAll.png b/repo/js/AutoEnter/assets/RecognitionObject/kickAll.png new file mode 100644 index 000000000..6dbc5de33 Binary files /dev/null and b/repo/js/AutoEnter/assets/RecognitionObject/kickAll.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/leaveTeam.png b/repo/js/AutoEnter/assets/RecognitionObject/leaveTeam.png new file mode 100644 index 000000000..367faea17 Binary files /dev/null and b/repo/js/AutoEnter/assets/RecognitionObject/leaveTeam.png differ diff --git a/repo/js/AutoEnter/assets/RecognitionObject/yUI.png b/repo/js/AutoEnter/assets/RecognitionObject/yUI.png index 4e365bb76..b7861b40b 100644 Binary files a/repo/js/AutoEnter/assets/RecognitionObject/yUI.png and b/repo/js/AutoEnter/assets/RecognitionObject/yUI.png differ diff --git a/repo/js/AutoEnter/main.js b/repo/js/AutoEnter/main.js index 4d45f2bd2..ee7ae3032 100644 --- a/repo/js/AutoEnter/main.js +++ b/repo/js/AutoEnter/main.js @@ -1,480 +1,208 @@ -//获取自定义配置 -const enterMode = settings.enterMode || "进入他人世界"; -const enteringUID = settings.enteringUID; -const permissionMode = settings.permissionMode || "无条件通过"; -const nameToPermit1 = settings.nameToPermit1; -const nameToPermit2 = settings.nameToPermit2; -const nameToPermit3 = settings.nameToPermit3; -const timeOut = +settings.timeout || 5; -const maxEnterCount = +settings.maxEnterCount || 3; - - -const enterUIDRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/enterUID.png")); -const searchRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/search.png")); -const requestEnterRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/requestEnter.png")); -const requestEnter2Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/requestEnter.png"), 1480, 300, 280, 600); -const yUIRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/yUI.png")); -const allowEnterRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/allowEnter.png"), 1250, 300, 150, 130); -const targetsPath = "targets"; -let enterCount = 0; -let targetsRo; -let checkToEnd = false; -// 先初始化空数组 -let targetList = []; -let enteredPlayers = []; - (async function () { + await autoEnter(settings); +} +)(); + +/** + * 自动联机脚本(整体打包为一个函数) + * @param {Object} autoEnterSettings 配置对象 + * enterMode: "进入他人世界" | "等待他人进入" + * enteringUID: string | null + * permissionMode: "无条件通过" | "白名单" + * nameToPermit1/2/3: string | null + * timeout: 分钟 + * maxEnterCount: number + */ +async function autoEnter(autoEnterSettings) { + // ===== 配置解析 ===== + const enterMode = autoEnterSettings.enterMode || "进入他人世界"; + const enteringUID = autoEnterSettings.enteringUID; + const permissionMode = autoEnterSettings.permissionMode || "无条件通过"; + const timeout = +autoEnterSettings.timeout || 5; + const maxEnterCount = +autoEnterSettings.maxEnterCount || 3; + + // 白名单 + const targetList = []; + [autoEnterSettings.nameToPermit1, autoEnterSettings.nameToPermit2, autoEnterSettings.nameToPermit3] + .forEach(v => v && targetList.push(v)); + + // ===== 模板 / 路径 ===== + const enterUIDRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/enterUID.png")); + const searchRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/search.png")); + const requestEnterRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/requestEnter.png")); + const requestEnter2Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/requestEnter.png"), 1480, 300, 280, 600); + const yUIRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/yUI.png")); + const allowEnterRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/allowEnter.png")); + const targetsPath = "targets"; + + // ===== 状态 ===== + let enterCount = 0; + let targetsRo = []; + let checkToEnd = false; + let enteredPlayers = []; + + // ===== 初始化 ===== setGameMetrics(1920, 1080, 1); const start = new Date(); log.info(`当前模式为:${enterMode}`); - // 依次判断并加入 - if (settings.nameToPermit1) targetList.push(settings.nameToPermit1); - if (settings.nameToPermit2) targetList.push(settings.nameToPermit2); - if (settings.nameToPermit3) targetList.push(settings.nameToPermit3); - - // 获取指定文件夹下所有文件 - let targetPngs = await readFolder(targetsPath, false); - - // 生成 targetsRo(使用 for...of) - targetsRo = []; + // 加载目标 PNG + const targetPngs = await readFolder(targetsPath, ".png"); for (const f of targetPngs) { - log.info(`找到文件${f.fullPath}`); - if (f.fullPath.endsWith('.png')) { - const mat = file.ReadImageMatSync(f.fullPath); - const ro = RecognitionObject.TemplateMatch(mat, 650, 320, 350, 60); - const baseName = f.fileName.replace(/\.png$/i, ''); - targetsRo.push({ ro, baseName }); - } + const mat = file.ReadImageMatSync(f.fullPath); + const ro = RecognitionObject.TemplateMatch(mat, 664, 481, 1355 - 668, 588 - 484); + const baseName = f.fileName.replace(/\.png$/i, ''); + targetsRo.push({ ro, baseName }); } + log.info(`加载完成共 ${targetsRo.length} 个目标`); - - log.info(`加载完成共${targetPngs.length}个目标`); - - while (new Date() - start < timeOut * 60 * 1000) { + // ===== 主循环 ===== + while (new Date() - start < timeout * 60 * 1000) { if (enterMode === "进入他人世界") { - //检验队伍编号 const playerSign = await getPlayerSign(); await sleep(500); - if (playerSign != 0) { - log.info(`加入世界成功,在队伍中的编号为${playerSign}`); + if (playerSign > 1) { + log.info(`加入成功,队伍编号 ${playerSign}`); break; - } else { - log.error(`不处于多人世界,开始尝试加入`); - await genshin.returnMainUi(); - await sleep(500); + } else if (playerSign === -1) { + log.warn("队伍编号识别异常,尝试按0p处理"); } - //反复敲门进入他人世界 - //我要cia进来llo - if (enteringUID) { - await keyPress("F2"); - //点击输入uid - await sleep(2000); - if (!await findAndClick(enterUIDRo)) { - await genshin.returnMainUi(); - continue; - } - await sleep(1000); - inputText(enteringUID); - //点击搜索 - await sleep(1000); - if (!await findAndClick(searchRo)) { - await genshin.returnMainUi(); - continue; - } - //判断是否成功搜索 - if (await confirmSearchResult()) { - await genshin.returnMainUi(); - continue; - } + log.info('不处于多人世界,开始尝试加入'); + await genshin.returnMainUi(); await sleep(500); - //点击申请加入 - await sleep(500); - if (!await findAndClick(requestEnterRo)) { - await genshin.returnMainUi(); - continue; - } - //等待加入完成 - await waitForMainUI(true, 20 * 1000); - } else { - log.error("未填写有效的uid,请检查后重试"); - break; - } - } else { - //等待他人进入世界 - if (enterCount < maxEnterCount) { - if (await isYUI()) { + if (!enteringUID) { log.error('未填写有效 UID'); break; } + + await keyPress("F2"); await sleep(2000); + if (!await findAndClick(enterUIDRo)) { await genshin.returnMainUi(); continue; } + await sleep(1000); inputText(enteringUID); + await sleep(1000); + if (!await findAndClick(searchRo)) { await genshin.returnMainUi(); continue; } + await sleep(500); + if (!await confirmSearchResult()) { await genshin.returnMainUi(); log.warn("无搜索结果"); continue; } + + await sleep(500); + if (!await findAndClick(requestEnterRo)) { await genshin.returnMainUi(); continue; } + await waitForMainUI(true, 20 * 1000); + + } else { // 等待他人进入 + const playerSign = await getPlayerSign(); + if (playerSign > 1) { + log.warn("处于他人世界,先尝试退出"); + let leaveAttempts = 0; + while (leaveAttempts < 10) { + if (await getPlayerSign() === 0) { + break; + } + await keyPress("F2"); + await sleep(1000); + await findAndClick(leaveTeamRo); + await sleep(1000); keyPress("VK_ESCAPE"); + await waitForMainUI(true); + await genshin.returnMainUi(); } - await genshin.returnMainUi(); - keyPress("Y"); - await sleep(250); - if (await isYUI()) { - log.info("处于y界面开始识别"); - let attempts = 0; - while (attempts < 5) { - attempts++; - if (permissionMode === "无条件通过") { - //无需筛选,全部通过 - if (await findAndClick(allowEnterRo)) { - //等待加入完成 - await waitForMainUI(true, 20 * 1000); - enterCount++; - break; - } - } else { - //需要筛选,开始识别第一行申请 - const result = await recognizeRequest(); - if (result) { - if (await findAndClick(allowEnterRo)) { - //等待加入完成 - await waitForMainUI(true, 20 * 1000); - enterCount++; - log.info(`允许${result}加入世界`); - // 把 result 加入 enteredPlayers,并立即去重 - enteredPlayers = [...new Set([...enteredPlayers, result])]; - await sleep(1000); - if (await isYUI()) { - keyPress("VK_ESCAPE"); - await genshin.returnMainUi(); - } - break; - } else { - if (await isYUI()) { - keyPress("VK_ESCAPE"); - await genshin.returnMainUi(); - } - } + } + if (enterCount >= maxEnterCount) break; + if (await isYUI()) keyPress("VK_ESCAPE"); await sleep(500); + await genshin.returnMainUi(); + keyPress("Y"); await sleep(250); - } + if (!await isYUI()) continue; + log.info("处于 Y 界面,开始识别"); + + let attempts = 0; + while (attempts++ < 5) { + if (permissionMode === "无条件通过") { + if (await findAndClick(allowEnterRo)) { + doRunExtra = true; + await waitForMainUI(true, 20 * 1000); + enterCount++; + break; + } + } else { + const result = await recognizeRequest(); + if (result) { + if (await findAndClick(allowEnterRo)) { + await waitForMainUI(true, 20 * 1000); + enterCount++; + enteredPlayers = [...new Set([...enteredPlayers, result])]; + log.info(`允许 ${result} 加入`); + notification.send(`允许 ${result} 加入`); + doRunExtra = true; + if (await isYUI()) { keyPress("VK_ESCAPE"); await sleep(500); await genshin.returnMainUi(); } + break; + } else { + if (await isYUI()) { keyPress("VK_ESCAPE"); await sleep(500); await genshin.returnMainUi(); } } - await sleep(500); } } - if (await isYUI()) { - keyPress("VK_ESCAPE"); - await genshin.returnMainUi(); - } + await sleep(500); } + + if (await isYUI()) { keyPress("VK_ESCAPE"); await genshin.returnMainUi(); } + if (enterCount >= maxEnterCount || checkToEnd) { - log.info("准备结束js"); checkToEnd = true; - if (await isYUI()) { - keyPress("VK_ESCAPE"); - await genshin.returnMainUi(); - } await sleep(20000); if (await findTotalNumber() === maxEnterCount + 1) { - break; - } else { - enterCount--; - } - } - } - - } - -} -)(); - -//等待主界面状态 -async function waitForMainUI(requirement, timeOut = 60 * 1000) { - - log.info(`等待至多${timeOut}毫秒`) - const startTime = Date.now(); - while (Date.now() - startTime < timeOut) { - const mainUIState = await isMainUI(); - if (mainUIState === requirement) return true; - - const elapsed = Date.now() - startTime; - const min = Math.floor(elapsed / 60000); - const sec = Math.floor((elapsed % 60000) / 1000); - const ms = elapsed % 1000; - log.info(`已等待 ${min}分 ${sec}秒 ${ms}毫秒`); - - await sleep(1000); - } - log.error("超时仍未到达指定状态"); - return false; -} - -//检查是否在主界面 -async function isMainUI() { - // 修改后的图像路径 - const imagePath = "assets/RecognitionObject/MainUI.png"; - // 修改后的识别区域(左上角区域) - const xMin = 0; - const yMin = 0; - const width = 150; // 识别区域宽度 - const height = 150; // 识别区域高度 - let template = file.ReadImageMatSync(imagePath); - let recognitionObject = RecognitionObject.TemplateMatch(template, xMin, yMin, width, height); - - // 尝试次数设置为 5 次 - const maxAttempts = 5; - - let attempts = 0; - while (attempts < maxAttempts) { - try { - - let gameRegion = captureGameRegion(); - let result = gameRegion.find(recognitionObject); - gameRegion.dispose(); - if (result.isExist()) { - //log.info("处于主界面"); - return true; // 如果找到图标,返回 true - } - } catch (error) { - log.error(`识别图像时发生异常: ${error.message}`); - - return false; // 发生异常时返回 false - } - attempts++; // 增加尝试次数 - await sleep(50); // 每次检测间隔 50 毫秒 - } - return false; // 如果尝试次数达到上限或取消,返回 false -} - -//获取联机世界的当前玩家标识 -async function getPlayerSign() { - const picDic = { - "1P": "assets/RecognitionObject/1P.png", - "2P": "assets/RecognitionObject/2P.png", - "3P": "assets/RecognitionObject/3P.png", - "4P": "assets/RecognitionObject/4P.png" - } - await genshin.returnMainUi(); - await sleep(500); - const p1Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(picDic["1P"]), 344, 22, 45, 45); - const p2Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(picDic["2P"]), 344, 22, 45, 45); - const p3Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(picDic["3P"]), 344, 22, 45, 45); - const p4Ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(picDic["4P"]), 344, 22, 45, 45); - moveMouseTo(1555, 860); // 移走鼠标,防止干扰识别 - const gameRegion = captureGameRegion(); - // 当前页面模板匹配 - let p1 = gameRegion.Find(p1Ro); - let p2 = gameRegion.Find(p2Ro); - let p3 = gameRegion.Find(p3Ro); - let p4 = gameRegion.Find(p4Ro); - gameRegion.dispose(); - if (p1.isExist()) return 1; - if (p2.isExist()) return 2; - if (p3.isExist()) return 3; - if (p4.isExist()) return 4; - return 0; -} - -async function confirmSearchResult() { - maxAttempts = 5; - for (let attempts = 0; attempts < maxAttempts; attempts++) { - const gameRegion = captureGameRegion(); - try { - const result = gameRegion.find(requestEnter2Ro); - if (result.isExist) { - return false; // 成功立刻返回 - } - } catch (err) { - } finally { - gameRegion.dispose(); - } - if (attempts < maxAttempts - 1) { // 最后一次不再 sleep - await sleep(250); - } - } - return true; -} - -async function findAndClick(target, maxAttempts = 20) { - for (let attempts = 0; attempts < maxAttempts; attempts++) { - const gameRegion = captureGameRegion(); - try { - const result = gameRegion.find(target); - if (result.isExist) { - await sleep(50); - result.click(); - await sleep(50); - return true; // 成功立刻返回 - } - log.warn(`识别失败,第 ${attempts + 1} 次重试`); - } catch (err) { - } finally { - gameRegion.dispose(); - } - if (attempts < maxAttempts - 1) { // 最后一次不再 sleep - await sleep(250); - } - } - log.error("已达到重试次数上限,仍未找到目标"); - return false; -} - -//检查是否在主界面 -async function isYUI() { - // 尝试次数设置为 5 次 - const maxAttempts = 5; - let attempts = 0; - while (attempts < maxAttempts) { - try { - let gameRegion = captureGameRegion(); - let result = gameRegion.find(yUIRo); - gameRegion.dispose(); - if (result.isExist()) { - //log.info("处于Y界面"); - return true; // 如果找到图标,返回 true - } - } catch (error) { - log.error(`识别图像时发生异常: ${error.message}`); - - return false; // 发生异常时返回 false - } - attempts++; // 增加尝试次数 - await sleep(250); // 每次检测间隔 50 毫秒 - } - return false; // 如果尝试次数达到上限或取消,返回 false -} - -// 确认当前申请是否为目标 -async function recognizeRequest() { - // 1. 模板匹配 - try { - const gameRegion = captureGameRegion(); - for (const { ro, baseName } of targetsRo) { - if (gameRegion.find(ro).isExist()) { - gameRegion.dispose(); - return baseName; - } - } - gameRegion.dispose(); - } catch (err) { - log.error(`模板匹配异常: ${err.message}`); - } - - // 2. OCR 仅识别一次,固定区域 650,320,350,60 - try { - const gameRegion = captureGameRegion(); - const resList = gameRegion.findMulti( - RecognitionObject.ocr(650, 320, 350, 60) - ); - gameRegion.dispose(); - - let hit = null; - for (const res of resList) { - const txt = res.text.trim(); - for (const whiteItem of targetList) { - if (txt === whiteItem) { - hit = whiteItem; + notification.send(`已达到预定人数:${maxEnterCount + 1}`); break; } + else enterCount--; } - if (hit) break; } + } - // 如果未命中,输出所有识别结果 - if (!hit) { + if (new Date() - start >= timeout * 60 * 1000) { + log.warn("超时未达到预定人数"); + notification.error(`超时未达到预定人数`); + if (settings.onlyRunPerfectly) { + skipRunning = true; + doRunExtra = false; + } + } + + async function confirmSearchResult() { + for (let i = 0; i < 4; i++) { + const gameRegion = captureGameRegion(); + const res = gameRegion.find(requestEnter2Ro); + gameRegion.dispose(); + if (res.isExist()) return false; + if (i < 4) await sleep(250); + } + return true; + } + + async function isYUI() { + for (let i = 0; i < 5; i++) { + const gameRegion = captureGameRegion(); + const res = gameRegion.find(yUIRo); + gameRegion.dispose(); + if (res.isExist()) return true; + await sleep(250); + } + return false; + } + + async function recognizeRequest() { + try { + const gameRegion = captureGameRegion(); + for (const { ro, baseName } of targetsRo) { + if (gameRegion.find(ro).isExist()) { gameRegion.dispose(); return baseName; } + } + gameRegion.dispose(); + } catch { } + try { + const gameRegion = captureGameRegion(); + const resList = gameRegion.findMulti(RecognitionObject.ocr(664, 481, 1355 - 668, 588 - 484)); + gameRegion.dispose(); + let hit = null; for (const res of resList) { - log.warn(`识别到"${res.text.trim()}",不在白名单`); + const txt = res.text.trim(); + if (targetList.includes(txt)) { hit = txt; break; } } - } - - return hit; // 命中返回白名单项,未命中返回 null - } catch (err) { - log.error(`OCR 识别异常: ${err.message}`); + if (!hit) resList.forEach(r => log.warn(`识别到"${r.text.trim()}",不在白名单`)); + return hit; + } catch { return null; } } - - return null; -} - - -// 定义 readFolder 函数 -async function readFolder(folderPath, onlyJson) { - // 新增一个堆栈,初始时包含 folderPath - const folderStack = [folderPath]; - - // 新增一个数组,用于存储文件信息对象 - const files = []; - - // 当堆栈不为空时,继续处理 - while (folderStack.length > 0) { - // 从堆栈中弹出一个路径 - const currentPath = folderStack.pop(); - - // 读取当前路径下的所有文件和子文件夹路径 - const filesInSubFolder = file.ReadPathSync(currentPath); - - // 临时数组,用于存储子文件夹路径 - const subFolders = []; - for (const filePath of filesInSubFolder) { - if (file.IsFolder(filePath)) { - // 如果是文件夹,先存储到临时数组中 - subFolders.push(filePath); - } else { - // 如果是文件,根据 onlyJson 判断是否存储 - if (onlyJson) { - if (filePath.endsWith(".json")) { - const fileName = filePath.split('\\').pop(); // 提取文件名 - const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组 - files.push({ - fullPath: filePath, - fileName: fileName, - folderPathArray: folderPathArray - }); - //log.info(`找到 JSON 文件:${filePath}`); - } - } else { - const fileName = filePath.split('\\').pop(); // 提取文件名 - const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组 - files.push({ - fullPath: filePath, - fileName: fileName, - folderPathArray: folderPathArray - }); - //log.info(`找到文件:${filePath}`); - } - } - } - // 将临时数组中的子文件夹路径按原顺序压入堆栈 - folderStack.push(...subFolders.reverse()); // 反转子文件夹路径 - } - - return files; -} - -async function findTotalNumber() { - await genshin.returnMainUi(); - await keyPress("F2"); - await sleep(2000); - - // 定义模板 - const kick2pRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/kickButton.png"), 1520, 277, 230, 120); - const kick3pRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/kickButton.png"), 1520, 400, 230, 120); - const kick4pRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/kickButton.png"), 1520, 527, 230, 120); - - moveMouseTo(1555, 860); // 防止鼠标干扰 - const gameRegion = captureGameRegion(); - await sleep(200); - - let count = 1; // 先算上自己 - - // 依次匹配 2P - if (gameRegion.Find(kick2pRo).isExist()) { - log.info("发现 2P"); - count++; - } - - // 依次匹配 3P - if (gameRegion.Find(kick3pRo).isExist()) { - log.info("发现 3P"); - count++; - } - - // 依次匹配 4P - if (gameRegion.Find(kick4pRo).isExist()) { - log.info("发现 4P"); - count++; - } - - gameRegion.dispose(); - - log.info(`当前联机世界玩家总数(含自己):${count}`); - return count; } \ No newline at end of file diff --git a/repo/js/AutoEnter/manifest.json b/repo/js/AutoEnter/manifest.json index 406b36eb8..22b5c8178 100644 --- a/repo/js/AutoEnter/manifest.json +++ b/repo/js/AutoEnter/manifest.json @@ -1,11 +1,9 @@ { "manifest_version": 1, "name": "进入联机状态", - "version": "0.0.4", - "tags": [ - "狗粮" - ], - "description": "配合AAA狗粮联机团购使用,自动进入别人世界或批准别人加入", + "version": "1.0.0", + "tags": [], + "description": "自动进入指定uid世界或批准别人加入,用于进入小号世界采集等", "saved_files": [ "records/*.txt" ], diff --git a/repo/js/AutoEnter/targets/火山老师.png b/repo/js/AutoEnter/targets/火山老师.png index 6c047f973..a5c5094a5 100644 Binary files a/repo/js/AutoEnter/targets/火山老师.png and b/repo/js/AutoEnter/targets/火山老师.png differ diff --git a/repo/js/AutoHoeingOneDragon/README.md b/repo/js/AutoHoeingOneDragon/README.md index a1f5cce25..55fae80e3 100644 --- a/repo/js/AutoHoeingOneDragon/README.md +++ b/repo/js/AutoHoeingOneDragon/README.md @@ -15,7 +15,11 @@ 使用即表示您已阅读并同意上述条款。 --- -(简易配置描述部分) +### 琪零、**温馨提示** +- 如果您在包括但不限于频道、群聊等收到包含以下内容的关于使用bgi【锄地】的建议,请无视,他们给出的建议存在误导,不具有参考价值: +- 不带芙/不带盾/带双盾/携带并使用大位移角色/打精英不带万叶/400-0 0-2000/带钟离打非传奇/使用世界使用带有q而非keypress(q),keydown(q)的战斗策略的(出现以上任意一条即可说明该账号对于锄地的理解有误区,不具有参考价值) +- 还有写战斗策略,如果你连attack()括号里的数字是什么意思、keypress(q)和直接q的区别、配置组设置自动战斗部分的各个配置项的作用都不知道,那要么先去看官网文档搞清楚这些东西再写,要么别研究了,老老实实用万能策略得了(点名希诺宁 e,attack(2)和希诺宁 e,attack,attack) +如果你觉得你被攻击了,那攻击的就是你,自己瞎打着玩没人管你,别一天天的跑出来误导别人 ### 一、**快速使用指南 ✅** @@ -26,7 +30,7 @@ - ⚠️ 关键设置,请务必检查! - 切换队伍:❌ 关闭(配队请在脚本自定义配置中填写) - 战斗策略:推荐使用万能策略,不在万能策略中的角色不建议使用 - - 自动拾取掉落物:❌ 强烈建议关闭(易卡死、拖慢速度) + - 扫描掉落物光柱:❌ 强烈建议关闭(易卡死、拖慢速度) - 自动检测战斗结束:✅ 开启,延时设为 0.35-0.5秒(默认1.5秒太慢) - 战斗结束后执行万叶长E:✅ 强烈建议开启(大幅提升狗粮收益),如果你觉得慢,你打280精英然后把捡的狗粮摧毁,和打400不捡狗粮的收益时一样的 - 战斗超时:小怪路线建议 ≤60秒;练度低可适当延长 diff --git a/repo/js/AutoHoeingOneDragon/main.js b/repo/js/AutoHoeingOneDragon/main.js index 9e2d3608f..3bd05f43d 100644 --- a/repo/js/AutoHoeingOneDragon/main.js +++ b/repo/js/AutoHoeingOneDragon/main.js @@ -70,7 +70,7 @@ let lastEatBuff = 0; await loadBlacklist(true); await rotateWarnIfAccountEmpty(); - + log.info("如果您在包括但不限于频道、群聊等收到包含以下内容的关于使用bgi【锄地】的建议,请无视,他们给出的建议存在误导,不具有参考价值:不带芙/不带盾/带双盾/携带并使用大位移角色/打精英不带万叶/400-0 0-2000/带钟离打非传奇/使用世界使用带有q而非keypress(q),keydown(q)的战斗策略的(出现以上任意一条即可说明该账号对于锄地的理解有误区,不具有参考价值)"); if (operationMode === "启用仅指定怪物模式") { await filterPathingsByTargetMonsters(); await updateRecords(pathings, accountName); diff --git a/repo/js/AutoHoeingOneDragon/manifest.json b/repo/js/AutoHoeingOneDragon/manifest.json index 7b6e8438d..05b075345 100644 --- a/repo/js/AutoHoeingOneDragon/manifest.json +++ b/repo/js/AutoHoeingOneDragon/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "锄地一条龙", - "version": "2.3.0", + "version": "2.3.1", "description": "一站式解决自动化锄地,支持只拾取狗粮,请仔细阅读README.md后使用", "authors": [ { diff --git a/repo/js/AutoNewsKiller/README.md b/repo/js/AutoNewsKiller/README.md index 6261d6581..927b8b227 100644 --- a/repo/js/AutoNewsKiller/README.md +++ b/repo/js/AutoNewsKiller/README.md @@ -1 +1,2 @@ -1. 适配1080p分辨率,其他分辨率不能使用属于正常现象,能够使用纯属巧合 \ No newline at end of file +适配1080p分辨率,其他分辨率不能使用属于正常现象,能够使用纯属巧合 + diff --git a/repo/js/AutoNewsKiller/main.js b/repo/js/AutoNewsKiller/main.js index 4921f35e4..d9b20d580 100644 --- a/repo/js/AutoNewsKiller/main.js +++ b/repo/js/AutoNewsKiller/main.js @@ -10,7 +10,7 @@ let newCount = 0; await genshin.returnMainUi(); keyPress("B"); - let types = Array.from(settings.executingTypes); + let types = Array.from(settings.executingTypes) || ["全选"]; if (types.includes("全选")) { types = ["武器", "圣遗物", @@ -46,7 +46,7 @@ let newCount = 0; await sleep(300); } let scrolls = 0; - while (scrolls < 200) { + while (scrolls < 145) { scrolls++ try { await sleep(1) } catch (e) { break; } while (true) { diff --git a/repo/js/AutoNewsKiller/manifest.json b/repo/js/AutoNewsKiller/manifest.json index 99eb416d8..557b2b291 100644 --- a/repo/js/AutoNewsKiller/manifest.json +++ b/repo/js/AutoNewsKiller/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "点击消除新", - "version": "1.0", + "version": "1.1", "description": "点击消除背包中的新", "authors": [ { diff --git a/repo/js/AutoNewsKiller/settings.json b/repo/js/AutoNewsKiller/settings.json index d2508f74b..cfd03be1f 100644 --- a/repo/js/AutoNewsKiller/settings.json +++ b/repo/js/AutoNewsKiller/settings.json @@ -4,7 +4,7 @@ "type": "multi-checkbox", "label": "要执行的物品种类", "default": [ - "圣遗物" + "全选" ], "options": [ "全选", diff --git a/repo/js/世界权限/assets/RecognitionObject/不允许加入.png b/repo/js/世界权限/assets/RecognitionObject/不允许加入.png new file mode 100644 index 000000000..9551c9875 Binary files /dev/null and b/repo/js/世界权限/assets/RecognitionObject/不允许加入.png differ diff --git a/repo/js/世界权限/assets/RecognitionObject/直接加入.png b/repo/js/世界权限/assets/RecognitionObject/直接加入.png new file mode 100644 index 000000000..a916bdae2 Binary files /dev/null and b/repo/js/世界权限/assets/RecognitionObject/直接加入.png differ diff --git a/repo/js/世界权限/assets/RecognitionObject/确认后可加入.png b/repo/js/世界权限/assets/RecognitionObject/确认后可加入.png new file mode 100644 index 000000000..f1f58ad3c Binary files /dev/null and b/repo/js/世界权限/assets/RecognitionObject/确认后可加入.png differ diff --git a/repo/js/世界权限/assets/RecognitionObject/设置不允许加入.png b/repo/js/世界权限/assets/RecognitionObject/设置不允许加入.png new file mode 100644 index 000000000..7293ce732 Binary files /dev/null and b/repo/js/世界权限/assets/RecognitionObject/设置不允许加入.png differ diff --git a/repo/js/世界权限/assets/RecognitionObject/设置直接加入.png b/repo/js/世界权限/assets/RecognitionObject/设置直接加入.png new file mode 100644 index 000000000..1132a7e08 Binary files /dev/null and b/repo/js/世界权限/assets/RecognitionObject/设置直接加入.png differ diff --git a/repo/js/世界权限/assets/RecognitionObject/设置确认后可加入.png b/repo/js/世界权限/assets/RecognitionObject/设置确认后可加入.png new file mode 100644 index 000000000..6b667d1af Binary files /dev/null and b/repo/js/世界权限/assets/RecognitionObject/设置确认后可加入.png differ diff --git a/repo/js/世界权限/assets/前往道成林并重新登录.json b/repo/js/世界权限/assets/前往道成林并重新登录.json new file mode 100644 index 000000000..812d68120 --- /dev/null +++ b/repo/js/世界权限/assets/前往道成林并重新登录.json @@ -0,0 +1,41 @@ +{ + "info": { + "authors": [ + { + "links": "", + "name": "mno" + } + ], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1766237117953, + "map_match_method": "", + "map_name": "Teyvat", + "name": "前往道成林并重新登录", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "action_params": "", + "id": 1, + "locked": false, + "move_mode": "walk", + "type": "teleport", + "x": 2297.896484375, + "y": -824.2373046875 + }, + { + "action": "exit_and_relogin", + "action_params": "", + "id": 2, + "move_mode": "walk", + "type": "orientation", + "x": 2297.896484375, + "y": -824.2373046875 + } + ] +} \ No newline at end of file diff --git a/repo/js/世界权限/main.js b/repo/js/世界权限/main.js new file mode 100644 index 000000000..bcc248394 --- /dev/null +++ b/repo/js/世界权限/main.js @@ -0,0 +1,99 @@ +let checkInterval = +settings.checkInterval || 50; +(async function () { + let currentState = ""; + let targetState = settings.targetState; + + if (!settings.forceCheck) { + log.info(`当前未启用强化确认模式,设置目标为${targetState}`); + while (true) { + await genshin.returnMainUi(); + await keyPress("F2"); + let attempts = 0; + while (attempts < 20) { + attempts++; + if (await findPNG("确认后可加入", 1)) { + currentState = "确认后可加入"; + break; + } + if (await findPNG("直接加入", 1)) { + currentState = "直接加入"; + break; + } + if (await findPNG("不允许加入", 1)) { + currentState = "不允许加入"; + break; + } + } + if (currentState) { + log.info(`当前世界权限为${currentState}`); + break; + } + } + + if (currentState != targetState) { + await clickPNG(currentState); + await clickPNG("设置" + targetState); + } + } else { + log.info(`当前已启用强化确认模式,设置目标为${targetState}`); + let checkCount = 0; + while (checkCount < 2) { + await pathingScript.runFile(`assets/前往道成林并重新登录.json`); + while (true) { + await genshin.returnMainUi(); + await keyPress("F2"); + let attempts = 0; + while (attempts < 20) { + attempts++; + if (await findPNG("确认后可加入", 1)) { + currentState = "确认后可加入"; + break; + } + if (await findPNG("直接加入", 1)) { + currentState = "直接加入"; + break; + } + if (await findPNG("不允许加入", 1)) { + currentState = "不允许加入"; + break; + } + } + if (currentState) { + log.info(`当前世界权限为${currentState}`); + break; + } + } + + if (currentState != targetState) { + await clickPNG(currentState); + await clickPNG("设置" + targetState); + checkCount = 0; + } else { + checkCount++; + } + } + } + await genshin.returnMainUi(); +})(); + +async function clickPNG(png, maxAttempts = 20) { + const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`)); + return await findAndClick(pngRo, true, maxAttempts); +} + +async function findPNG(png, maxAttempts = 20) { + const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`)); + return await findAndClick(pngRo, false, maxAttempts); +} + +async function findAndClick(target, doClick = true, maxAttempts = 20) { + for (let i = 0; i < maxAttempts; i++) { + const rg = captureGameRegion(); + try { + const res = rg.find(target); + if (res.isExist()) { await sleep(checkInterval * 2 + 50); if (doClick) { res.click(); } return true; } + } finally { rg.dispose(); } + if (i < maxAttempts - 1) await sleep(checkInterval); + } + return false; +} \ No newline at end of file diff --git a/repo/js/世界权限/manifest.json b/repo/js/世界权限/manifest.json new file mode 100644 index 000000000..e96818c02 --- /dev/null +++ b/repo/js/世界权限/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest_version": 1, + "name": "世界权限", + "version": "1.1", + "description": "带确认状态的设置世界权限", + "authors": [ + { + "name": "mno" + } + ], + "settings_ui": "settings.json", + "main": "main.js" +} \ No newline at end of file diff --git a/repo/js/世界权限/settings.json b/repo/js/世界权限/settings.json new file mode 100644 index 000000000..812e23b2b --- /dev/null +++ b/repo/js/世界权限/settings.json @@ -0,0 +1,24 @@ +[ + { + "name": "targetState", + "type": "select", + "label": "选择要设置的世界权限", + "options": [ + "确认后可加入", + "直接加入", + "不允许加入" + ], + "default": "确认后可加入" + }, + { + "name": "forceCheck", + "type": "checkbox", + "label": "强化确认模式,勾选后将确认切换成功才退出" + }, + { + "name": "checkInterval", + "type": "input-text", + "label": "识别间隔(毫秒),设备性能较差经常设置失败时适当调大", + "default": "50" + } +] \ No newline at end of file diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/best0.png b/repo/js/普通奇怪料理/assets/RecognitionObject/best0.png new file mode 100644 index 000000000..f1b710379 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/best0.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/best1.png b/repo/js/普通奇怪料理/assets/RecognitionObject/best1.png new file mode 100644 index 000000000..e412ebdef Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/best1.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/best2.png b/repo/js/普通奇怪料理/assets/RecognitionObject/best2.png new file mode 100644 index 000000000..9e0283e01 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/best2.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/制作.png b/repo/js/普通奇怪料理/assets/RecognitionObject/制作.png new file mode 100644 index 000000000..853c4f76f Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/制作.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/手动烹饪.png b/repo/js/普通奇怪料理/assets/RecognitionObject/手动烹饪.png new file mode 100644 index 000000000..e2d6f9934 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/手动烹饪.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/搜索.png b/repo/js/普通奇怪料理/assets/RecognitionObject/搜索.png new file mode 100644 index 000000000..006c44e0c Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/搜索.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/料理制作.png b/repo/js/普通奇怪料理/assets/RecognitionObject/料理制作.png new file mode 100644 index 000000000..40856d391 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/料理制作.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/确认.png b/repo/js/普通奇怪料理/assets/RecognitionObject/确认.png new file mode 100644 index 000000000..ac96c0679 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/确认.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/确认筛选.png b/repo/js/普通奇怪料理/assets/RecognitionObject/确认筛选.png new file mode 100644 index 000000000..d183dcb8f Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/确认筛选.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/筛选.png b/repo/js/普通奇怪料理/assets/RecognitionObject/筛选.png new file mode 100644 index 000000000..164c52a53 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/筛选.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/结束.png b/repo/js/普通奇怪料理/assets/RecognitionObject/结束.png new file mode 100644 index 000000000..e5954394f Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/结束.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/重置.png b/repo/js/普通奇怪料理/assets/RecognitionObject/重置.png new file mode 100644 index 000000000..1e16d04ed Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/重置.png differ diff --git a/repo/js/普通奇怪料理/assets/RecognitionObject/锅.png b/repo/js/普通奇怪料理/assets/RecognitionObject/锅.png new file mode 100644 index 000000000..daaef3e67 Binary files /dev/null and b/repo/js/普通奇怪料理/assets/RecognitionObject/锅.png differ diff --git a/repo/js/普通奇怪料理/assets/蒙德炉子.json b/repo/js/普通奇怪料理/assets/蒙德炉子.json new file mode 100644 index 000000000..52850589b --- /dev/null +++ b/repo/js/普通奇怪料理/assets/蒙德炉子.json @@ -0,0 +1,115 @@ +{ + "config": { + "realtime_triggers": { + "AutoPick": true + } + }, + "farming_info": { + "allow_farming_count": false, + "duration_seconds": 0, + "elite_details": "", + "elite_mob_count": 0, + "normal_mob_count": 0, + "primary_target": "", + "total_mora": 0 + }, + "info": { + "authors": [ + { + "links": "https://github.com/jiangziyanowo", + "name": "江紫烟owo" + } + ], + "bgi_version": "0.45.0", + "description": "蒙德做菜炉子", + "enable_monster_loot_split": false, + "items": [], + "last_modified_time": 1758891203475, + "map_match_method": "", + "map_name": "Teyvat", + "name": "每周做菜", + "order": 0, + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "id": 1, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "teleport", + "x": -874.724609375, + "y": 2276.950439453125 + }, + { + "action": "", + "id": 2, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "path", + "x": -881.150390625, + "y": 2268.43115234375 + }, + { + "action": "", + "id": 3, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "path", + "x": -887.611328125, + "y": 2256.250244140625 + }, + { + "action": "", + "action_params": "", + "id": 4, + "move_mode": "run", + "point_ext_params": { + "description": "", + "enable_monster_loot_split": false, + "misidentification": { + "arrival_time": 0, + "handling_mode": "previousDetectedPoint", + "type": [ + "unrecognized" + ] + } + }, + "type": "target", + "x": -883.83203125, + "y": 2242.46826171875 + } + ] +} \ No newline at end of file diff --git a/repo/js/普通奇怪料理/main.js b/repo/js/普通奇怪料理/main.js new file mode 100644 index 000000000..f5eea51f7 --- /dev/null +++ b/repo/js/普通奇怪料理/main.js @@ -0,0 +1,262 @@ +let clickDelay = 16; +let sleepTimeRate = +settings.sleepTimeRate || 1; + +(async function () { + if (!settings.targetDish) { + log.info("不填名字我怎么知道你要做啥") + } + await enterCook(); + var extraTime = (+settings.extraTime || 0); + let i = 0; + let targetNum = +settings.targetNum || 10; + while (i < targetNum) { + let text = "" + if (settings.operationMode === "奇怪料理") { + text = "奇怪的" + } + let progress = `${i + 1}/${targetNum}`; + let percent = Math.round(((i + 1) / targetNum) * 100); + log.info(`[${percent}%]正在制作第${progress}个${text}${settings.targetDish}`); + log.debug(`交互或拾取:"${text}${settings.targetDish}"`); + if (await auto_cooking_bgi(extraTime)) { + i++; + } + else await enterCook(); + } +})(); + +async function enterCook() { + const stove = "蒙德炉子"; + let attempts = 0; + let enterCooking = false; + try { + while (attempts < 10) { + await genshin.returnMainUi(); + let filePath = `assets/${stove}.json`; + await pathingScript.runFile(filePath); + keyPress("F"); + if (await findPNG("料理制作")) { + log.info("成功进入料理制作界面"); + enterCooking = true; + break; + } + log.warn("进入料理制作界面失败,重试"); + attempts++; + await genshin.returnMainUi(); + } + } catch (error) { + log.error(`执行 ${stove} 路径时发生错误`); + return; + } + if (enterCooking) { + await clickPNG("筛选"); + await clickPNG("重置"); + await clickPNG("搜索"); + inputText(settings.targetDish); + await sleep(clickDelay * sleepTimeRate); + //keyPress("VK_RETURN"); + await clickPNG("确认筛选"); + await clickPNG("制作"); + } +} + +/** + * + * 自动执行手动烹饪(源于JS脚本: 烹饪熟练度一键拉满-(柒叶子-https://github.com/511760049)) + * + * @param extraTime + * @returns {Promise} + */ +async function auto_cooking_bgi(extraTime) { + let success = false; + moveMouseTo(400, 750); // 移动到屏幕水平中心,垂直750坐标 + await clickPNG("手动烹饪", 200); + + if (settings.operationMode === "奇怪料理") { + await findPNG("结束", 10); + keyPress("VK_SPACE"); + await sleep(50 * sleepTimeRate); + keyPress("VK_SPACE"); + if (await clickPNG("确认", 20 * 3)) { + return true; + } + else return false; + } + + await sleep(1000); // 等待画面稳定 + const checkPoints = [ + { x: 741, y: 772 }, // 原始点1 + { x: 758, y: 766 }, // 中间点1-2 + { x: 776, y: 760 }, // 原始点2 + { x: 793, y: 755 }, // 中间点2-3 + { x: 810, y: 751 }, // 原始点3 + { x: 827, y: 747 }, // 中间点3-4 + { x: 845, y: 744 }, // 原始点4 + { x: 861, y: 742 }, // 中间点4-5 + { x: 878, y: 740 }, // 原始点5 + { x: 897, y: 737 }, // 中间点5-6 + { x: 916, y: 735 }, // 原始点6 + { x: 933, y: 735 }, // 中间点6-7 + { x: 950, y: 736 }, // 原始点7 + { x: 968, y: 736 }, // 中间点7-8 + { x: 986, y: 737 }, // 原始点8 + { x: 1002, y: 738 }, // 中间点8-9 + { x: 1019, y: 740 }, // 原始点9 + { x: 1038, y: 742 }, // 中间点9-10 + { x: 1057, y: 744 }, // 原始点10 + { x: 1074, y: 748 }, // 中间点10-11 + { x: 1092, y: 752 }, // 原始点11 + { x: 1107, y: 757 }, // 中间点11-12 + { x: 1122, y: 762 }, // 原始点12 + { x: 1138, y: 766 }, // 中间点12-13 + { x: 1154, y: 770 }, // 原始点13 + { x: 1170, y: 774 }, // 中间点13-14 + { x: 1193, y: 779 } // 原始点14 + ]; + + // 区域大小 + const regionSize = 60; + + // 加载模板图片 + const templateMat0 = file.readImageMatSync(`assets/RecognitionObject/best0.png`); + const templateMat1 = file.readImageMatSync(`assets/RecognitionObject/best1.png`); + const templateMat2 = file.readImageMatSync(`assets/RecognitionObject/best2.png`); + + // 创建模板匹配识别对象 + const templateRo0 = RecognitionObject.templateMatch(templateMat0); + const templateRo1 = RecognitionObject.templateMatch(templateMat1); + const templateRo2 = RecognitionObject.templateMatch(templateMat2); + templateRo0.threshold = 0.9; + templateRo0.Use3Channels = true; + templateRo1.threshold = 0.9; + templateRo1.Use3Channels = true; + templateRo2.threshold = 0.9; + templateRo2.Use3Channels = true; + // 捕获游戏区域 + const gameRegion = captureGameRegion(); + + // 检查每个点 + for (let i = 0; i < checkPoints.length; i++) { + const point = checkPoints[i]; + + // 裁剪出当前检测区域 + const region = gameRegion.deriveCrop( + point.x - regionSize / 2, + point.y - regionSize / 2, + regionSize, + regionSize + ); + + let result; + if (i < 9) { + result = region.find(templateRo0); + } else if (i >= 18) { + result = region.find(templateRo2); + } else { + result = region.find(templateRo1); + } + region.dispose(); + + if (!result.isEmpty()) { + const segmentTime = 66; + const waitTime = Math.round(i * segmentTime + extraTime); + //log.info(`找到点位${i}号区域`); + await sleep(waitTime); + keyPress("VK_SPACE"); + gameRegion.dispose(); + if (await clickPNG("确认", 20 * 3)) { + return true; + } + else return false; + } + } + gameRegion.dispose(); + return false; + // log.info(`未找到点位区域,烹饪结束`); + // keyPress("ESCAPE"); + // await sleep(1000); + // keyPress("ESCAPE"); + // throw new Error("人家才不是错误呢>_<"); +} + +async function clickPNG(png, maxAttempts = 100, Threshold = 0.9) { + const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`)); + pngRo.Threshold = Threshold; + pngRo.InitTemplate(); + return await findAndClick(pngRo, true, maxAttempts * 50 * sleepTimeRate); +} + +async function findPNG(png, maxAttempts = 100) { + const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`)); + pngRo.Threshold = 0.9; + pngRo.InitTemplate(); + return await findAndClick(pngRo, false, maxAttempts * 50 * sleepTimeRate); +} + +/** + * 通用找图/找RO并可选点击(支持单图片文件路径、单RO、图片文件路径数组、RO数组) + * @param {string|string[]|RecognitionObject|RecognitionObject[]} target + * @param {boolean} [doClick=true] 是否点击 + * @param {number} [timeout=3000] 识别时间上限(ms) + * @param {number} [interval=50] 识别间隔(ms) + * @param {number} [retType=0] 0-返回布尔;1-返回 Region 结果 + * @param {number} [preClickDelay=50] 点击前等待 + * @param {number} [postClickDelay=50] 点击后等待 + * @returns {boolean|Region} 根据 retType 返回是否成功或最终 Region + */ +async function findAndClick(target, + doClick = true, + timeout = 3000, + interval = 50, + retType = 0, + preClickDelay = 50, + postClickDelay = 50) { + try { + // 1. 统一转成 RecognitionObject 数组 + let ros = []; + if (Array.isArray(target)) { + ros = target.map(t => + (typeof t === 'string') + ? RecognitionObject.TemplateMatch(file.ReadImageMatSync(t)) + : t + ); + } else { + ros = [(typeof target === 'string') + ? RecognitionObject.TemplateMatch(file.ReadImageMatSync(target)) + : target]; + } + + const start = Date.now(); + let found = null; + + while (Date.now() - start <= timeout) { + const gameRegion = captureGameRegion(); + try { + // 依次尝试每一个 ro + for (const ro of ros) { + const res = gameRegion.find(ro); + if (!res.isEmpty()) { // 找到 + found = res; + if (doClick) { + await sleep(preClickDelay * sleepTimeRate); + res.click(); + await sleep(postClickDelay * sleepTimeRate); + } + break; // 成功即跳出 for + } + } + if (found) break; // 成功即跳出 while + } finally { + gameRegion.dispose(); + } + await sleep(interval * sleepTimeRate); // 没找到时等待 + } + + // 3. 按需返回 + return retType === 0 ? !!found : (found || null); + + } catch (error) { + log.error(`执行通用识图时出现错误:${error.message}`); + return retType === 0 ? false : null; + } +} \ No newline at end of file diff --git a/repo/js/普通奇怪料理/manifest.json b/repo/js/普通奇怪料理/manifest.json new file mode 100644 index 000000000..2dd8730d8 --- /dev/null +++ b/repo/js/普通奇怪料理/manifest.json @@ -0,0 +1,16 @@ +{ + "manifest_version": 1, + "name": "普通奇怪料理", + "version": "1.2", + "bgi_version": "0.44.8", + "description": "", + "saved_files": [], + "authors": [ + { + "name": "mno", + "links": "https://github.com/Bedrockx" + } + ], + "settings_ui": "settings.json", + "main": "main.js" +} \ No newline at end of file diff --git a/repo/js/普通奇怪料理/settings.json b/repo/js/普通奇怪料理/settings.json new file mode 100644 index 000000000..2bbff89d5 --- /dev/null +++ b/repo/js/普通奇怪料理/settings.json @@ -0,0 +1,34 @@ +[ + { + "name": "targetDish", + "type": "input-text", + "label": "需要烹饪的料理名称" + }, + { + "name": "operationMode", + "type": "select", + "label": "选择烹饪类型", + "options": [ + "奇怪料理", + "普通料理" + ] + }, + { + "name": "targetNum", + "type": "input-text", + "label": "输入目标数量", + "default": "10" + }, + { + "name": "extraTime", + "type": "input-text", + "label": "烹饪时间修正(毫秒)\n制作普通料理时按空格的时机将延后该值(负数则提前)", + "default": "0" + }, + { + "name": "sleepTimeRate", + "type": "input-text", + "label": "识别间隔修正,各种等待时间将被调整为原来的该数值倍\n仅建议在设备较差出现明显问题时调大", + "default": "1" + } +] \ No newline at end of file