This reverts commit d07f8e95b8.
@@ -1,2 +1,4 @@
|
||||
进入特定uid世界或自动批准特定用户进入,填写的名字和放在targets文件夹中的截图都会被视为白名单用户放行,使用前务必将世界权限设置为确认后加入
|
||||
自动进入联机状态js测试版本,出什么bug都是正常的,加qq718135749去超市作者
|
||||
|
||||
组队和更多使用说明请加qq群1057307824
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 888 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 971 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,208 +1,480 @@
|
||||
//获取自定义配置
|
||||
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}`);
|
||||
|
||||
// 加载目标 PNG
|
||||
const targetPngs = await readFolder(targetsPath, ".png");
|
||||
// 依次判断并加入
|
||||
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 = [];
|
||||
for (const f of targetPngs) {
|
||||
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(`找到文件${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 });
|
||||
}
|
||||
}
|
||||
log.info(`加载完成共 ${targetsRo.length} 个目标`);
|
||||
|
||||
// ===== 主循环 =====
|
||||
while (new Date() - start < timeout * 60 * 1000) {
|
||||
|
||||
log.info(`加载完成共${targetPngs.length}个目标`);
|
||||
|
||||
while (new Date() - start < timeOut * 60 * 1000) {
|
||||
if (enterMode === "进入他人世界") {
|
||||
//检验队伍编号
|
||||
const playerSign = await getPlayerSign();
|
||||
await sleep(500);
|
||||
if (playerSign > 1) {
|
||||
log.info(`加入成功,队伍编号 ${playerSign}`);
|
||||
if (playerSign != 0) {
|
||||
log.info(`加入世界成功,在队伍中的编号为${playerSign}`);
|
||||
break;
|
||||
} else if (playerSign === -1) {
|
||||
log.warn("队伍编号识别异常,尝试按0p处理");
|
||||
} else {
|
||||
log.error(`不处于多人世界,开始尝试加入`);
|
||||
await genshin.returnMainUi();
|
||||
await sleep(500);
|
||||
}
|
||||
log.info('不处于多人世界,开始尝试加入');
|
||||
await genshin.returnMainUi(); await sleep(500);
|
||||
//反复敲门进入他人世界
|
||||
//我要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;
|
||||
}
|
||||
|
||||
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);
|
||||
//点击申请加入
|
||||
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()) {
|
||||
keyPress("VK_ESCAPE");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
await sleep(500);
|
||||
}
|
||||
}
|
||||
if (await isYUI()) {
|
||||
keyPress("VK_ESCAPE");
|
||||
await waitForMainUI(true);
|
||||
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(); }
|
||||
|
||||
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) {
|
||||
notification.send(`已达到预定人数:${maxEnterCount + 1}`);
|
||||
break;
|
||||
} else {
|
||||
enterCount--;
|
||||
}
|
||||
else enterCount--;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
//等待主界面状态
|
||||
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 (res.isExist()) return false;
|
||||
if (i < 4) await sleep(250);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (result.isExist()) {
|
||||
//log.info("处于主界面");
|
||||
return true; // 如果找到图标,返回 true
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`识别图像时发生异常: ${error.message}`);
|
||||
|
||||
async function isYUI() {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const gameRegion = captureGameRegion();
|
||||
const res = gameRegion.find(yUIRo);
|
||||
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 (res.isExist()) return true;
|
||||
}
|
||||
if (attempts < maxAttempts - 1) { // 最后一次不再 sleep
|
||||
await sleep(250);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
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}`);
|
||||
}
|
||||
|
||||
async function recognizeRequest() {
|
||||
try {
|
||||
const gameRegion = captureGameRegion();
|
||||
for (const { ro, baseName } of targetsRo) {
|
||||
if (gameRegion.find(ro).isExist()) { gameRegion.dispose(); return baseName; }
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gameRegion.dispose();
|
||||
} catch { }
|
||||
try {
|
||||
const gameRegion = captureGameRegion();
|
||||
const resList = gameRegion.findMulti(RecognitionObject.ocr(664, 481, 1355 - 668, 588 - 484));
|
||||
gameRegion.dispose();
|
||||
let hit = null;
|
||||
if (hit) break;
|
||||
}
|
||||
|
||||
// 如果未命中,输出所有识别结果
|
||||
if (!hit) {
|
||||
for (const res of resList) {
|
||||
const txt = res.text.trim();
|
||||
if (targetList.includes(txt)) { hit = txt; break; }
|
||||
log.warn(`识别到"${res.text.trim()}",不在白名单`);
|
||||
}
|
||||
if (!hit) resList.forEach(r => log.warn(`识别到"${r.text.trim()}",不在白名单`));
|
||||
return hit;
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
return hit; // 命中返回白名单项,未命中返回 null
|
||||
} catch (err) {
|
||||
log.error(`OCR 识别异常: ${err.message}`);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "进入联机状态",
|
||||
"version": "1.0.0",
|
||||
"tags": [],
|
||||
"description": "自动进入指定uid世界或批准别人加入,用于进入小号世界采集等",
|
||||
"version": "0.0.4",
|
||||
"tags": [
|
||||
"狗粮"
|
||||
],
|
||||
"description": "配合AAA狗粮联机团购使用,自动进入别人世界或批准别人加入",
|
||||
"saved_files": [
|
||||
"records/*.txt"
|
||||
],
|
||||
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.5 KiB |
@@ -15,11 +15,7 @@
|
||||
使用即表示您已阅读并同意上述条款。
|
||||
|
||||
---
|
||||
### 琪零、**温馨提示**
|
||||
- 如果您在包括但不限于频道、群聊等收到包含以下内容的关于使用bgi【锄地】的建议,请无视,他们给出的建议存在误导,不具有参考价值:
|
||||
- 不带芙/不带盾/带双盾/携带并使用大位移角色/打精英不带万叶/400-0 0-2000/带钟离打非传奇/使用世界使用带有q而非keypress(q),keydown(q)的战斗策略的(出现以上任意一条即可说明该账号对于锄地的理解有误区,不具有参考价值)
|
||||
- 还有写战斗策略,如果你连attack()括号里的数字是什么意思、keypress(q)和直接q的区别、配置组设置自动战斗部分的各个配置项的作用都不知道,那要么先去看官网文档搞清楚这些东西再写,要么别研究了,老老实实用万能策略得了(点名希诺宁 e,attack(2)和希诺宁 e,attack,attack)
|
||||
如果你觉得你被攻击了,那攻击的就是你,自己瞎打着玩没人管你,别一天天的跑出来误导别人
|
||||
(简易配置描述部分)
|
||||
|
||||
### 一、**快速使用指南 ✅**
|
||||
|
||||
@@ -30,7 +26,7 @@
|
||||
- ⚠️ 关键设置,请务必检查!
|
||||
- 切换队伍:❌ 关闭(配队请在脚本自定义配置中填写)
|
||||
- 战斗策略:推荐使用万能策略,不在万能策略中的角色不建议使用
|
||||
- 扫描掉落物光柱:❌ 强烈建议关闭(易卡死、拖慢速度)
|
||||
- 自动拾取掉落物:❌ 强烈建议关闭(易卡死、拖慢速度)
|
||||
- 自动检测战斗结束:✅ 开启,延时设为 0.35-0.5秒(默认1.5秒太慢)
|
||||
- 战斗结束后执行万叶长E:✅ 强烈建议开启(大幅提升狗粮收益),如果你觉得慢,你打280精英然后把捡的狗粮摧毁,和打400不捡狗粮的收益时一样的
|
||||
- 战斗超时:小怪路线建议 ≤60秒;练度低可适当延长
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "锄地一条龙",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.0",
|
||||
"description": "一站式解决自动化锄地,支持只拾取狗粮,请仔细阅读README.md后使用",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
适配1080p分辨率,其他分辨率不能使用属于正常现象,能够使用纯属巧合
|
||||
|
||||
1. 适配1080p分辨率,其他分辨率不能使用属于正常现象,能够使用纯属巧合
|
||||
@@ -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 < 145) {
|
||||
while (scrolls < 200) {
|
||||
scrolls++
|
||||
try { await sleep(1) } catch (e) { break; }
|
||||
while (true) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "点击消除新",
|
||||
"version": "1.1",
|
||||
"version": "1.0",
|
||||
"description": "点击消除背包中的新",
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "multi-checkbox",
|
||||
"label": "要执行的物品种类",
|
||||
"default": [
|
||||
"全选"
|
||||
"圣遗物"
|
||||
],
|
||||
"options": [
|
||||
"全选",
|
||||
|
||||
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "世界权限",
|
||||
"version": "1.1",
|
||||
"description": "带确认状态的设置世界权限",
|
||||
"authors": [
|
||||
{
|
||||
"name": "mno"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
"main": "main.js"
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "targetState",
|
||||
"type": "select",
|
||||
"label": "选择要设置的世界权限",
|
||||
"options": [
|
||||
"确认后可加入",
|
||||
"直接加入",
|
||||
"不允许加入"
|
||||
],
|
||||
"default": "确认后可加入"
|
||||
},
|
||||
{
|
||||
"name": "forceCheck",
|
||||
"type": "checkbox",
|
||||
"label": "强化确认模式,勾选后将确认切换成功才退出"
|
||||
},
|
||||
{
|
||||
"name": "checkInterval",
|
||||
"type": "input-text",
|
||||
"label": "识别间隔(毫秒),设备性能较差经常设置失败时适当调大",
|
||||
"default": "50"
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 239 B |
|
Before Width: | Height: | Size: 652 B |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 873 B |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,115 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
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<number>}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||