From 03182ce5b070af23aaf95bfe48d720d19395f894 Mon Sep 17 00:00:00 2001 From: DarkFlameMaster <1004452714@qq.com> Date: Sun, 16 Nov 2025 09:26:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=B0BOSS=E6=94=AF?= =?UTF-8?q?=E6=8C=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=96=B0=E7=9A=84=E5=88=B7?= =?UTF-8?q?=E5=8F=96=E9=80=BB=E8=BE=91=20(#2356)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加新BOSS支持,添加新的刷取逻辑 * 补充readme --- repo/js/批量讨伐角色养成材料BOSS/README.md | 102 +++---- ...水精灵快速前往.json => 纯水精灵战斗后快速前往.json} | 0 .../assets/Pathing/重拳出击鸭前往.json | 44 +++ .../assets/Pathing/重拳出击鸭战斗后快速前往.json | 26 ++ .../assets/Pathing/霜夜巡天灵主前往.json | 44 +++ .../assets/Pathing/霜夜巡天灵主战斗后快速前往.json | 26 ++ .../assets/config/example.json | 279 +----------------- repo/js/批量讨伐角色养成材料BOSS/main.js | 197 ++++++++++--- .../js/批量讨伐角色养成材料BOSS/manifest.json | 9 +- repo/js/批量讨伐角色养成材料BOSS/reward.js | 16 +- .../js/批量讨伐角色养成材料BOSS/settings.json | 37 ++- repo/js/批量讨伐角色养成材料BOSS/utils.js | 16 + 12 files changed, 418 insertions(+), 378 deletions(-) rename repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/{纯水精灵快速前往.json => 纯水精灵战斗后快速前往.json} (100%) create mode 100644 repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭前往.json create mode 100644 repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭战斗后快速前往.json create mode 100644 repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主前往.json create mode 100644 repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主战斗后快速前往.json create mode 100644 repo/js/批量讨伐角色养成材料BOSS/utils.js diff --git a/repo/js/批量讨伐角色养成材料BOSS/README.md b/repo/js/批量讨伐角色养成材料BOSS/README.md index 229512c34..3585ca7c3 100644 --- a/repo/js/批量讨伐角色养成材料BOSS/README.md +++ b/repo/js/批量讨伐角色养成材料BOSS/README.md @@ -1,40 +1,20 @@ # 批量讨伐角色养成材料BOSS -## 零、脚本特点 +## 一、功能简介 1. 一次性配置多个角色材料的boss 2. 体力不足时保存进度,每次运行时断点续打 - -## 一、调度器设置 - -1. **调度器配置组设置**: - * **开启**:战斗配置 - * **开启** 自动检测战斗结束 - * **选择战斗策略** 根据队伍自动选择 - -2. **战斗超时时间**: - * **位置**:调度器 -> 【战斗配置】 -> 【战斗超时(秒)】 - * **建议**:请设置一个**较长**的超时时间。此时间应大于你所有已配置Boss中,打的最慢的那个所需的时间,并额外增加一些容错时间(例如30-60秒)。如果超时,脚本会判定战斗结束。 - * **替代方案**:如果不使用调度器的【战斗配置】,可以在独立任务中设置超时时间。 +3. 支持三种刷取模式,包括一次性刷取、一次性刷取但每日限量、每日重复刷取 -## 二、不支持的BOSS -以下Boss目前**未添加**尚未添加支持: -* 无相之风 -* 黄金王兽 -* 无相之冰 -* 深海龙蜥之群 - - - -## 三、脚本使用指南 +## 二、脚本使用指南 ### 1. 首次运行配置(添加Boss) * **步骤**: 1. 右键点击脚本。 2. 选择【修改JS脚本自定义配置】。 - 3. 选择【选择模式】 -> 【追加boss】。 - 4. 在弹出的界面中,选择要挑战的Boss、设置讨伐队伍、是否每次回七天神像回血、以及讨伐次数。 + 3. 【选择模式】 -> 【追加boss】。 + 4. 在弹出的界面中,选择要挑战的Boss、设置讨伐队伍、是否每次回七天神像回血、以及讨伐次数等 5. **关闭**该配置窗口。 6. 运行一次脚本,完成Boss的添加。 @@ -52,6 +32,30 @@ * **追加Boss**:此操作是**新增**一个Boss条目到列表末尾,**不会**合并或增加同名Boss的数量。 * *示例*:列表中已有“急冻树”和“爆炎树”,再次追加“急冻树”,列表将变为:“急冻树”、“爆炎树”、“急冻树”。 * **删除Boss**:执行删除操作会**清除所有**同名的Boss条目。 +### 4.选项说明 +> !!!暂未生效,需要等BGI更新再更新!!! 配置后会保存在config,之后版本更新后会沿用这个配置。 +>1. **战斗策略名**: +> * 如果对战斗策略没特殊要求,不需要修改,**保证在`\BetterGI\User\AutoFight\`目录下有适合你配队的策略即可** +> * **如果需要自定义策略,需注意:** +> * 必须在`\BetterGI\User\AutoFight\`目录下存在,只需要填写文件名,不需要后缀,比如文件名是`1.四神挂机[推荐].txt`,只需要填`1.四神挂机>[推荐]` +> * 如果在文件夹内,必须包含目录结构,比如战斗策略在`\BetterGI\User\AutoFight\群友分享\仆人纯火.txt`,你需要填写`群友分享\仆人纯火` +> * 简单来说,按`BGI本体->独立任务->自动战斗->选择战斗策略`中的格式填写 +> +>2. **战斗超时时间**: +> * **每个boss独立设置** ,单位为秒,默认为240秒,即4分钟,如果超过这个时间,强制结束战斗 + +3. **刷取模式**: + >刷取的前提是有体力,没体力会自动停止 + * **一次性**:boss刷取次数完成后不再刷取这个配置。且按添加顺序,刷完一个配置后,才会刷取下一个配置。需配置`讨伐次数`。选择此模式时,`每日限量次数`选项无效 + * **一次性-每日限量**:跟一次性类似,但可以在`每日限量次数`限制刷取的次数,刷够次数后会按添加顺序刷取下一配置,需同时配置`讨伐次数`跟`每日限量次数` + * **每日重置**:简单说就是每天都刷取`每日限量次数`指定的次数,无论刷完与否,次日重置。选择此模式时,`讨伐次数`选项无效 + +## 三、不支持的BOSS +以下Boss目前**未添加**尚未添加支持: +* 无相之风 +* 黄金王兽 +* 无相之冰 +* 深海龙蜥之群 ## 四、手动编辑配置文件 (config.json) @@ -64,39 +68,37 @@ ```json [ { - "name": "爆炎树", // Boss名称 - "totalCount": 20, // 总讨伐次数,比如20次 - "completedCount": 0, // 已完成次数,填0 - "remainingCount": 20, // 剩余次数,手动添加时填写总讨伐次数,比如20次 - "team": "不切换", // 讨伐队伍,填不切换就不会换队伍,填队伍名会自动切换队伍 - "returnToStatueAfterEachRound": false // 每轮结束后是否回七天神像回血 - }, - { - "name": "掣电树", - "totalCount": 4, - "completedCount": 0, - "remainingCount": 4, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "爆炎树", - "totalCount": 2, - "completedCount": 0, - "remainingCount": 2, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, + "name": "魔像督军", // Boss名称 + "totalCount": 20, // 总讨伐次数 + "remainingCount": 20, // 剩余次数,手动添加时填写总讨伐次数 + "team": "四神", // 讨伐队伍名,默认不切换 + "returnToStatueAfterEachRound": true, // 每轮结束后是否回七天神像回血 + "farmMode": "一次性-每日限量", // 刷取模式,可选:一次性、一次性-每日限量、每日重置 + "lastFarmTime": null, // 上次刷取时间,用于每日判断,格式YYYYMMDD + "dailyLimitCount": 1, // 每日限量次数,在一次性-每日限量 和 每日重置 模式下生效 + "dailyRemainingCount": 1, // 今日剩余可刷取次数 + "fightParam": { // 战斗参数配置 (暂未实装) + "timeout": 240, // 战斗超时时间,单位为秒,默认240秒 (暂未实装) + "strategyName": "根据队伍自动选择" // 战斗策略名,需与AutoFight目录下的策略文件名对应(暂未实装) + } + } ] ``` ## 五、BUG反馈or建议 - +有任何问题或者功能需求,欢迎加群 * QQ群:[1025022262](https://qm.qq.com/q/7i92V2JYF) ## 六、更新日志 +### 2025.11.16 V0.5 +* !!注意!! + 本次版本更新后可能会重置且不支持老版本config.json,强烈建议备份后重新配置 +* 新增两种刷取模式: + * **一次性-每日限量** :与上版本的 **一次性** 类似,但可以在`每日限量次数`限制刷取的次数,刷够次数后会按添加顺序刷取下一配置,次日循环,刷完总数量为止。 + * **每日重置**:每日都会刷取`每日限量次数`指定的次数。无论刷完与否都会在次日重置次数。 +* 新增两个BOSS路线支持:**重拳出击鸭**、**霜夜巡天灵主** ### 2025.08.23 V0.3 @@ -107,7 +109,7 @@ ### 2025.08.21 * fix 截图资源未释放 -* update 地图追踪文件 【从别的大佬那里偷来的】 +* update 地图追踪文件 ### 2025.08.21 V0.1 diff --git a/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/纯水精灵快速前往.json b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/纯水精灵战斗后快速前往.json similarity index 100% rename from repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/纯水精灵快速前往.json rename to repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/纯水精灵战斗后快速前往.json diff --git a/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭前往.json b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭前往.json new file mode 100644 index 000000000..835046dec --- /dev/null +++ b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭前往.json @@ -0,0 +1,44 @@ +{ + "info": { + "authors": [], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1763000407374, + "map_match_method": "", + "map_name": "Teyvat", + "name": "重拳出击鸭", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "action_params": "", + "id": 1, + "move_mode": "walk", + "type": "teleport", + "x": 10018.619140625, + "y": 1538.62646484375 + }, + { + "action": "", + "action_params": "", + "id": 2, + "move_mode": "walk", + "type": "path", + "x": 10041.8623046875, + "y": 1483.87255859375 + }, + { + "action": "combat_script", + "action_params": "keypress(f),wait(3),keypress(f),wait(1),keypress(f),wait(1),keypress(f)", + "id": 3, + "move_mode": "walk", + "type": "target", + "x": 10044.5068359375, + "y": 1473.15869140625 + } + ] +} \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭战斗后快速前往.json b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭战斗后快速前往.json new file mode 100644 index 000000000..f5e642bc7 --- /dev/null +++ b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/重拳出击鸭战斗后快速前往.json @@ -0,0 +1,26 @@ +{ + "info": { + "authors": [], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1763000508226, + "map_match_method": "", + "map_name": "Teyvat", + "name": "重拳出击鸭-打完返回", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "combat_script", + "action_params": "keypress(f),wait(3),keypress(f),wait(1),keypress(f),wait(1),keypress(f)", + "id": 1, + "move_mode": "walk", + "type": "target", + "x": 10044.5068359375, + "y": 1473.15869140625 + } + ] +} \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主前往.json b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主前往.json new file mode 100644 index 000000000..abdf1d169 --- /dev/null +++ b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主前往.json @@ -0,0 +1,44 @@ +{ + "info": { + "authors": [], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1762998844804, + "map_match_method": "", + "map_name": "Teyvat", + "name": "霜夜巡天灵主", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "action_params": "", + "id": 1, + "move_mode": "walk", + "type": "teleport", + "x": 10789.005859375, + "y": 2277.687744140625 + }, + { + "action": "", + "action_params": "", + "id": 2, + "move_mode": "walk", + "type": "path", + "x": 10773.37890625, + "y": 2294 + }, + { + "action": "", + "action_params": "", + "id": 3, + "move_mode": "walk", + "type": "path", + "x": 10761.36328125, + "y": 2319.513427734375 + } + ] +} \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主战斗后快速前往.json b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主战斗后快速前往.json new file mode 100644 index 000000000..48bbb2f2f --- /dev/null +++ b/repo/js/批量讨伐角色养成材料BOSS/assets/Pathing/霜夜巡天灵主战斗后快速前往.json @@ -0,0 +1,26 @@ +{ + "info": { + "authors": [], + "bgi_version": "0.45.0", + "description": "", + "enable_monster_loot_split": false, + "last_modified_time": 1762999379940, + "map_match_method": "", + "map_name": "Teyvat", + "name": "霜夜巡天灵主-打完重新靠近", + "tags": [], + "type": "collect", + "version": "1.0" + }, + "positions": [ + { + "action": "", + "action_params": "", + "id": 1, + "move_mode": "walk", + "type": "path", + "x": 10761.2197265625, + "y": 2319.93359375 + } + ] +} \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/assets/config/example.json b/repo/js/批量讨伐角色养成材料BOSS/assets/config/example.json index 9bbfc36be..819eb9921 100644 --- a/repo/js/批量讨伐角色养成材料BOSS/assets/config/example.json +++ b/repo/js/批量讨伐角色养成材料BOSS/assets/config/example.json @@ -1,274 +1,17 @@ [ - { - "name": "爆炎树", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "掣电树", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "纯水精灵", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "翠翎恐簟", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "风蚀沙虫", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "歌裴莉娅的葬送", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "古岩龙蜥", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "恒常机关阵列", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "急冻树", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "金焰绒翼龙暴君", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "科培琉司的劫罚", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "雷音权现", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "灵觉隐修的迷者", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "秘源机兵·构型械", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "秘源机兵·统御械", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "魔偶剑鬼", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, { "name": "魔像督军", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, + "totalCount": 20, + "remainingCount": 20, "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "千年珍珠骏麟", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "熔岩辉龙像", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "深罪浸礼者", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "深邃摹结株", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "实验性场力发生装置", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "水形幻人", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "贪食匿叶龙山王", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "铁甲熔火帝皇", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "无相之草", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "无相之火", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "无相之雷", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "无相之水", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "无相之岩", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "遗迹巨蛇", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "隐山猊兽", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "兆载永劫龙兽", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false - }, - { - "name": "半永恒统辖矩阵", - "totalCount": 0, - "completedCount": 0, - "remainingCount": 0, - "team": "不切换", - "returnToStatueAfterEachRound": false + "returnToStatueAfterEachRound": true, + "farmMode": "一次性-每日限量", + "lastFarmTime": null, + "dailyLimitCount": 1, + "dailyRemainingCount": 1, + "fightParam": { + "timeout": 240, + "strategyName": "根据队伍自动选择" + } } ] \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/main.js b/repo/js/批量讨伐角色养成材料BOSS/main.js index 171b640e6..4aef7e478 100644 --- a/repo/js/批量讨伐角色养成材料BOSS/main.js +++ b/repo/js/批量讨伐角色养成材料BOSS/main.js @@ -1,25 +1,67 @@ eval(file.readTextSync("reward.js")); +eval(file.readTextSync("utils.js")); (async function () { + const FARM_MODES = { + ONCE: "一次性", + DAILY_LIMIT: "一次性-每日限量", + DAILY: "每日重置" + }; try { /** * 追加Boss配置 */ - function addBoss() { + async function addBoss() { + //总次数 const rounds = parseInt(settings.rounds, 10); if (isNaN(rounds) || rounds < 0) { - console.warn(`⚠️无效的挑战次数: ${settings.rounds},将使用 1 作为默认值。`); + log.warn(`⚠️无效的挑战次数: {rounds},将使用 1 作为默认值。`, settings.rounds); } const totalCount = isNaN(rounds) ? 1 : rounds; + + // 每日刷取上限 + let dailyLimitCount = parseInt(settings.dailyLimitCount, 10); + if (isNaN(dailyLimitCount) || dailyLimitCount < 0) { + log.warn(`⚠️无效的每日上限: {dailyLimitCount},将使用 1 作为默认值。`, settings.dailyLimitCount); + } + dailyLimitCount = isNaN(dailyLimitCount) ? 1 : dailyLimitCount; + + + //战斗超时时间 + let timeout = parseInt(settings.timeout, 10); + if (isNaN(timeout)) { + log.warn(`⚠️无效的超时时间: {timeout},将使用 240 秒作为默认值。`, settings.timeout) + timeout = 240; + } + + // 刷取模式 + if (settings.farmMode === FARM_MODES.ONCE) { + farmMode = FARM_MODES.ONCE + } else if (settings.farmMode === FARM_MODES.DAILY_LIMIT) { + farmMode = FARM_MODES.DAILY_LIMIT + } else if (settings.farmMode === FARM_MODES.DAILY) { + farmMode = FARM_MODES.DAILY + } else { + throw new Error(`无效的farmMode: ${settings.farmMode}`); + } + const newBoss = { name: settings.bossSelection, totalCount: totalCount, - completedCount: 0, remainingCount: totalCount, team: settings.switchPartyName, - returnToStatueAfterEachRound: settings.returnToStatueBeforeStart + returnToStatueAfterEachRound: settings.returnToStatueBeforeStart, + farmMode: farmMode, + lastFarmTime: null, + dailyLimitCount: dailyLimitCount, + dailyRemainingCount: dailyLimitCount, + fightParam: { + timeout: timeout, + strategyName: settings.strategyName, + } }; + config.push(newBoss); - log.info(`✅Boss "${settings.bossSelection}" 已追加。`); + log.info(`✅Boss "{bossSelection}" 已追加。`, settings.bossSelection); } /** @@ -44,20 +86,26 @@ eval(file.readTextSync("reward.js")); * 遍历整个boss讨伐列表,然后按照讨伐次数自动讨伐并领取奖励 * @async * @param {boolean} goToBoss - 是否需要导航到Boss。 - * @param {boolean} isClaimFailed - 是否因为体力不足而中止。 + * @param {boolean} isInsufficientResin - 是否因为体力不足而中止。 * @param {boolean} battleSuccess - 当前一轮讨伐是否成功。 * @param {boolean} returnToStatueAfterEachRound - 是否在每次讨伐后回到七天神像。 */ async function runMain() { - + // --- 打印所有Boss的剩余次数 --- for (let i = 0; i < config.length; i++) { if (i % 10 === 0) { let currentPage = Math.floor(i / 10) + 1; - log.info(`--- 当前boss队列 (第 ${currentPage} 页) ---`); + log.info(`--- 当前boss队列 (第 {currentPage} 页) ---`, currentPage); } - log.info(`🎵${i + 1}.${config[i]["name"]} - 剩余: ${config[i]["remainingCount"]}/${config[i]["totalCount"]}, 队伍: ${config[i]["team"]}`); + log.info(`🎵{i}.{name} - 剩余: {remainingCount}/{totalCount}, 队伍: {team}`, + i + 1, + config[i]["name"], + config[i]["remainingCount"], + config[i]["totalCount"], + config[i]["team"] + ); const isEndOfPage = (i + 1) % 10 === 0; const isLastItem = i === config.length - 1; @@ -71,7 +119,7 @@ eval(file.readTextSync("reward.js")); if (isEndOfPage && !isLastItem) { let currentPage = Math.floor((i + 1) / 10); - log.info(`⌛️当前第 ${currentPage} 页结束,5秒后显示下一页`); + log.info(`⌛️当前第 {currentPage} 页结束,5秒后显示下一页`, currentPage); await sleep(5000); } else if (isLastItem) { log.info(`🔚列表显示完毕,5秒后继续`); @@ -79,61 +127,101 @@ eval(file.readTextSync("reward.js")); } } } + try { - let isClaimFailed = false; + let isInsufficientResin = false; // --- 遍历Boss列表 --- - for (const boss of config) { + for (let boss of config) { let goToBoss = true; const returnToStatueAfterEachRound = boss.returnToStatueAfterEachRound // --- 检查体力是否足够 --- - if (isClaimFailed) { - break; // 如果体力不足,跳出循环 + if (isInsufficientResin) { + log.info(`体力不足,结束刷取BOSS材料`) + break; }; + //刷取模式为 每日限量 or 每日重置 时 + if (boss.farmMode === FARM_MODES.DAILY_LIMIT || boss.farmMode === FARM_MODES.DAILY) { + //如果今天还未刷取,重置今日已刷取次数 + if (!isToday(boss.lastFarmTime)) { + log.info(`今天还未刷取{boss.name},重置今日刷取次数`, boss.name); + boss.dailyRemainingCount = Math.min(boss.dailyLimitCount, boss.remainingCount); + boss.lastFarmTime = getToday(); + } + if (boss.dailyRemainingCount < 1) { + log.info(`今日刷取{name}达到上限,跳过`, boss.name); + continue; + } + } + + // --- 检查当前boss剩余需讨伐次数 --- - if (boss.remainingCount <= 0) { - log.info(`Boss "${boss.name}" 已完成全部${boss.totalCount}次讨伐。跳过`); + if (boss.remainingCount <= 0 && boss.farmMode === FARM_MODES.ONCE) { + log.info(`Boss "{name}" 已完成全部{totalCount}次讨伐。跳过`, boss.name, boss.totalCount); continue; }; // --- 切换队伍 --- if (boss.team !== "不切换") { - log.info(`切换队伍『${boss.team}』`); + log.info(`切换队伍『{team}』`, boss.team); await genshin.switchParty(boss.team); }; + // --- 初始化自定战斗参数 --- + // let fightParam = new AutoFightParam(boss.fightParam.strategyName) + // fightParam.timeout = boss.fightParam.timeout; + + let remainingCount + if (boss.farmMode === FARM_MODES.DAILY_LIMIT || boss.farmMode === FARM_MODES.DAILY) { + remainingCount = boss.dailyRemainingCount + } else if (boss.farmMode === FARM_MODES.ONCE) { + remainingCount = boss.remainingCount + } + // --- 根据剩余次数循环讨伐 --- - for (let round = 1; round <= boss.remainingCount; round++) { + for (let round = 1; round <= remainingCount; round++) { let battleSuccess = false; - if (isClaimFailed) { + if (isInsufficientResin) { break; // --- 体力不足,停止讨伐 --- } - - log.info(`📢当前进度:讨伐『${boss.name}』,第${round}/${boss.remainingCount}次`); - log.info(`使用队伍:${boss.team},每轮回七天神像:${returnToStatueAfterEachRound ? '是' : '否'}`); + log.info(`📢当前进度:讨伐『{boss.name}』,第{round}/{remainingCount}次,今日限次:{dailyLimitCount}`, + boss.name, + round, + remainingCount, + boss.farmMode === FARM_MODES.ONCE ? boss.totalCount : boss.dailyLimitCount + ); + log.info(`使用队伍:{team},每轮回七天神像:{text}`, + boss.team, + returnToStatueAfterEachRound ? '是' : '否' + ); for (let attempt = 1; attempt <= 2; attempt++) { //体力不足和战斗成功后无需重试 - if (isClaimFailed || battleSuccess) { + if (isInsufficientResin || battleSuccess) { break; }; if (goToBoss) { - log.info(`🏃前往『${boss.name}』`); + log.info(`🏃前往『{name}』`,boss.name); await pathingScript.runFile(`assets/Pathing/${boss.name}前往.json`); }; try { - log.info(`⚔️开始第 ${attempt} 次讨伐尝试`); + log.info(`⚔️开始第 {round} 次讨伐的第 {attempt} 尝试`,round,attempt); await dispatcher.runTask(new SoloTask("AutoFight")); + // await dispatcher.runAutoFightTask(fightParam); await autoNavigateToReward(); - isClaimFailed = await takeReward(isClaimFailed); + isInsufficientResin = await takeReward(isInsufficientResin); battleSuccess = true; goToBoss = false; // === 更新 讨伐完成次数 与 剩余讨伐次数 === - if (!isClaimFailed) { - boss.remainingCount--; - boss.completedCount++; + if (!isInsufficientResin) { + if (boss.farmMode === FARM_MODES.DAILY_LIMIT || boss.farmMode === FARM_MODES.ONCE) { + boss.remainingCount--; + } + if (boss.farmMode === FARM_MODES.DAILY_LIMIT || boss.farmMode === FARM_MODES.DAILY) { + boss.dailyRemainingCount--; + } }; break; @@ -146,7 +234,7 @@ eval(file.readTextSync("reward.js")); } if (!battleSuccess) { - log.error(`💀战斗失败次数超过2次,跳过当前BOSS ${boss.name}`); + log.error(`💀战斗失败次数超过2次,跳过当前BOSS ${name}`,boss.name); break; } @@ -157,8 +245,8 @@ eval(file.readTextSync("reward.js")); goToBoss = true; }; - if (!goToBoss && boss.remainingCount > 0 && !isClaimFailed) { - if (["歌裴莉娅的葬送", "科培琉司的劫罚", "纯水精灵"].includes(boss.name)) { + if (!goToBoss && boss.remainingCount > 0 && !isInsufficientResin) { + if (["歌裴莉娅的葬送", "科培琉司的劫罚", "纯水精灵","霜夜巡天灵主","重拳出击鸭"].includes(boss.name)) { await pathingScript.runFile(`assets/Pathing/${boss.name}战斗后快速前往.json`); } else { log.debug("等待5s后BOSS刷新"); @@ -169,34 +257,45 @@ eval(file.readTextSync("reward.js")); } } catch (error) { - log.error(`遍历Boss列表失败,error: ${error}`); + log.error(`遍历Boss列表失败, ${error}`); } finally { + log.info("📢脚本执行完毕,保存配置"); file.writeTextSync("assets/config/config.json", JSON.stringify(config, null, 4)); } - - } - const content = file.readTextSync("assets/config/config.json"); - let config = JSON.parse(content); - - if (!Array.isArray(config)) { + let config; + try { + config = JSON.parse(file.readTextSync("assets/config/config.json")); + } catch (error) { + log.error(`读取配置文件失败: ${error}`); + log.info(`初始化配置`); config = []; } + if (!Array.isArray(config)) { + log.warn("配置文件格式不正确,已重置为空数组"); + config = []; + } + const runMode = settings.runMode; - const modeHandlers = { - "追加指定Boss及相关配置": addBoss, - "删除同名Boss及相关配置": removeBoss, - "!!删除所有BOSS!!": clearAllBosses, - "运行": runMain, + // === 执行对应操作 === + const RUN_MODES = { + ADD_BOSS: "追加指定Boss及相关配置", + REMOVE_BOSS: "删除同名Boss及相关配置", + CLEAR_ALL: "!!删除所有BOSS!!", + RUN: "运行" }; - // === 执行对应操作 === - const handler = modeHandlers[runMode]; - if (handler) { - await handler(); + if (runMode === RUN_MODES.ADD_BOSS) { + addBoss(); + } else if (runMode === RUN_MODES.REMOVE_BOSS) { + removeBoss(); + } else if (runMode === RUN_MODES.CLEAR_ALL) { + clearAllBosses(); + } else if (runMode === RUN_MODES.RUN) { + await runMain(); } else { - log.debug("❓️未知的运行模式:", runMode); + log.error("❓️未知的运行模式:", runMode); } // === 写回配置文件 === diff --git a/repo/js/批量讨伐角色养成材料BOSS/manifest.json b/repo/js/批量讨伐角色养成材料BOSS/manifest.json index 93ecaa4be..195ec313c 100644 --- a/repo/js/批量讨伐角色养成材料BOSS/manifest.json +++ b/repo/js/批量讨伐角色养成材料BOSS/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 1, "name": "批量讨伐角色养成材料BOSS", - "version": "0.4", - "description": "一次性配置多个角色材料的boss,体力不足时保存进度,每次运行时断点续打。\n\n感谢boss地图追踪作者:@柒叶子@Tool_tingsu,\n\n感谢 @柒叶子【首领讨伐一条龙 - 首领连续讨伐】的地脉花寻路的代码实现 \n\n感谢@wjdsg @Tool_tingsu【纪行周常-10个boss一条龙】循环讨伐的代码实现", + "version": "0.5", + "description": "一次性配置多个角色材料的boss,体力不足时保存进度,每次运行时断点续打", "authors": [ { "name": "无激情非少年", @@ -10,5 +10,8 @@ } ], "settings_ui": "settings.json", - "main": "main.js" + "main": "main.js", + "saved_files": [ + "assets/config" + ] } \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/reward.js b/repo/js/批量讨伐角色养成材料BOSS/reward.js index ec9188c3b..3b2014942 100644 --- a/repo/js/批量讨伐角色养成材料BOSS/reward.js +++ b/repo/js/批量讨伐角色养成材料BOSS/reward.js @@ -48,7 +48,13 @@ async function autoNavigateToReward() { await sleep(800); keyUp("w"); } - if (iconRes.x >= 920 && iconRes.x <= 980 && iconRes.y <= 540) { + if (iconRes.isEmpty()) { + log.warn("未找到宝箱图标,重试"); + moveMouseBy(200, 0); + await sleep(500); + continue; + } + else if (iconRes.x >= 920 && iconRes.x <= 980 && iconRes.y <= 540) { advanceNum++; log.info(`视野已调正,前进第${advanceNum}次`); break; @@ -76,7 +82,7 @@ async function autoNavigateToReward() { } } -async function takeReward(isClaimFailed) { +async function takeReward(isInsufficientResin) { try { const mainUiRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/RecognitionObject/mainUi.png")); for (let attempt = 1; attempt <= 100; attempt++) { @@ -88,7 +94,7 @@ async function takeReward(isClaimFailed) { let rewardTextArea = captureRegion.DeriveCrop(1210, 515, 200, 50); let rewardResult = rewardTextArea.find(RecognitionObject.ocrThis); rewardTextArea.dispose(); - if (rewardResult.text === "接触征讨之花" && isClaimFailed == false) { + if (rewardResult.text === "接触征讨之花" && isInsufficientResin == false) { keyPress("F"); await sleep(1000); captureRegion.dispose(); @@ -106,7 +112,7 @@ async function takeReward(isClaimFailed) { await sleep(1000); captureRegion.dispose(); captureRegion = captureGameRegion(); - isClaimFailed = true; + isInsufficientResin = true; } else if (useResult.text.includes("使用")) { log.info("使用脆弱树脂领取奖励"); @@ -133,7 +139,7 @@ async function takeReward(isClaimFailed) { if (inMainUi.x > 0 && !useResult.text.includes("树脂")) { log.debug("回到主界面"); captureRegion.dispose(); - return isClaimFailed; + return isInsufficientResin; } captureRegion.dispose(); rewardTextArea.Dispose(); diff --git a/repo/js/批量讨伐角色养成材料BOSS/settings.json b/repo/js/批量讨伐角色养成材料BOSS/settings.json index 612ab2add..3acb3810f 100644 --- a/repo/js/批量讨伐角色养成材料BOSS/settings.json +++ b/repo/js/批量讨伐角色养成材料BOSS/settings.json @@ -14,7 +14,7 @@ { "name": "bossSelection", "type": "select", - "label": "选择讨伐BOSS(挂机打不过概不负责)", + "label": "选择讨伐BOSS \n - 以拼音首字母按英文字母表排序", "options": [ "半永恒统辖矩阵", "爆炎树", @@ -37,6 +37,7 @@ "千年珍珠骏麟", "熔岩辉龙像", "深罪浸礼者", + "霜夜巡天灵主", "深邃摹结株", "实验性场力发生装置", "水形幻人", @@ -49,7 +50,8 @@ "无相之岩", "遗迹巨蛇", "隐山猊兽", - "兆载永劫龙兽" + "兆载永劫龙兽", + "重拳出击鸭" ] }, { @@ -58,16 +60,45 @@ "label": "要切换的队伍名(默认不切换)", "default": "不切换" }, + { + "name": "timeout", + "type": "input-text", + "label": "战斗超时设置(秒)\n !!!暂时未生效,等下版本BGI更新后更新!!!", + "default": 240 + }, + { + "name": "strategyName", + "type": "input-text", + "label": "战斗策略名\n !!!暂时未生效,等下版本BGI更新后更新!!!\n - BGI本体策略文件夹下的策略名,不含后缀, \n - 如在文件夹内需保留目录结构", + "default": "根据队伍自动选择" + }, { "name": "returnToStatueBeforeStart", "type": "checkbox", "label": "是否开始前回七天神像(默认否)", - "default": false + "default": true + }, + { + "name": "farmMode", + "type": "select", + "label": "刷取模式", + "options":[ + "一次性", + "一次性-每日限量", + "每日重置" + ], + "default": "一次性" }, { "name": "rounds", "type": "input-text", "label": "讨伐次数", "default": "1" + }, + { + "name": "dailyLimitCount", + "type": "input-text", + "label": "每日限量次数 \n - 仅在 【一次性-每日限量】 模式下有效", + "default": "1" } ] \ No newline at end of file diff --git a/repo/js/批量讨伐角色养成材料BOSS/utils.js b/repo/js/批量讨伐角色养成材料BOSS/utils.js new file mode 100644 index 000000000..d2d4d297f --- /dev/null +++ b/repo/js/批量讨伐角色养成材料BOSS/utils.js @@ -0,0 +1,16 @@ +// 获取今天的日期(考虑4点前为昨天)并格式化为YYYYMMDD +function getToday() { + const now = new Date(); + const targetDate = now.getHours() < 4 ? + new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1) : + new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const year = targetDate.getFullYear(); + const month = String(targetDate.getMonth() + 1).padStart(2, '0'); + const day = String(targetDate.getDate()).padStart(2, '0'); + return `${year}${month}${day}`; +} + +// 判断给定日期是否为今天 +function isToday(date) { + return date === getToday(); +}