mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-29 05:39:51 +08:00
自动循环使用料理 v1.1更新 (#1615)
* 自动循环使用料理 v1.1更新 * 自动循环使用料理js文件遗漏补充 manifest.json文件更新
This commit is contained in:
83
repo/js/自动循环使用料理/README.md
Normal file
83
repo/js/自动循环使用料理/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# 🍱 自动补充 Buff 类药物使用说明
|
||||
|
||||
## ✨ 核心功能
|
||||
|
||||
- 🎯 **双模式智能切换**
|
||||
`坐标定位`(灵活便捷) + `名称搜索`(稳定高效)
|
||||
- ⏲️ **独立冷却管理**
|
||||
为每个食物单独设置使用间隔(秒级精度)
|
||||
- 🔄 **多队列并行执行**
|
||||
同时运行多组食物方案,互不干扰
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 配置指南
|
||||
|
||||
### 1️⃣ 星标前置,坐标定位模式(推荐)
|
||||
|
||||
**格式**:`行,列,间隔秒数(可选)`
|
||||
**示例**:`"2,3; 4,5,60"`
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
| -------- | ------------------------------- | ------ |
|
||||
| `2,3` | 第 2 行第 3 列食物 | 300 秒 |
|
||||
| `4,5,60` | 第 4 行第 5 列食物(60 秒间隔) | - |
|
||||
|
||||
> ✅ **优势**:执行速度最快,稳定性最佳
|
||||
> 📌 **提示**:打开背包从左上角开始计数(第 1 行第 1 列=1,1)
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ 名称搜索模式
|
||||
|
||||
**格式**:`食物全称,间隔秒数(可选),序号(可选)`
|
||||
**示例**:`"甜甜花酿鸡,120; 提瓦特煎蛋,60,2"`
|
||||
|
||||
| 配置项 | 说明 |
|
||||
| ----------------- | ------------------------------ |
|
||||
| `甜甜花酿鸡,120` | 使用第一个匹配项(120 秒间隔) |
|
||||
| `提瓦特煎蛋,60,2` | 使用第二个匹配项(60 秒间隔) |
|
||||
|
||||
> ⚠️ **注意**:名称必须完全一致(含符号和空格)
|
||||
> 🔍 搜索逻辑:按背包顺序从左 → 右,上 → 下扫描
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键限制
|
||||
|
||||
### ❗ 禁用弹窗类药物
|
||||
|
||||
```diff
|
||||
- 生命回复/复活类食物(如"提瓦特煎蛋")
|
||||
+ 仅支持无确认弹窗的增益类食物(如"蒙德土豆饼")
|
||||
```
|
||||
|
||||
> 脚本会自动取消弹窗操作,相关食物无法生效
|
||||
|
||||
### ⚡ 参数规则
|
||||
|
||||
| 参数 | 说明 | 默认值 | 范围 |
|
||||
| ------ | ------------------ | ------ | ---------- |
|
||||
| 间隔 | 使用冷却时间(秒) | 300 | ≥30 |
|
||||
| 序号 | 同名食物选择顺序 | 1 | ≥1 |
|
||||
| 分隔符 | 条目:`;` 或 `;` | - | 兼容中英文 |
|
||||
| | 参数:`,` 或 `,` | | |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 最佳实践
|
||||
|
||||
1. **模式选择优先级**
|
||||
```mermaid
|
||||
graph LR
|
||||
A[首选坐标模式] --> B[名称模式备用]
|
||||
```
|
||||
2. **冷却时间设置**
|
||||
- 常规增益:300 秒
|
||||
- 强力 BUFF:≥300 秒
|
||||
3. **多队列避坑指南**
|
||||
- 避免配置同一位置的食物
|
||||
- 同类食物间隔时间保持一致
|
||||
- 首次使用前手动整理背包
|
||||
|
||||
> 📅 最后更新:2025.08 | ⚙️ 适用版本:v0.48+
|
||||
@@ -1,56 +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 }
|
||||
{ queue: queue1 }
|
||||
];
|
||||
|
||||
// 用户配置
|
||||
let isBusy = false; // 全局互斥锁,控制界面操作互斥
|
||||
let eatQueue = []; // 全局吃药队列,所有任务都往这里推送目标
|
||||
|
||||
|
||||
// 模板识别对象
|
||||
const InventoryInterfaceRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/InventoryInterface.png"),
|
||||
0, 0, 140, 100
|
||||
); // 【背包界面】图标
|
||||
const DisabledFoodInterfaceRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/DisabledFoodInterface.png"),
|
||||
0, 0, 1920, 100
|
||||
); // 【食物界面-未处于】图标
|
||||
const FoodInterfaceRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/FoodInterface.png"),
|
||||
0, 0, 1920, 100
|
||||
); // 【食物界面-已处于】图标
|
||||
|
||||
const FilterButtonRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/FilterButton.png"),
|
||||
0, 0, 665, 1080
|
||||
); // 【筛选】图标
|
||||
const SearchButtonRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/SearchButton.png"),
|
||||
0, 0, 665, 1080
|
||||
); // 【搜索】图标
|
||||
const ConfirmFilterButtonRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/ConfirmFilterButton.png"),
|
||||
0, 0, 665, 1080
|
||||
); // 【确认筛选】按钮
|
||||
const ResetButtonRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/ResetButton.png"),
|
||||
0, 0, 665, 1080
|
||||
); // 【重置】按钮
|
||||
|
||||
const UseButtonRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/UseButton.png"),
|
||||
1570, 985, 230, 65
|
||||
); // 【使用】按钮
|
||||
const CancelButtonRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync("Assets/RecognitionObject/CancelButton.png"),
|
||||
510, 740, 905, 100
|
||||
); // 【取消】按钮
|
||||
|
||||
|
||||
|
||||
/*********************** 工具函数 ***********************/
|
||||
|
||||
// 解析用户输入,返回目标数组,得到所有目标行列
|
||||
function parseTargets(queue) {
|
||||
/*
|
||||
|
||||
/*
|
||||
步骤详解:
|
||||
1.分组
|
||||
.split(/[;;]/)
|
||||
支持中英文分号分割,比如 "1,2;3,4;5,6" 都能正确分组。
|
||||
.split(/[;;]/)
|
||||
支持中英文分号分割,比如 "1,2;3,4;5,6" 都能正确分组。
|
||||
2.去除空格
|
||||
.map(pair => pair.trim())
|
||||
去掉每组前后的空格,防止用户输入 " 1,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 } 这样的对象,方便后续处理。
|
||||
.filter(pair => pair.length > 0)
|
||||
如果用户输入多余分号(如 "1,2;;3,4"),会产生空字符串,这里直接过滤掉。
|
||||
*/
|
||||
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 parseTargets(queue, defaultTime = 300) {
|
||||
try {
|
||||
const result = [];
|
||||
const seenCoord = new Set(); // 只存已经出现过的 "r,c"
|
||||
const seenName = new Map(); // key = "name",value = {idx} 用于覆盖
|
||||
|
||||
const list = queue
|
||||
.split(/[;;]/)
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean)
|
||||
|
||||
for (const s of list) {
|
||||
const parts = s.split(/[,,]/).map(v => v.trim());
|
||||
// 1. 行列坐标
|
||||
if (parts.length >= 2 && /^-?\d+$/.test(parts[0]) && /^-?\d+$/.test(parts[1])) {
|
||||
const [row, col, time] = parts.map(Number);
|
||||
const key = `${row},${col}`;
|
||||
if (!seenCoord.has(key)) {
|
||||
seenCoord.add(key);
|
||||
result.push({
|
||||
type: 'coord',
|
||||
data: { row, col },
|
||||
time: (!isNaN(time) && time > 0) ? time : defaultTime,
|
||||
lastTime: 0
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. 食物名
|
||||
if (parts[0]) {
|
||||
const name = parts[0];
|
||||
let time = defaultTime, nth = 1;
|
||||
if (parts.length === 2) {
|
||||
// 只有两个参数时,第二个参数总是 time
|
||||
const t = Number(parts[1]);
|
||||
if (!isNaN(t) && t > 0) time = t;
|
||||
} else if (parts.length === 3) {
|
||||
// 三个参数时,第二个是 time,第三个是 nth
|
||||
const t = Number(parts[1]), n = Number(parts[2]);
|
||||
if (!isNaN(t) && t > 0) time = t;
|
||||
if (!isNaN(n) && n >= 1 && n <= 40 && n === Math.floor(n)) nth = n;
|
||||
}
|
||||
// 后出现的覆盖先出现的
|
||||
if (seenName.has(name)) {
|
||||
result[seenName.get(name)] = {
|
||||
type: 'search',
|
||||
data: { name, nth },
|
||||
time,
|
||||
lastTime: 0
|
||||
};
|
||||
} else {
|
||||
seenName.set(name, result.length);
|
||||
result.push({
|
||||
type: 'search',
|
||||
data: { name, nth },
|
||||
time,
|
||||
lastTime: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
log.error(`解析队列时发生错误: ${error.message}`);
|
||||
return []; // 返回空数组表示解析失败
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空吃药队列并返回被删除的元素。
|
||||
* @returns {Array} 被删除的元素数组,如果清空失败则返回空数组。
|
||||
*/
|
||||
function flushEatQueue() {
|
||||
try {
|
||||
return eatQueue.splice(0);// splice 会原地清空并返回被删元素
|
||||
} catch (error) {
|
||||
log.error(`清空吃药队列时发生异常: ${error.message}`);
|
||||
return []; // 返回空数组表示清空失败
|
||||
}
|
||||
}
|
||||
|
||||
// 计算料理图标的坐标,只生成需要的行列
|
||||
@@ -62,8 +161,8 @@ function computeGridCoordinates(targets) {
|
||||
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);
|
||||
const ProcessingX = Math.round(185 + (col - 1) * 145);
|
||||
const ProcessingY = Math.round(200 + (row - 1) * 175);
|
||||
gridCoordinates.push({ row, col, x: ProcessingX, y: ProcessingY });
|
||||
}
|
||||
}
|
||||
@@ -74,65 +173,244 @@ function computeGridCoordinates(targets) {
|
||||
|
||||
}
|
||||
|
||||
// 模板匹配并点击
|
||||
async function findAndClick(target) {
|
||||
try {
|
||||
const gameRegion = captureGameRegion();
|
||||
const found = gameRegion.find(target);
|
||||
if (found && found.isExist()) {
|
||||
found.click();
|
||||
}
|
||||
gameRegion.dispose();
|
||||
} catch (error) {
|
||||
log.error(`findAndClick 出错: ${error.message}`);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获游戏区域并查找指定模板,返回匹配结果对象
|
||||
* @param {object} templateRo 模板识别对象
|
||||
* @returns {object} 匹配结果对象
|
||||
*/
|
||||
function findInGameRegion(templateRo) {
|
||||
try {
|
||||
const gameRegion = captureGameRegion();
|
||||
const result = gameRegion.find(templateRo);
|
||||
gameRegion.dispose();
|
||||
return result;
|
||||
} catch (error) {
|
||||
log.error(`findInGameRegion 出错: ${error.message}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 点击“使用”后,检测并处理可能出现的弹窗
|
||||
* @param {number} timeout 最长等待时间(ms)
|
||||
* @returns {Promise<boolean>} true=检测到弹窗并已点击取消,false=无弹窗
|
||||
*/
|
||||
async function handlePopupAfterUse(timeout = 300) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeout) {
|
||||
const cancel = findInGameRegion(CancelButtonRo);
|
||||
if (cancel.isExist()) {
|
||||
log.warn("检测到弹窗,点击【取消】关闭");
|
||||
try {
|
||||
toast("检测到弹窗,已自动取消");
|
||||
} catch (e) { /* 某些环境无 toast,忽略 */ }
|
||||
cancel.click();
|
||||
await sleep(300); // 给界面一点时间消失
|
||||
return true;
|
||||
}
|
||||
await sleep(100);
|
||||
}
|
||||
// 超时未匹配到,视为无弹窗
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*********************** 主要逻辑函数 ***********************/
|
||||
|
||||
// ---------- 算下一次该推送谁 ----------
|
||||
function nextTick(targets) {
|
||||
const now = Date.now();
|
||||
// 找出距离下一次推送最近的那条记录
|
||||
let minDelay = 1000; // 默认最小 1 秒
|
||||
for (const t of targets) {
|
||||
const due = (t.lastPushTime || 0) + t.time * 1000;
|
||||
const delay = Math.max(0, due - now);
|
||||
if (delay < minDelay) minDelay = delay;
|
||||
}
|
||||
// 不能为 0,至少给 50 ms
|
||||
return Math.max(50, minDelay);
|
||||
}
|
||||
|
||||
// 定时将本组要吃的药加入全局队列
|
||||
async function runQueueTask(queue, seconds) {
|
||||
async function runQueueTask(queue) {
|
||||
const targets = parseTargets(queue);
|
||||
while (true) {
|
||||
// 立即推一次
|
||||
// 给每个目标初始化 lastPushTime
|
||||
targets.forEach(t => t.lastPushTime = 0);
|
||||
|
||||
// 把推送逻辑写成内部函数,立即执行一次,再定时执行
|
||||
const pushOnce = () => {
|
||||
const now = Date.now();
|
||||
// 极端保护:队列过长时报警并强制清空
|
||||
if (eatQueue.length > 1000) {
|
||||
log.error("警告:eatQueue 长度超过1000,已强制清空!");
|
||||
eatQueue = [];
|
||||
flushEatQueue();
|
||||
}
|
||||
// 到点时把本组要吃的药加入全局队列
|
||||
eatQueue.push(...targets);
|
||||
//log.info(`eatQueue 当前长度: ${eatQueue.length}`); // 日志监控
|
||||
await sleep(seconds * 1000);
|
||||
|
||||
// 把本组目标推送到全局队列(去重)
|
||||
for (const t of targets) {
|
||||
if (now - (t.lastPushTime || 0) >= t.time * 1000) {
|
||||
// 真正到点才推送
|
||||
const key = t.type === 'coord'
|
||||
? `${t.data.row},${t.data.col}`
|
||||
: t.data.name; // 同名食物也算重复
|
||||
if (!eatQueue.some(e =>
|
||||
(e.type === 'coord' && key === `${e.data.row},${e.data.col}`) ||
|
||||
(e.type === 'search' && key === e.data.name)
|
||||
)) {
|
||||
eatQueue.push(t);
|
||||
} t.lastPushTime = now; // 更新上一次推送时间
|
||||
}
|
||||
}
|
||||
};
|
||||
// 立即推一次,然后循环
|
||||
pushOnce();
|
||||
while (true) {
|
||||
const wait = nextTick(targets);
|
||||
await sleep(wait);
|
||||
pushOnce();
|
||||
}
|
||||
}
|
||||
|
||||
// 吃药调度器,批量处理所有吃药请求
|
||||
async function eatDispatcher() {
|
||||
let firstRun = true;
|
||||
while (true) {
|
||||
if (eatQueue.length === 0) {
|
||||
await sleep(500);
|
||||
await sleep(200);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 合并短时间内的请求,等待1.5秒收集更多任务
|
||||
await sleep(1500);
|
||||
// 只有非首次才等待 1.5 s 做批量
|
||||
if (!firstRun) await sleep(1500);
|
||||
firstRun = false;
|
||||
|
||||
// 再次检查队列,合并所有1.5秒内的请求
|
||||
while (isBusy) await sleep(200); // 等待空闲
|
||||
isBusy = true; // 上锁
|
||||
const batch = flushEatQueue();
|
||||
try {
|
||||
keyPress("B"); await sleep(1000);//开启背包
|
||||
click(865, 50); await sleep(100);//点击料理区域
|
||||
let InventoryInterface = findInGameRegion(InventoryInterfaceRo);
|
||||
//开启背包
|
||||
|
||||
// 取出所有要吃的药,去重,保证每个 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 });
|
||||
if (!InventoryInterface.isExist()) {
|
||||
log.info("未检测到背包界面,尝试返回主界面并打开背包");
|
||||
await genshin.returnMainUi();
|
||||
keyPress("B"); await sleep(1000);
|
||||
} else {
|
||||
log.info("检测到处于背包界面");
|
||||
}
|
||||
|
||||
let FoodInterface = findInGameRegion(FoodInterfaceRo);
|
||||
if (!FoodInterface.isExist()) {
|
||||
log.info("未处于食物界面,准备点击食物界面图标");
|
||||
await findAndClick(DisabledFoodInterfaceRo); await sleep(600);// 点击食物界面图标
|
||||
} else {
|
||||
log.info("已经处于食物界面,准备点击料理区域");
|
||||
}
|
||||
|
||||
|
||||
// 计算提前补药的时间窗口
|
||||
const now = Date.now();
|
||||
const PRE_TIME = 5000; // 5秒提前量
|
||||
|
||||
// 只处理5秒内需要补的药
|
||||
const coordTasks = batch.filter(t => t.type === 'coord' && (now - (t.lastTime || 0) + PRE_TIME >= t.time * 1000));// 行列
|
||||
const searchTasks = batch.filter(t => t.type === 'search' && (now - (t.lastTime || 0) + PRE_TIME >= t.time * 1000));// 食物名
|
||||
|
||||
/* ---------- 2. 处理坐标点击 ---------- */
|
||||
if (coordTasks.length) {
|
||||
const grid = computeGridCoordinates(
|
||||
coordTasks.map(t => t.data)
|
||||
);
|
||||
const now = Date.now();
|
||||
for (const tk of coordTasks) {
|
||||
if (now - (tk.lastTime || 0) >= tk.time * 1000) {
|
||||
const c = grid.find(g =>
|
||||
g.row === tk.data.row && g.col === tk.data.col);
|
||||
if (c) {
|
||||
click(c.x, c.y); await sleep(50);
|
||||
await findAndClick(UseButtonRo);
|
||||
await handlePopupAfterUse();
|
||||
tk.lastTime = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const batch = Array.from(uniqueMap.values());
|
||||
eatQueue = []; // 清空全局队列,准备下一轮
|
||||
|
||||
const gridCoordinates = computeGridCoordinates(batch);
|
||||
/* ---------- 3. 处理搜索吃药 ---------- */
|
||||
for (const tk of searchTasks) {
|
||||
const now = Date.now();
|
||||
if (now - (tk.lastTime || 0) < tk.time * 1000) continue;
|
||||
try {
|
||||
/* 3-2 判断是否已处于搜索界面(通过 SearchButton 是否存在) */
|
||||
let SearchButton = findInGameRegion(SearchButtonRo);
|
||||
if (!SearchButton.isExist()) {
|
||||
log.info("未处于搜索界面,先点【筛选】");
|
||||
await findAndClick(FilterButtonRo);
|
||||
await sleep(300);
|
||||
} else {
|
||||
log.info("已处于搜索界面,直接点击搜索框");
|
||||
}
|
||||
/* 3-3 点击搜索框 -> 输入食物名 -> 确认筛选 */
|
||||
await findAndClick(SearchButtonRo);
|
||||
await sleep(200);
|
||||
if (typeof inputText === 'function') {
|
||||
inputText(tk.data.name); // 推荐:直接输入字符串
|
||||
} else {
|
||||
log.error("请实现 inputText 方法以支持中文输入");
|
||||
}
|
||||
await sleep(200);
|
||||
await findAndClick(ConfirmFilterButtonRo);
|
||||
await sleep(1000);
|
||||
/* 3-4 只有 nth>1 时才需要精确定位并点击,否则直接点第 1 个 */
|
||||
const nth = tk.data.nth || 1;
|
||||
|
||||
// 点击料理图标并使用。遍历目标,查找并点击对应坐标
|
||||
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); // 点击使用
|
||||
if (nth !== 1) {
|
||||
// 默认第 1 个已高亮,无需再计算坐标
|
||||
// 计算第 n 个食物坐标(一行 8 个)
|
||||
const col = ((nth - 1) % 8) + 1;
|
||||
const row = Math.floor((nth - 1) / 8) + 1;
|
||||
const x = Math.round(182.5 + (col - 1) * 145);
|
||||
const y = Math.round(197.5 + (row - 1) * 175);
|
||||
click(x, y);
|
||||
await sleep(300);
|
||||
}
|
||||
/* 3-5 使用 */
|
||||
await findAndClick(UseButtonRo);
|
||||
await handlePopupAfterUse();
|
||||
/* 3-6 重置筛选 */
|
||||
await findAndClick(FilterButtonRo);
|
||||
await sleep(200);
|
||||
await findAndClick(ResetButtonRo);
|
||||
await sleep(200);
|
||||
|
||||
tk.lastTime = now;
|
||||
} catch (err) {
|
||||
log.error(`搜索吃药流程异常: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
log.info("批量吃药完成,准备返回主界面");
|
||||
await genshin.returnMainUi(); // 返回主界面
|
||||
} catch (error) {
|
||||
log.error(`批量吃药时发生异常: ${error.message}`);
|
||||
await genshin.returnMainUi(); // 返回主界面
|
||||
} finally {
|
||||
isBusy = false; // 无论如何都解锁
|
||||
}
|
||||
@@ -149,7 +427,7 @@ async function eatDispatcher() {
|
||||
// 启动吃药调度器和各组任务,并等待它们(实际上这些都是死循环,不会退出)
|
||||
await Promise.all([
|
||||
eatDispatcher(), // 启动吃药调度器
|
||||
...queueList.map(({ queue, seconds }) => runQueueTask(queue, seconds)) // 启动各组任务
|
||||
...queueList.map(({ queue }) => runQueueTask(queue)) // 启动各组任务
|
||||
|
||||
]);
|
||||
})();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "自动循环使用料理",
|
||||
"version": "1.0",
|
||||
"name": "自动循环使用背包料理",
|
||||
"version": "1.1",
|
||||
"bgi_version": "0.41.0",
|
||||
"description": "循环使用背包中指定位置的料理,支持300秒、600秒、1800秒、1500秒循环模式。需要在设置中配置料理行列位置。使用前请先将背包中的料理标星置顶处理。",
|
||||
"description": "循环使用背包中的料理。需要在设置中配置料理行列位置或名称。建议使用行列模式,将料理标星前置处理。",
|
||||
"tags": ["循环料理使用"],
|
||||
"authors": [
|
||||
{
|
||||
|
||||
@@ -2,21 +2,6 @@
|
||||
{
|
||||
"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秒循环模式"
|
||||
"label": "自动循环补Buff类药\n料理的行,列,补药间隔 \n料理名,补药间隔,搜索后食用第几个\n补药间隔默认300s,搜索食用默认第1个\n使用示例:【2,2,900;致水神】\n900s循环使用第二行第二个料理;\n搜索“致水神”后使用第1个\n多条用分号隔开"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user