js:采集cd管理优化食材加工,分离单独的食材加工js (#2584)
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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": [
|
||||
|
||||
2
repo/js/食材加工极速版/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
原则上仅用于1080p分辨率,其他分辨率能够使用纯属巧合,出现问题后果自负
|
||||
当某种食材原料恰好在本次加工中耗尽时会引起食材位置改变,默认状态下会在未完成加工时尝试重试,可通过自定义配置禁用
|
||||
BIN
repo/js/食材加工极速版/assets/RecognitionObject/交互烹饪锅.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/全部领取.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/兽肉1.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/兽肉2.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/制作.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/培根1.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/培根2.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/奶油1.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/奶油2.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/奶酪1.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/奶酪2.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/已不能持有更多.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/已加工0个.png
Normal file
|
After Width: | Height: | Size: 995 B |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/已加工1个.png
Normal file
|
After Width: | Height: | Size: 670 B |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/材料不足.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/果酱1.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/果酱2.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/火腿1.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/火腿2.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/点击空白区域继续.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/熏禽肉1.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/熏禽肉2.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/确认加工.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/神秘的肉1.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/神秘的肉2.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/糖1.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/糖2.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/蟹黄1.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/蟹黄2.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/选择加工数量.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/酸奶油1.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/酸奶油2.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/队列已满.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/面粉1.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/面粉2.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/食材加工.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/食材加工2.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/香肠1.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/香肠2.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/香辛料1.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/香辛料2.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/鱼肉1.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/鱼肉2.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/黄油1.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/黄油2.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/黑麦粉1.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
repo/js/食材加工极速版/assets/RecognitionObject/黑麦粉2.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
115
repo/js/食材加工极速版/assets/蒙德炉子.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
311
repo/js/食材加工极速版/main.js
Normal 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;
|
||||
}
|
||||
16
repo/js/食材加工极速版/manifest.json
Normal 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"
|
||||
}
|
||||
23
repo/js/食材加工极速版/settings.json
Normal 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": "勾选后禁用第二轮加工重试"
|
||||
}
|
||||
]
|
||||