js:采集cd管理优化食材加工,分离单独的食材加工js (#2584)

This commit is contained in:
mno
2025-12-29 06:55:42 +08:00
committed by GitHub
parent dc7a81fdc6
commit fb19b5dada
54 changed files with 546 additions and 54 deletions

View File

@@ -1762,7 +1762,6 @@ async function isTimeRestricted(timeRule, threshold = 5) {
return false;
}
/**
* 食材加工主函数,用于自动前往指定地点进行食材的加工
*
@@ -1772,8 +1771,20 @@ async function isTimeRestricted(timeRule, threshold = 5) {
* @returns {Promise<void>} 无返回值,执行完所有加工流程后退出
*/
async function ingredientProcessing() {
if (Foods.length == 0) { log.error("未选择要加工的料理/食材"); return; }
if (Foods.length != foodCount.length) { log.error("请检查料理与对应的数量是否一致!"); return; }
const targetFoods = [
"面粉", "兽肉", "鱼肉", "神秘的肉", "黑麦粉", "奶油", "熏禽肉",
"黄油", "火腿", "糖", "香辛料", "酸奶油", "蟹黄", "果酱",
"奶酪", "培根", "香肠"
];
if (Foods.length == 0) { log.error("未选择要加工的食材"); return; }
if (Foods.length != foodCount.length) { log.error("请检查食材与对应的数量是否一致!"); return; }
const taskList = Foods.map((name, i) => `${name}*${foodCount[i]}`).join("");
const tasks = Foods.map((name, idx) => ({
name,
count: Number(foodCount[idx]) || 0,
done: false
}));
log.info(`本次加工食材:${taskList}`);
const stove = "蒙德炉子";
log.info(`正在前往${stove}进行食材加工`);
@@ -1813,13 +1824,6 @@ async function ingredientProcessing() {
}
await clickPNG("食材加工");
// 固定列表只定义一次
const targetFoods = new Set([
"面粉", "兽肉", "鱼肉", "神秘的肉", "黑麦粉", "奶油", "熏禽肉",
"黄油", "火腿", "糖", "香辛料", "酸奶油", "蟹黄", "果酱",
"奶酪", "培根", "香肠"
]);
/* ===== 1. 公共加工流程 ===== */
async function doCraft(i) {
await clickPNG("制作");
@@ -1827,9 +1831,9 @@ async function ingredientProcessing() {
/* ---------- 1. 队列已满 ---------- */
if (await findPNG("队列已满", 1)) {
log.warn(`检测到${Foods[i]}队列已满,等待图标消失`);
log.warn(`检测到${tasks[i].name}队列已满,等待图标消失`);
while (await findPNG("队列已满", 1)) {
log.warn(`检测到${Foods[i]}队列已满,等待图标消失`);
log.warn(`检测到${tasks[i].name}队列已满,等待图标消失`);
await sleep(300);
}
if (await clickPNG("全部领取", 3)) {
@@ -1842,9 +1846,9 @@ async function ingredientProcessing() {
/* ---------- 2. 材料不足 ---------- */
if (await findPNG("材料不足", 1)) {
log.warn(`检测到${Foods[i]}材料不足,等待图标消失`);
log.warn(`检测到${tasks[i].name}材料不足,等待图标消失`);
while (await findPNG("材料不足", 1)) {
log.warn(`检测到${Foods[i]}材料不足,等待图标消失`);
log.warn(`检测到${tasks[i].name}材料不足,等待图标消失`);
await sleep(300);
}
if (await clickPNG("全部领取", 3)) {
@@ -1854,6 +1858,7 @@ async function ingredientProcessing() {
}
Foods.splice(i, 1);
foodCount.splice(i, 1);
return false;
}
@@ -1861,16 +1866,17 @@ async function ingredientProcessing() {
await findPNG("选择加工数量");
click(960, 460);
await sleep(800);
inputText(foodCount[i]);
log.info(`尝试制作${Foods[i]} ${foodCount[i]}`);
inputText(String(tasks[i].count));
log.info(`尝试制作${tasks[i].name} ${tasks[i].count}`);
await clickPNG("确认加工");
await sleep(500);
/* ---------- 4. 已不能持有更多 ---------- */
if (await findPNG("已不能持有更多", 1)) {
log.warn(`检测到${Foods[i]}已满,等待图标消失`);
log.warn(`检测到${tasks[i].name}已满,等待图标消失`);
while (await findPNG("已不能持有更多", 1)) {
log.warn(`检测到${Foods[i]}已满,等待图标消失`);
log.warn(`检测到${tasks[i].name}已满,等待图标消失`);
await sleep(300);
}
if (await clickPNG("全部领取", 3)) {
@@ -1880,6 +1886,7 @@ async function ingredientProcessing() {
}
Foods.splice(i, 1);
foodCount.splice(i, 1);
return false;
}
@@ -1893,8 +1900,6 @@ async function ingredientProcessing() {
}
/* ===== 2. 两轮扫描 ===== */
const done = new Array(Foods.length).fill(false);
// 进入界面先领取一次
if (await clickPNG("全部领取", 3)) {
await clickPNG("点击空白区域继续");
@@ -1902,17 +1907,30 @@ async function ingredientProcessing() {
await sleep(100);
}
/* ---------- 第一轮:直接找 Foods[i]+"1" ---------- */
for (let i = 0; i < Foods.length; i++) {
if (!targetFoods.has(Foods[i])) continue;
if (await clickPNG(Foods[i] + "1", 5)) {
log.info(`${Foods[i]}已找到`);
let lastSuccess = true;
for (let i = 0; i < tasks.length; i++) {
if (!targetFoods.includes(tasks[i].name)) continue;
const retry = lastSuccess ? 5 : 1;
if (await clickPNG(`${tasks[i].name}1`, retry)) {
log.info(`${tasks[i].name}已找到`);
await doCraft(i);
done[i] = true;
tasks[i].done = true;
lastSuccess = true; // 记录成功
} else {
lastSuccess = false; // 记录失败
}
}
/* ---------- 第二轮:先点 item再轮询 5 次识别 ---------- */
const remain1 = tasks.filter(t => !t.done).map(t => `${t.name}*${t.count}`).join("") || "无";
log.info(`剩余待加工食材:${remain1}`);
if (remain1 === "无") {
log.info("所有食材均已加工完毕,跳过第二轮扫描");
await genshin.returnMainUi();
return;
}
const rg = captureGameRegion();
const foodItems = [];
try {
@@ -1926,50 +1944,57 @@ async function ingredientProcessing() {
}
} finally { rg.dispose(); }
for (const item of foodItems) {
click(item.x, item.y); await sleep(200);
click(item.x, item.y); await sleep(200);
log.info(`识别到${foodItems.length}个加工中食材`);
const foodROs = [];
for (let i = 0; i < Foods.length; i++) {
if (!targetFoods.has(Foods[i])) continue; // 只处理目标食材
const ro = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(`assets/RecognitionObject/${Foods[i]}2.png`)
);
ro.Threshold = 0.95;
ro.InitTemplate();
foodROs.push({ idx: i, name: Foods[i], ro }); // 记住原数组下标
}
for (const item of foodItems) {
click(item.x, item.y); await sleep(1 * checkInterval);
click(item.x, item.y); await sleep(6 * checkInterval);
for (let round = 0; round < 5; round++) {
const rg = captureGameRegion();
try {
let hit = false;
/* 2. 在同一帧里用建好的模板依次匹配 */
for (const it of foodROs) {
if (done[it.idx]) continue; // 已完成的跳过
/* 直接扫 tasks模板已挂在 task.ro */
for (const task of tasks) {
if (task.done) continue;
if (!targetFoods.includes(task.name)) continue;
const res = rg.find(it.ro);
/* 首次使用再加载,避免重复 IO */
if (!task.ro) {
task.ro = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(`assets/RecognitionObject/${task.name}2.png`)
);
task.ro.Threshold = 0.9;
task.ro.InitTemplate();
}
if (!task.ro) {
log.warn(`${task.name}2.png 不存在,跳过识别`);
continue;
}
const res = rg.find(task.ro);
if (res.isExist()) {
log.info(`${it.name}已找到`);
res.click();
rg.dispose(); // 提前释放
await doCraft(it.idx);
done[it.idx] = true;
log.info(`${task.name}已找到`);
await doCraft(tasks.indexOf(task));
task.done = true;
hit = true;
break; // 一轮只处理一个
break; // 一轮只处理一个
}
}
if (hit) break; // 本轮已处理,跳出 round
if (hit) break; // 本轮已命中,跳出 round
} finally {
rg.dispose(); // 确保释放截图
rg.dispose();
}
}
}
const remain = tasks.filter(t => !t.done).map(t => `${t.name}*${t.count}`).join("") || "无";
log.info(`剩余待加工食材:${remain}`);
await genshin.returnMainUi();
}

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "采集cd管理",
"version": "2.8.1",
"version": "2.8.2",
"bgi_version": "0.44.8",
"description": "仅面对会操作文件和读readme的用户基于文件夹操作自动管理采集路线的cd会按照路径组的顺序依次运行直到指定的时间并会按照给定的cd类型自动跳过未刷新的路线",
"saved_files": [

View File

@@ -0,0 +1,2 @@
原则上仅用于1080p分辨率其他分辨率能够使用纯属巧合出现问题后果自负
当某种食材原料恰好在本次加工中耗尽时会引起食材位置改变,默认状态下会在未完成加工时尝试重试,可通过自定义配置禁用

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -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
}
]
}

View File

@@ -0,0 +1,311 @@
let ingredientProcessingFood = settings.ingredientProcessingFood;
let foodCounts = settings.foodCount;
let checkInterval = +settings.checkInterval || 50;
let Foods = [];
let foodCount = [];
(async function () {
if (typeof ingredientProcessingFood === 'string' && ingredientProcessingFood.trim()) {
Foods = ingredientProcessingFood
.split(/[,;\s]+/) // 支持中英文逗号、分号、空格
.map(word => word.trim())
.filter(word => word.length > 0);
}
if (typeof foodCounts === 'string' && foodCounts.trim()) {
foodCount = foodCounts
.split(/[,;\s]+/)
.map(word => word.trim())
.filter(word => word.length > 0);
}
await ingredientProcessing();
})();
/**
* 食材加工主函数,用于自动前往指定地点进行食材的加工
*
* 该函数会根据 Foods 和 foodCount 数组中的食材名称和数量,依次查找并制作对应的料食材
* 支持调味品类食材(直接在“食材加工”界面查找)
*
* @returns {Promise<void>} 无返回值,执行完所有加工流程后退出
*/
async function ingredientProcessing() {
const targetFoods = [
"面粉", "兽肉", "鱼肉", "神秘的肉", "黑麦粉", "奶油", "熏禽肉",
"黄油", "火腿", "糖", "香辛料", "酸奶油", "蟹黄", "果酱",
"奶酪", "培根", "香肠"
];
if (Foods.length == 0) { log.error("未选择要加工的食材"); return; }
if (Foods.length != foodCount.length) { log.error("请检查食材与对应的数量是否一致!"); return; }
const taskList = Foods.map((name, i) => `${name}*${foodCount[i]}`).join("");
const tasks = Foods.map((name, idx) => ({
name,
count: Number(foodCount[idx]) || 0,
done: false
}));
log.info(`本次加工食材:${taskList}`);
const stove = "蒙德炉子";
log.info(`正在前往${stove}进行食材加工`);
try {
let filePath = `assets/${stove}.json`;
await pathingScript.runFile(filePath);
} catch (error) {
log.error(`执行 ${stove} 路径时发生错误`);
return;
}
const res1 = await findPNG("交互烹饪锅");
if (res1) {
keyPress("F");
} else {
log.warn("烹饪按钮未找到,正在寻找……");
let attempts = 0;
const maxAttempts = 3;
let foundInRetry = false;
while (attempts < maxAttempts) {
log.info(`${attempts + 1}次尝试寻找烹饪按钮`);
keyPress("W");
const res2 = await findPNG("交互烹饪锅");
if (res2) {
keyPress("F");
foundInRetry = true;
break;
} else {
attempts++;
await sleep(500);
}
}
if (!foundInRetry) {
log.error("多次未找到烹饪按钮,放弃");
return;
}
}
await clickPNG("食材加工");
/* ===== 1. 公共加工流程 ===== */
async function doCraft(i) {
await clickPNG("制作");
await sleep(300);
/* ---------- 1. 队列已满 ---------- */
if (await findPNG("队列已满", 1)) {
log.warn(`检测到${tasks[i].name}队列已满,等待图标消失`);
while (await findPNG("队列已满", 1)) {
log.warn(`检测到${tasks[i].name}队列已满,等待图标消失`);
await sleep(300);
}
if (await clickPNG("全部领取", 3)) {
await clickPNG("点击空白区域继续");
await findPNG("食材加工2");
await sleep(100);
}
return false;
}
/* ---------- 2. 材料不足 ---------- */
if (await findPNG("材料不足", 1)) {
log.warn(`检测到${tasks[i].name}材料不足,等待图标消失`);
while (await findPNG("材料不足", 1)) {
log.warn(`检测到${tasks[i].name}材料不足,等待图标消失`);
await sleep(300);
}
if (await clickPNG("全部领取", 3)) {
await clickPNG("点击空白区域继续");
await findPNG("食材加工2");
await sleep(100);
}
Foods.splice(i, 1);
foodCount.splice(i, 1);
return false;
}
/* ---------- 3. 正常加工流程 ---------- */
await findPNG("选择加工数量");
click(960, 460);
await sleep(800);
inputText(String(tasks[i].count));
log.info(`尝试制作${tasks[i].name} ${tasks[i].count}`);
await clickPNG("确认加工");
await sleep(500);
/* ---------- 4. 已不能持有更多 ---------- */
if (await findPNG("已不能持有更多", 1)) {
log.warn(`检测到${tasks[i].name}已满,等待图标消失`);
while (await findPNG("已不能持有更多", 1)) {
log.warn(`检测到${tasks[i].name}已满,等待图标消失`);
await sleep(300);
}
if (await clickPNG("全部领取", 3)) {
await clickPNG("点击空白区域继续");
await findPNG("食材加工2");
await sleep(100);
}
Foods.splice(i, 1);
foodCount.splice(i, 1);
return false;
}
await sleep(200);
/* 正常完成:仅领取,不移除 */
if (await clickPNG("全部领取", 3)) {
await clickPNG("点击空白区域继续");
await findPNG("食材加工2");
await sleep(100);
}
}
/* ===== 2. 两轮扫描 ===== */
// 进入界面先领取一次
if (await clickPNG("全部领取", 3)) {
await clickPNG("点击空白区域继续");
await findPNG("食材加工2");
await sleep(100);
}
let lastSuccess = true;
for (let i = 0; i < tasks.length; i++) {
if (!targetFoods.includes(tasks[i].name)) continue;
const retry = lastSuccess ? 5 : 1;
if (await clickPNG(`${tasks[i].name}1`, retry)) {
log.info(`${tasks[i].name}已找到`);
await doCraft(i);
tasks[i].done = true;
lastSuccess = true; // 记录成功
} else {
lastSuccess = false; // 记录失败
}
}
const remain1 = tasks.filter(t => !t.done).map(t => `${t.name}*${t.count}`).join("") || "无";
log.info(`剩余待加工食材:${remain1}`);
if (remain1 === "无") {
log.info("所有食材均已加工完毕,跳过第二轮扫描");
await genshin.returnMainUi();
return;
}
/* ---------- 第二轮:无限轮询,直到全部完成或本轮零进展 ---------- */
let allDone; // 本轮是否全部完成
let progress; // 本轮是否至少完成 1 个
while (true) {
allDone = tasks.every(t => t.done);
if (allDone) break; // 条件 1
progress = false; // 先假设本轮零进展
const rg = captureGameRegion();
const foodItems = [];
try {
for (const flag of ['已加工0个', '已加工1个']) {
const mat = file.ReadImageMatSync(`assets/RecognitionObject/${flag}.png`);
const res = rg.findMulti(RecognitionObject.TemplateMatch(mat));
for (let k = 0; k < res.count; ++k) {
foodItems.push({ x: res[k].x, y: res[k].y });
}
mat.dispose();
}
} finally { rg.dispose(); }
log.info(`识别到${foodItems.length}个加工中食材`);
for (const item of foodItems) {
click(item.x, item.y); await sleep(1 * checkInterval);
click(item.x, item.y); await sleep(6 * checkInterval);
for (let round = 0; round < 5; round++) {
const rg = captureGameRegion();
try {
let hit = false;
/* 直接扫 tasks模板已挂在 task.ro */
for (const task of tasks) {
if (task.done) continue;
if (!targetFoods.includes(task.name)) continue;
/* 首次使用再加载,避免重复 IO */
if (!task.ro) {
task.ro = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(`assets/RecognitionObject/${task.name}2.png`)
);
task.ro.Threshold = 0.9;
task.ro.InitTemplate();
}
if (!task.ro) {
log.warn(`${task.name}2.png 不存在,跳过识别`);
continue;
}
const res = rg.find(task.ro);
if (res.isExist()) {
log.info(`${task.name}已找到`);
await doCraft(tasks.indexOf(task));
task.done = true;
hit = true;
progress = true; // 本轮有进展
break; // 一轮只处理一个
}
}
if (hit) break; // 本轮已命中,跳出 round
} finally {
rg.dispose();
}
}
}
const remain = tasks.filter(t => !t.done).map(t => `${t.name}*${t.count}`).join("") || "无";
log.info(`剩余待加工食材:${remain}`);
if (!progress) { // 条件 2本轮零进展
log.info("本轮未再完成任何食材,结束第二轮");
break;
}
if (settings.disableRetry) {
break;
}
}
await genshin.returnMainUi();
}
async function clickPNG(png, maxAttempts = 20) {
//log.info(`调试-点击目标${png},重试次数${maxAttempts}`);
const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`));
pngRo.Threshold = 0.9;
pngRo.InitTemplate();
return await findAndClick(pngRo, true, maxAttempts);
}
async function findPNG(png, maxAttempts = 20) {
//log.info(`调试-识别目标${png},重试次数${maxAttempts}`);
const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`));
pngRo.Threshold = 0.9;
pngRo.InitTemplate();
return await findAndClick(pngRo, false, maxAttempts);
}
async function findAndClick(target, doClick = true, maxAttempts = 60) {
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;
}

View File

@@ -0,0 +1,16 @@
{
"manifest_version": 1,
"name": "食材加工极速版",
"version": "1.0",
"bgi_version": "0.44.8",
"description": "目前最快最稳的食材加工原则上仅用于1080p分辨率其他分辨率能够使用纯属巧合出现问题后果自负",
"saved_files": [],
"authors": [
{
"name": "mno",
"links": "https://github.com/Bedrockx"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,23 @@
[
{
"name": "ingredientProcessingFood",
"type": "input-text",
"label": "食材名称\n用中文逗号分隔"
},
{
"name": "foodCount",
"type": "input-text",
"label": "食材数量\n数量对应上方的食材\n用中文逗号分隔"
},
{
"name": "checkInterval",
"type": "input-text",
"label": "食材加工中的识别间隔(毫秒),仅建议在设备反应较慢出现识别错误时适当调大",
"default": "50"
},
{
"name": "disableRetry",
"type": "checkbox",
"label": "勾选后禁用第二轮加工重试"
}
]