diff --git a/repo/js/营养袋吃药统计/README.md b/repo/js/营养袋吃药统计/README.md
index 7d751ea71..71f716120 100644
--- a/repo/js/营养袋吃药统计/README.md
+++ b/repo/js/营养袋吃药统计/README.md
@@ -1,127 +1,120 @@
-# 药品消耗统计脚本使用指南
-
+### 药品消耗统计脚本使用指南(更新版)
## 📋 一、脚本概述
-
-本脚本是一款专为《原神》设计的自动化工具,主要功能如下:
-
-- **自动识别**:通过OCR技术读取背包中指定药品的数量
-- **智能统计**:记录每日药品消耗/新增变化,统计周期为当日4点至次日4点
-- **数据管理**:自动保存30天历史记录,过期数据自动清理
-- **智能提醒**:通过系统通知推送药品变化信息
+本脚本是专为《原神》设计的自动化药品消耗统计工具,核心能力升级如下:
+- **多模式识别**:支持「营养袋模式」「筛选模式」双模式适配不同场景的药品识别
+- **精准OCR读取**:自动识别背包中指定回血/复活药品数量,适配不同界面布局
+- **灵活时间配置**:支持自定义每日统计刷新时间(默认4:00,可设0-24小时及一位小数)
+- **智能数据管理**:自动保留30天历史记录,按自定义时间周期统计药品消耗/新增
+- **多账户隔离**:不同账户数据独立存储,支持合规账户名校验(1-20位中英文/数字)
+- **异常友好处理**:识别失败自动兜底、参数错误智能修正,拒绝进入世界的申请
## 🛠️ 二、环境与工具要求
-
### 必备工具:茶包版BGI
-
-**为什么不推荐公版BGI?**
-
-公版BGI执行JS脚本时默认关闭自动吃药功能,且无法通过脚本代码开启。本脚本的设计逻辑完全依赖自动吃药功能的正常运行:
-
-- 药品消耗主要发生在自动吃药场景中
-- 脚本未内置开启自动吃药的适配代码
+公版BGI默认关闭自动吃药功能且无法通过脚本开启,本脚本依赖该功能实现消耗统计,因此**必须使用茶包版BGI**。
### 系统配置要求
-
-1. **游戏分辨率**:1920×1080(固定不可改)
-2. **游戏内设置**:
- - 装备「便携营养袋」
- - 确保游戏界面无遮挡、无缩放
-3. **模板文件**:确认`assets/RecognitionObject/`文件夹包含所有必要的模板图片
+1. **游戏分辨率**:固定1920×1080(不可修改,影响OCR识别区域定位)
+2. **游戏和js设置**:
+ - 需装备「便携营养袋」
+ - 启用「筛选模式」需要填写完整的药名
## 📥 三、获取茶包版BGI
+1. 优先方式:在脚本仓库搜索关键词「茶包」,进入作者主页下载
+2. 备用方式:查阅「锄地一条龙js」的README文档,通过文档末尾群号加入测测莫酱交流群获取
-1. **直接获取**:在脚本仓库搜索关键词「茶包」,进入作者主页下载
-2. **备用方案**:查阅「锄地一条龙js」的README文档,通过文档末尾的群号加入测测莫酱交流群获取
+## ⚙️ 四、全参数配置说明(新增/细化)
+| 参数项 | 类型 | 填写要求 | 默认值 | 核心说明 |
+|--------|------|----------|--------|----------|
+| **runMode** | 下拉选择 | 二选一:「营养袋模式」/「筛选模式」 | 营养袋模式 | 营养袋模式:读取便携营养袋内药品;筛选模式:通过背包筛选搜索药品 |
+| **initSelect** | 复选框 | 仅初始化/重置时临时勾选 | false | 勾选后运行脚本会删除当日同名记录,重新初始化数据(使用后务必取消) |
+| **recoveryFoodName** | 文本输入 | 与游戏内回血药名称完全一致 | 空 | 例:「美味的甜甜花酿鸡」,名称错误会导致识别失败 |
+| **resurrectionFoodName** | 文本输入 | 与游戏内复活药名称完全一致 | 空 | 例:「美味的提瓦特煎蛋」,支持全量游戏内可食用复活类物品 |
+| **userName** | 文本输入 | 1-20字符,仅支持中英文、数字 | 默认账户 | 多账户区分核心,违规名称自动替换为默认账户 |
+| **refreshTime** | 文本输入 | 0-24之间,支持一位小数 | 4.0 | 自定义每日统计周期分界点(如4.5=4:30),错误值自动修正为4.0 |
+| **loadDelay** | 文本输入 | 非负整数(单位:毫秒) | 800 | 界面打开/切换的等待延迟,低配置设备可适当增大 |
+| **stepDelay** | 文本输入 | 非负整数(单位:毫秒) | 500 | OCR识别/输入药名前的短暂等待,保证识别稳定性 |
-## ⚙️ 四、使用前配置
-
-### 1. 基础检查
-
-- [ ] 游戏分辨率为1920×1080
-- [ ] 已装备「便携营养袋」
-- [ ] 茶包版BGI中已开启「自动吃药」功能
-
-### 2. 参数设置(关键步骤)
-
-| 参数项 | 填写要求 | 示例 |
-|--------|----------|------------|
-| **userName** | 1-20字符,仅支持中英文、数字 | "旅行者001" |
-| **recoveryFoodName** | 与游戏内名称完全一致 | "美味的甜甜花酿鸡" |
-| **resurrectionFoodName** | 与游戏内名称完全一致 | "美味的提瓦特煎蛋" |
-| **initSelect** | 默认关闭,仅初始化时临时开启 | 取消勾选 |
-
-**重要**:药品名称必须完全匹配。
-
-## 🔄 五、操作流程
+### 配置优先级
+脚本运行时参数读取规则:`自定义配置 > 默认值`,参数错误时自动兜底为默认值并输出警告日志。
+## 🔄 五、操作流程(适配双模式)
### 场景一:首次使用 / 新账户
+1. 启用筛选模式需要完成参数配置(填写药品名称),启用营养袋模式则需要营养袋装备两种药品
+2. 确认游戏分辨率1920×1080、BGI自动吃药功能开启,便携营养袋已装备
+3. 运行脚本,自动执行:
+ - 校验账户名合法性 → 打开背包 → 按选定模式识别药品数量
+ - 在`assets/`目录生成「账户名.txt」记录文件
+4. 收到「今日初始化完成」通知,首次配置完成
-1. 完成上述配置后运行脚本
-2. 脚本自动执行:
- - 校验账户名合法性
- - 打开背包识别药品数量
- - 创建记录文件(路径:`assets/账户名.txt`)
-3. 收到「今日初始化完成」通知
+### 场景二:日常使用(分模式)
+#### 营养袋模式
+- 脚本自动打开背包→进入小道具→读取便携营养袋内回血/复活药数量
+- 无需手动筛选,识别速度更快,依赖便携营养袋装备状态
-### 场景二:日常使用
+#### 筛选模式
+- 脚本自动打开背包→进入食物分类→筛选搜索指定药品→读取数量
+- 无需装备营养袋,适配未配置营养袋的场景,识别流程稍长
-- **每日首次运行**(4点后):记录为当日初始值
-- **后续运行**:对比最新数量,计算消耗/新增
-- **自动记录**:每次运行都会保存一次记录数据
-- **智能清理**:仅保留30天内数据
+#### 通用规则
+- 每日首次运行(自定义refreshTime后):记录为当日初始值
+- 后续运行:对比初始值计算消耗/新增(正数=消耗,负数=新增)
+- 每次运行自动保存记录,仅保留30天内数据,过期自动清理
-### 场景三:更换药品或重置数据
+### 场景三:更换药品/重置统计数据
+1. 在设置中勾选`initSelect`选项
+2. 确认药品名称已更新为新名称(如需更换)
+3. 运行脚本一次,收到「强制初始化完成」通知
+4. **立即取消`initSelect`勾选**(避免后续运行重复初始化)
-1. 在设置中**勾选`initSelect`选项**
-2. 运行脚本一次
-3. **务必取消勾选`initSelect`**
-
-**效果**:删除当日同名药品记录,以当前数量重新初始化。
-
-## 📊 六、核心机制说明
-
-### 统计周期规则
-
-- 以**凌晨4点**为分界点
-- 举例:当前时间3点,统计昨天4点至今的数据
-- 当前时间5点,统计今天4点起的数据
-
-### 记录格式示例
+## 📊 六、核心机制(新增/补充)
+### 1. 自定义统计周期计算规则
+以`refreshTime`为分界点,脚本自动判定统计范围:
+- 当前时间 < 当日refreshTime:统计「昨日refreshTime → 今日refreshTime」数据
+- 当前时间 ≥ 当日refreshTime:统计「今日refreshTime → 明日refreshTime」数据
+示例:refreshTime=4.5(4:30),当前时间3:00 → 统计昨天4:30至今;当前时间5:00 → 统计今天4:30起。
+### 2. 记录文件格式
```
-时间:2023/10/15 14:30:45-美味的甜甜花酿鸡-18
-时间:2023/10/15 14:30:45-美味的提瓦特煎蛋-6
+时间:202X/XX/XX XX:XX:XX-【药品名称】-【数量】
+时间:202X/XX/XX XX:XX:XX-【药品名称】-【数量】
```
+每条记录包含时间戳+药品名+数量,双药品记录成对生成,保证数据完整性。
-### 异常处理机制
+### 3. OCR识别异常处理
+| 异常场景 | 处理逻辑 | 通知/日志 |
+|----------|----------|-----------|
+| OCR未识别到文本 | 数量设为0 | 推送「未识别到XX药数量,设置为0」通知 |
+| 药品名称为空 | 识别结果无效 | log.warn「XX药名字没填」 |
+| refreshTime非法 | 自动修正为4.0 | log.warn「刷新时间设置错误,使用默认值4.0」 |
+| 账户名违规 | 替换为默认账户 | log.error「账户名XX违规,使用默认账户」 |
-| 异常情况 | 自动处理方式 |
-|----------|-------------|
-| 药品未识别 | 数量设为0,发送通知提醒 |
-| 记录文件格式错误 | 自动重置文件 |
+## ⚠️ 七、重要注意事项(强化)
+### 1. 运行时机建议
+- 每日首次运行:在锄地/刷本前、药品制作后执行,保证初始值准确
+- 避免在两次脚本运行之间大批量制作/使用药品,防止统计偏差
+- 低配置设备:适当增大loadDelay/stepDelay,提升识别成功率
-## ⚠️ 七、重要注意事项
+### 2. 双模式适配要点
+| 模式 | 优势 | 注意事项 |
+|------|------|----------|
+| 营养袋模式 | 识别快、步骤少 | 必须装备便携营养袋,仅识别营养袋内药品 |
+| 筛选模式 | 无需装备营养袋 | 药品名称必须精准,依赖背包筛选功能正常 |
-### 每日首次使用时机建议
+### 3. 常见误区修正
+❌ 错误:`initSelect`长期勾选 → 每次运行都重置数据,无法统计消耗
+✅ 正确:仅重置/换药品时勾选,运行后立即取消
-- **在锄地一条龙前**
-- **在药品制作后**
-- **避免**在两次脚本运行之间制作大量药品
+❌ 错误:随意修改refreshTime后未重启脚本 → 统计周期混乱
+✅ 正确:修改refreshTime后重新运行脚本,生效新的统计分界点
-### 常见误区
-
-1. **`initSelect`常开**:导致每次运行都重新初始化
-2. **名称不准确**:最常导致识别失败的原因
-3. **窗口遮挡**:影响OCR识别准确率
-4. **多账户混淆**:每个账户有独立记录文件
-
-## 🔧 八、故障排查指南
-
-### 快速自查表
-
-| 问题现象 | 优先检查项 |
-|----------|------------|
-| 药品数量识别为0 | 1. 药品名称准确性
2. 背包中是否有该药品
3. 分辨率是否为1920×1080 |
-| 统计数据异常 | 1. 是否在脚本运行期间操作药品
2. 是否每日4点后首次运行
3. `initSelect`是否误开启 |
-| 自动吃药无效 | 1. 是否使用茶包版BGI
2. BGI中自动吃药功能是否开启 |
+❌ 错误:分辨率改为非1920×1080 → OCR识别区域错位
+✅ 正确:固定游戏分辨率为1920×1080,不可自定义
+## 🔧 八、故障排查指南(新增模式/参数排查)
+| 问题现象 | 优先检查项 | 解决方案 |
+|----------|------------|----------|
+| 药品数量识别为0 | 1. 运行模式是否匹配场景
2. 药品名称是否完全一致
| 1. 营养袋模式需装备营养袋;筛选模式检查背包食物分类
2. 复制游戏内药品全名(含「美味的/冷的」等前缀)
|
+| 统计周期错误 | 1. refreshTime是否合法
2. 系统时间是否准确 | 1. 确认值在0-24之间(如4.0/12.5),错误值会自动修正
2. 同步系统时间到网络标准时间 |
+| 脚本运行卡顿/超时 | 1. loadDelay/stepDelay是否过小
2. 设备性能是否不足 | 1. 逐步增大延迟值(如loadDelay改为1000)
2. 关闭后台无关程序,保证游戏前台运行 |
+| 多账户数据混淆 | 1. userName是否唯一
2. 记录文件是否存在重名 | 1. 为每个账户设置唯一名称(如「旅行者001/旅行者002」)
2. 检查assets目录,删除重名的错误记录文件 |
+| 模式切换后识别失败 | 1. 对应模式的模板图片是否存在
2. 点击坐标是否匹配分辨率 | 1. 确认assets目录有「营养袋.png」「筛选1.png」「筛选2.png」等
2. 重新确认游戏分辨率为1920×1080 |
diff --git a/repo/js/营养袋吃药统计/assets/拒绝.png b/repo/js/营养袋吃药统计/assets/拒绝.png
new file mode 100644
index 000000000..87b176809
Binary files /dev/null and b/repo/js/营养袋吃药统计/assets/拒绝.png differ
diff --git a/repo/js/营养袋吃药统计/assets/营养袋.png b/repo/js/营养袋吃药统计/assets/营养袋.png
new file mode 100644
index 000000000..475863a65
Binary files /dev/null and b/repo/js/营养袋吃药统计/assets/营养袋.png differ
diff --git a/repo/js/营养袋吃药统计/main.js b/repo/js/营养袋吃药统计/main.js
index 7d7e45e85..c2fd6f4c7 100644
--- a/repo/js/营养袋吃药统计/main.js
+++ b/repo/js/营养袋吃药统计/main.js
@@ -1,14 +1,36 @@
let userName = settings.userName || "默认账户";
-const recoveryFoodName = settings.recoveryFoodName || "回血药名字没填";
-const resurrectionFoodName = settings.resurrectionFoodName || "复活药名字没填";
+const mode = settings.runMode || "营养袋模式"
+let recoveryFoodName = settings.recoveryFoodName || "回血药名字没填";
+let resurrectionFoodName = settings.resurrectionFoodName || "复活药名字没填";
const ocrRegion = {
- x: 110,
+ x: 1422,
+ y: 586,
+ width: 300,
+ height: 40
+ };
+const ocrRegion1 = {
+ x: 1420,
+ y: 687,
+ width: 300,
+ height: 40
+ };
+const ocrRegion2 = {
+ x: 105,
y: 242,
- width: 124,
- height: 32
+ width: 140,
+ height: 40
};
const loadDelay = +settings.loadDelay || 800;
const stepDelay = +settings.stepDelay || 500;
+let refreshTime = parseFloat(settings.refreshTime) || 4.0;
+if (isNaN(refreshTime) || refreshTime < 0 || refreshTime >= 24) {
+ refreshTime = 4.0;
+ log.warn(`刷新时间设置错误,使用默认值4.0`);
+}
+// 计算刷新时间的小时和分钟
+const refreshHour = Math.floor(refreshTime);
+const refreshMinute = Math.floor((refreshTime - refreshHour) * 60);
+log.info(`刷新时间为: ${refreshHour}:${String(refreshMinute).padStart(2, '0')}`);
(async function () {
// 检验账户名
async function getUserName() {
@@ -21,6 +43,23 @@ const stepDelay = +settings.stepDelay || 500;
return userName;
}
+ async function close_join_world_popup_window() {
+ const game_region = captureGameRegion();
+ const text_x = 762;
+ const text_y = 29;
+ const text_w = 210;
+ const text_h = 40;
+ const ocr_res = game_region.find(RecognitionObject.ocr(text_x, text_y, text_w, text_h));
+ if (ocr_res) {
+ if (ocr_res.text.includes("进入世界申请")) {
+ log.info("检测到有人申请进入世界,拒绝申请");
+ click(1051, 51);//选择珍贵物品
+ await clickPNG('拒绝', 10);
+ }
+ }
+ game_region.dispose();
+ }
+
async function close_expired_stuff_popup_window() {
const game_region = captureGameRegion();
const text_x = 850;
@@ -62,26 +101,24 @@ const stepDelay = +settings.stepDelay || 500;
if (lines.length === 0) return result;
- // 获取当前时间范围(当天4点至次日4点)
+ // 获取当前时间范围(根据自定义刷新时间)
const now = new Date();
let startTime, endTime;
- if (now.getHours() < 4) {
- // 当前时间在4点前,时间范围为昨天4点至今天4点
- startTime = new Date(now);
- startTime.setDate(now.getDate() - 1);
- startTime.setHours(4, 0, 0, 0);
+ // 创建今天的刷新时间点
+ const todayRefresh = new Date(now);
+ todayRefresh.setHours(refreshHour, refreshMinute, 0, 0);
- endTime = new Date(now);
- endTime.setHours(4, 0, 0, 0);
+ if (now < todayRefresh) {
+ // 当前时间在刷新时间前,时间范围为昨天刷新时间至今天刷新时间
+ startTime = new Date(todayRefresh);
+ startTime.setDate(startTime.getDate() - 1);
+ endTime = new Date(todayRefresh);
} else {
- // 当前时间在4点后,时间范围为今天4点至明天4点
- startTime = new Date(now);
- startTime.setHours(4, 0, 0, 0);
-
- endTime = new Date(now);
- endTime.setDate(now.getDate() + 1);
- endTime.setHours(4, 0, 0, 0);
+ // 当前时间在刷新时间后,时间范围为今天刷新时间至明天刷新时间
+ startTime = new Date(todayRefresh);
+ endTime = new Date(todayRefresh);
+ endTime.setDate(endTime.getDate() + 1);
}
// 时间格式正则:匹配 "时间:YYYY/MM/DD HH:mm:ss"
@@ -195,22 +232,23 @@ const stepDelay = +settings.stepDelay || 500;
// 如果需要删除当天同名记录
if (deleteSameDayRecords) {
- // 获取当前时间范围(当天4点至次日4点)
+ // 获取当前时间范围(根据自定义刷新时间)
let startTime, endTime;
- if (now.getHours() < 4) {
- // 当前时间在4点前,时间范围为昨天4点至今天4点
- startTime = new Date(now);
- startTime.setDate(now.getDate() - 1);
- startTime.setHours(4, 0, 0, 0);
- endTime = new Date(now);
- endTime.setHours(4, 0, 0, 0);
+
+ // 创建今天的刷新时间点
+ const todayRefresh = new Date(now);
+ todayRefresh.setHours(refreshHour, refreshMinute, 0, 0);
+
+ if (now < todayRefresh) {
+ // 当前时间在刷新时间前,时间范围为昨天刷新时间至今天刷新时间
+ startTime = new Date(todayRefresh);
+ startTime.setDate(startTime.getDate() - 1);
+ endTime = new Date(todayRefresh);
} else {
- // 当前时间在4点后,时间范围为今天4点至明天4点
- startTime = new Date(now);
- startTime.setHours(4, 0, 0, 0);
- endTime = new Date(now);
- endTime.setDate(now.getDate() + 1);
- endTime.setHours(4, 0, 0, 0);
+ // 当前时间在刷新时间后,时间范围为今天刷新时间至明天刷新时间
+ startTime = new Date(todayRefresh);
+ endTime = new Date(todayRefresh);
+ endTime.setDate(endTime.getDate() + 1);
}
// 创建药品匹配正则
@@ -275,7 +313,6 @@ const stepDelay = +settings.stepDelay || 500;
if (!res || !res.text) {
continue;
}
-
const numberMatch = res.text.match(pattern);
if (numberMatch) {
const number = parseInt(numberMatch[1] || numberMatch[0]);
@@ -296,6 +333,55 @@ const stepDelay = +settings.stepDelay || 500;
return null;
}
+ async function recognizeFoodItemByOCR(ocrRegion, pattern) {
+ let captureRegion = null;
+ try {
+ const ocrRo = RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height);
+ captureRegion = captureGameRegion();
+ const resList = captureRegion.findMulti(ocrRo);
+
+ if (!resList || resList.length === 0) {
+ log.warn("OCR未识别到任何文本");
+ return { name: null, count: null };
+ }
+
+ for (const res of resList) {
+ if (!res || !res.text) {
+ continue;
+ }
+ const match = res.text.match(pattern);
+ if (match) {
+ let name = null;
+ let count = null;
+
+ if (match[1]) {
+ name = match[1].trim();
+ }
+
+ if (match[2]) {
+ count = parseInt(match[2]);
+ if (isNaN(count)) {
+ count = null;
+ }
+ }
+
+ if (name || count) {
+ return { name, count };
+ }
+ }
+ }
+ }
+ catch (error) {
+ log.error(`OCR识别时发生异常: ${error.message}`);
+ }
+ finally {
+ if (captureRegion) {
+ captureRegion.dispose();
+ }
+ }
+ return { name: null, count: null };
+}
+
async function findAndClick(target, doClick = true, maxAttempts = 60) {
for (let i = 0; i < maxAttempts; i++) {
const rg = captureGameRegion();
@@ -308,72 +394,155 @@ const stepDelay = +settings.stepDelay || 500;
return false;
}
- async function clickPNG(png, maxAttempts = 20) {
+ async function clickPNG(png, maxAttempts = 20, doClick=true) {
// log.info(`调试-点击目标${png},重试次数${maxAttempts}`);
const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/${png}.png`));
pngRo.Threshold = 0.95;
pngRo.InitTemplate();
- return await findAndClick(pngRo, true, maxAttempts);
+ return await findAndClick(pngRo, doClick, maxAttempts);
}
async function main() {
- // 设置分辨率和缩放
- setGameMetrics(1920, 1080, 1);
- await genshin.returnMainUi();
- keyPress("B");//打开背包
- await sleep(1000);
- await close_expired_stuff_popup_window()
- await sleep(loadDelay);
- click(863, 51);//选择食物
- await sleep(loadDelay);
- await clickPNG('筛选1', 1);
- await clickPNG('筛选2', 1);
- await clickPNG('重置');
- await sleep(stepDelay);
- await clickPNG('搜索');
- await sleep(loadDelay);
- log.info(`搜索${recoveryFoodName}`)
- inputText(recoveryFoodName);
- await clickPNG('确认筛选');
- await sleep(stepDelay);
- let recoveryNumber=await recognizeNumberByOCR(ocrRegion,/\d+/) //识别回血药数量
- // 处理回血药识别结果
- if (recoveryNumber === null) {
- recoveryNumber = 0;
- notification.send(`未识别到回血药数量(数量小于10识别不到),设置数量为0,药品名:${recoveryFoodName}`)
- await sleep(5000);
- click(863, 51);//选择食物
+ let recoveryNumber = 0;
+ let resurrectionNumber = 0;
+ // 设置分辨率和缩放
+ setGameMetrics(1920, 1080, 1);
+ await genshin.returnMainUi();
+ keyPress("B");//打开背包
await sleep(1000);
+ // 关闭弹窗
+ await close_expired_stuff_popup_window();
+ await close_join_world_popup_window();
+ await sleep(loadDelay);
+ // 打开界面
+ let maxRetries = 5; // 最大重试次数
+ let retryCount = 0;
+ let successClick = false;
+ // 根据模式选择点击的位置
+ let clickX, clickY;
+ if (mode === "营养袋模式") {
+ clickX = 1051; // 选择小道具
+ clickY = 51;
+ } else if (mode === "筛选模式") {
+ clickX = 863; // 选择食物
+ clickY = 51;
+ }
+ while (retryCount < maxRetries && !successClick) {
+ retryCount++;
+ await close_join_world_popup_window();
+ click(clickX, clickY);
+ await sleep(loadDelay);
+ // 检查是否进入了申请界面(通过查找"拒绝"按钮)
+ if (await clickPNG('拒绝', 3)) { // 找到拒绝按钮,说明在申请界面
+ log.info("检测到进入世界申请,已拒绝,重新尝试点击分类标签");
+ await sleep(stepDelay);
+ continue; // 继续下一次循环
+ }
+ if (mode === "营养袋模式") {
+ if (await clickPNG('营养袋', 1, false)) { // 只检查不点击
+ successClick = true;
+ log.info("成功进入小道具界面");
+ break;
+ }
+ } else if (mode === "筛选模式") {
+ if (await clickPNG('筛选1', 1, false)||await clickPNG('筛选2', 1, false)) { // 只检查不点击
+ successClick = true;
+ log.info("成功进入食物界面");
+ break;
+ }
+ }
+ log.warn(`尝试点击分类标签失败,第${retryCount}次重试`);
+ await sleep(stepDelay);
+ }
+ if (!successClick) {
+ log.error("多次尝试点击分类标签失败,脚本终止");
+ return { recoveryNumber, resurrectionNumber};
+ }
+ if (mode === "营养袋模式") {
+ // 营养袋模式
+ await clickPNG('营养袋', 1);
+ await sleep(loadDelay);
+ const pattern = /(.+?)\s*[(\(](\d+)[份\s]*[)\)]/;
+ // 识别回血药
+ let result = await recognizeFoodItemByOCR(ocrRegion, pattern);
+ if (result.name && result.count !== null) {
+ log.info(`识别到: ${result.name}, 份数: ${result.count}`);
+ } else {
+ log.warn("未识别到有效的回血药信息");
+ }
+ recoveryNumber = result.count; // 识别回血药数量
+ recoveryFoodName = result.name || '未识别到回血药名称'; // 如果识别失败,使用settings中的名字
+ // 处理回血药识别结果
+ if (recoveryNumber === null) {
+ recoveryNumber = 0;
+ notification.send(`未识别到回血药数量,设置数量为0,药品名:${recoveryFoodName}`);
+ }
+ // 识别复活药
+ result = await recognizeFoodItemByOCR(ocrRegion1, pattern);
+ if (result.name && result.count !== null) {
+ log.info(`识别到: ${result.name}, 份数: ${result.count}`);
+ } else {
+ log.warn("未识别到有效的复活药信息");
+ }
+ resurrectionNumber = result.count; // 识别复活药数量
+ resurrectionFoodName = result.name || '未识别到复活药名称'; // 如果识别失败,使用settings中的名字
+ // 处理复活药识别结果
+ if (resurrectionNumber === null) {
+ resurrectionNumber = 0;
+ notification.send(`未识别到复活药数量,设置数量为0,药品名:${resurrectionFoodName}`);
+ }
+ } else if (mode === "筛选模式") {
+ // 食物筛选模式
+ // 先识别回血药
+ await clickPNG('筛选1', 1);
+ await clickPNG('筛选2', 1);
+ await clickPNG('重置');
+ await sleep(stepDelay);
+ await clickPNG('搜索');
+ await sleep(loadDelay);
+ log.info(`搜索${recoveryFoodName}`);
+ inputText(recoveryFoodName);
+ await clickPNG('确认筛选');
+ await sleep(loadDelay);
+ recoveryNumber = await recognizeNumberByOCR(ocrRegion2, /\d+/); // 识别回血药数量
+ // 处理回血药识别结果
+ if (recoveryNumber === null) {
+ recoveryNumber = 0;
+ notification.send(`未识别到回血药数量,设置数量为0,药品名:${recoveryFoodName}`);
+ await sleep(5000);
+ click(863, 51); // 选择食物
+ await sleep(1000);
+ }
+ // 重置筛选,识别复活药
+ await clickPNG('筛选1', 1);
+ await clickPNG('筛选2', 1);
+ await clickPNG('重置');
+ await sleep(stepDelay);
+ await clickPNG('搜索');
+ await sleep(loadDelay);
+ log.info(`搜索${resurrectionFoodName}`);
+ inputText(resurrectionFoodName);
+ await clickPNG('确认筛选');
+ await sleep(loadDelay);
+ resurrectionNumber = await recognizeNumberByOCR(ocrRegion2, /\d+/); // 识别复活药数量
+ // 处理复活药识别结果
+ if (resurrectionNumber === null) {
+ resurrectionNumber = 0;
+ notification.send(`未识别到复活药数量,设置数量为0,药品名:${resurrectionFoodName}`);
+ await sleep(5000);
+ click(863, 51); // 选择食物
+ await sleep(1000);
+ }
+ // 重置筛选
+ await clickPNG('筛选1', 1);
+ await clickPNG('筛选2', 1);
+ await clickPNG('重置');
+ await sleep(stepDelay);
+ await clickPNG('确认筛选');
+ }
+ await genshin.returnMainUi();
+ return { recoveryNumber, resurrectionNumber };
}
- await sleep(loadDelay);
- await clickPNG('筛选1', 1);
- await clickPNG('筛选2', 1);
- await clickPNG('重置');
- await sleep(stepDelay);
- await clickPNG('搜索');
- await sleep(loadDelay);
- log.info(`搜索${resurrectionFoodName}`)
- inputText(resurrectionFoodName);
- await clickPNG('确认筛选');
- await sleep(stepDelay);
- let resurrectionNumber=await recognizeNumberByOCR(ocrRegion,/\d+/) //识别复活药数量
- // 处理复活药识别结果
- if (resurrectionNumber === null) {
- resurrectionNumber = 0;
- notification.send(`未识别到复活药数量(数量小于10识别不到),设置数量为0,药品名:${resurrectionFoodName}`)
- await sleep(5000);
- click(863, 51);//选择食物
- await sleep(1000);
- }
- await clickPNG('筛选1', 1);
- await clickPNG('筛选2', 1);
- await clickPNG('重置');
- await sleep(stepDelay);
- await clickPNG('确认筛选');
- await genshin.returnMainUi();
- return { recoveryNumber, resurrectionNumber };
- }
-
// 主执行流程
userName = await getUserName();
const recordPath = `assets/${userName}.txt`;
diff --git a/repo/js/营养袋吃药统计/manifest.json b/repo/js/营养袋吃药统计/manifest.json
index 89b694383..8c9a69c45 100644
--- a/repo/js/营养袋吃药统计/manifest.json
+++ b/repo/js/营养袋吃药统计/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "吃药统计",
- "version": "1.6.1",
+ "version": "1.7",
"bgi_version": "0.51",
"description": "用于统计指定两个食物的消耗,推荐锄地前后使用",
"authors": [
diff --git a/repo/js/营养袋吃药统计/settings.json b/repo/js/营养袋吃药统计/settings.json
index 9ecfcf192..7be6e7a4a 100644
--- a/repo/js/营养袋吃药统计/settings.json
+++ b/repo/js/营养袋吃药统计/settings.json
@@ -1,9 +1,13 @@
[
{
- "name": "userName",
- "type": "input-text",
- "label": "账户名称\n用于多账户运行时区分不同账户",
- "default": "默认账户"
+ "name": "runMode",
+ "type": "select",
+ "label": "运行模式",
+ "options": [
+ "营养袋模式",
+ "筛选模式"
+ ],
+ "default": "营养袋模式"
},
{
"name": "initSelect",
@@ -11,7 +15,7 @@
"label": "重新初始化药品数量",
"default": false
},
- {
+ {
"name": "recoveryFoodName",
"type": "input-text",
"label": "回血药名称",
@@ -23,16 +27,28 @@
"label": "复活药名称",
"default": ""
},
+ {
+ "name": "userName",
+ "type": "input-text",
+ "label": "账户名称\n用于多账户运行时区分不同账户",
+ "default": "默认账户"
+ },
+ {
+ "name": "refreshTime",
+ "type": "input-text",
+ "label": "每日刷新时间\n用来定义当日刷新时间(0-24之间,支持一位小数)",
+ "default": "4.0"
+ },
{
"name": "loadDelay",
"type": "input-text",
- "label": "加载等待延迟\n用于界面打开和切换的等待\n默认800,单位毫秒",
+ "label": "加载等待延迟\n用于界面打开和切换的等待(默认800,单位毫秒)",
"default": "800"
},
{
"name": "stepDelay",
"type": "input-text",
- "label": "操作间隔延迟\n用于OCR前和输入药名前的短暂等待\n默认500,单位毫秒",
+ "label": "操作间隔延迟\n用于OCR前和输入药名前的短暂等待(默认500,单位毫秒)",
"default": "500"
}
]