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