新建简易版自动循环使用料理 (#1550)

* Create 文件夹新建

* Add files via upload

* Delete repo/js/自动循环使用料理/文件夹新建
This commit is contained in:
呱呱z
2025-08-11 19:15:34 +08:00
committed by GitHub
parent 118709ed63
commit 320af9455f
3 changed files with 192 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
/*********************** 配置与常量 ***********************/
// 用户输入,格式如:"2,3;4,5"
const queue1 = settings.queue1 || "";
const queue2 = settings.queue2 || "";
const queue3 = settings.queue3 || "";
const queue4 = settings.queue4 || "";
const queueList = [
{ queue: queue1, seconds: 300 },
{ queue: queue2, seconds: 900 },
{ queue: queue3, seconds: 1800 },
{ queue: queue4, seconds: 1500 }
];
// 用户配置
let isBusy = false; // 全局互斥锁,控制界面操作互斥
let eatQueue = []; // 全局吃药队列,所有任务都往这里推送目标
/*********************** 工具函数 ***********************/
// 解析用户输入,返回目标数组,得到所有目标行列
function parseTargets(queue) {
/*
步骤详解:
1.分组
.split(/[;]/)
支持中英文分号分割,比如 "1,2;3,45,6" 都能正确分组。
2.去除空格
.map(pair => pair.trim())
去掉每组前后的空格,防止用户输入 " 1,2 " 这样的内容出错。
3.过滤空项
.filter(pair => pair.length > 0)
如果用户输入多余分号(如 "1,2;;3,4"),会产生空字符串,这里直接过滤掉。
4.分割行列并转数字
.map(pair => pair.split(/[,]/).map(s => Number(s.trim())))
支持中英文逗号分割,并把每个数字字符串转为数字类型。
也会去除数字前后的空格。
5.只保留合法的行列对
.filter(arr => arr.length === 2 && arr.every(n => !isNaN(n)))
只保留长度为2且都是数字的数组防止用户输入 "1,," 或 "a,b" 这样的无效内容。
6.转为对象格式
.map(([row, col]) => ({ row, col }))
最终输出为 { row: 1, col: 2 } 这样的对象,方便后续处理。
*/
return queue
.split(/[;]/) // ① 按中英文分号分割每组
.map(pair => pair.trim()) // ② 去除每组前后空格
.filter(pair => pair.length > 0) // ③ 过滤掉空字符串(防止多余分隔符导致空项)
.map(pair => pair.split(/[,]/).map(s => Number(s.trim()))) // ④ 按中英文逗号分割,并转为数字
.filter(arr => arr.length === 2 && arr.every(n => !isNaN(n))) // ⑤ 只保留合法的 [row, col] 数组
.map(([row, col]) => ({ row, col })); // ⑥ 转为对象格式 {row, col}
}
// 计算料理图标的坐标,只生成需要的行列
function computeGridCoordinates(targets) {
const allRows = [...new Set(targets.map(t => t.row))];
const allCols = [...new Set(targets.map(t => t.col))];
const gridCoordinates = [];// 存储计算后的坐标
// 计算料理图标的坐标(行列排列)
try {
for (const row of allRows) {
for (const col of allCols) {
const ProcessingX = Math.round(182.5 + (col - 1) * 145);
const ProcessingY = Math.round(197.5 + (row - 1) * 175);
gridCoordinates.push({ row, col, x: ProcessingX, y: ProcessingY });
}
}
return gridCoordinates;
} catch (error) {
log.error(`计算料理图标的坐标时发生异常: ${error.message}`);
}
}
/*********************** 主要逻辑函数 ***********************/
// 定时将本组要吃的药加入全局队列
async function runQueueTask(queue, seconds) {
const targets = parseTargets(queue);
while (true) {
// 极端保护:队列过长时报警并强制清空
if (eatQueue.length > 1000) {
log.error("警告eatQueue 长度超过1000已强制清空");
eatQueue = [];
}
// 到点时把本组要吃的药加入全局队列
eatQueue.push(...targets);
//log.info(`eatQueue 当前长度: ${eatQueue.length}`); // 日志监控
await sleep(seconds * 1000);
}
}
// 吃药调度器,批量处理所有吃药请求
async function eatDispatcher() {
while (true) {
if (eatQueue.length === 0) {
await sleep(500);
continue;
}
// 合并短时间内的请求等待1.5秒收集更多任务
await sleep(1500);
// 再次检查队列合并所有1.5秒内的请求
while (isBusy) await sleep(200); // 等待空闲
isBusy = true; // 上锁
try {
keyPress("B"); await sleep(1000);//开启背包
click(865, 50); await sleep(100);//点击料理区域
// 取出所有要吃的药,去重,保证每个 row,col 只点一次
const uniqueMap = new Map();
for (const t of eatQueue) {
if (typeof t.row === 'number' && typeof t.col === 'number') {
uniqueMap.set(`${t.row},${t.col}`, { row: t.row, col: t.col });
}
}
const batch = Array.from(uniqueMap.values());
eatQueue = []; // 清空全局队列,准备下一轮
const gridCoordinates = computeGridCoordinates(batch);
// 点击料理图标并使用。遍历目标,查找并点击对应坐标
for (const target of batch) {
const coord = gridCoordinates.find(g => g.row === target.row && g.col === target.col);
if (coord) {
click(coord.x, coord.y); await sleep(50); // 点击料理图标
//click(1760, 1025); await sleep(50); //点击使用
}
}
await genshin.returnMainUi(); // 返回主界面
} catch (error) {
log.error(`批量吃药时发生异常: ${error.message}`);
} finally {
isBusy = false; // 无论如何都解锁
}
}
}
/*********************** 主执行入口 ***********************/
(async function () {
// 设置游戏基础参数,初始化环境
setGameMetrics(1920, 1080, 1.25); // 设置编写脚本环境的游戏分辨率和DPI缩放
await genshin.returnMainUi(); // 返回主界面
// 启动吃药调度器和各组任务,并等待它们(实际上这些都是死循环,不会退出)
await Promise.all([
eatDispatcher(), // 启动吃药调度器
...queueList.map(({ queue, seconds }) => runQueueTask(queue, seconds)) // 启动各组任务
]);
})();

View File

@@ -0,0 +1,15 @@
{
"manifest_version": 1,
"name": "自动循环使用料理",
"version": "1.0",
"bgi_version": "0.41.0",
"description": "循环使用背包中指定位置的料理支持300秒、600秒、1800秒、1500秒循环模式。需要在设置中配置料理行列位置。使用前请先将背包中的料理标星置顶处理。",
"tags": ["循环料理使用"],
"authors": [
{
"name": "呱呱z"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,22 @@
[
{
"name": "queue1",
"type": "input-text",
"label": "料理的行列位置 使用【逗号】分隔\n多个料理使用【分号】分隔\n\n300秒循环模式"
},
{
"name": "queue2",
"type": "input-text",
"label": "900秒循环模式"
},
{
"name": "queue3",
"type": "input-text",
"label": "1800秒循环模式"
},
{
"name": "queue4",
"type": "input-text",
"label": "1500秒循环模式"
}
]