From f3edc73809874321c02e6e3bb8283fa2fe14eb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=89=E5=90=89=E5=96=B5?= Date: Sat, 18 Oct 2025 21:05:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E2=80=9C=E8=B6=85=E9=87=8F?= =?UTF-8?q?=E9=98=88=E5=80=BC=E2=80=9D=E5=92=8C=E2=80=9C=E8=AF=86=E5=88=AB?= =?UTF-8?q?=E5=90=8D=E5=8D=95=E2=80=9D=E8=BE=93=E5=85=A5=E6=A1=86=EF=BC=9B?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=A4=9A=E5=B1=82=E5=BC=B9=E7=AA=97=E9=80=BB?= =?UTF-8?q?=E8=BE=91=20(#2174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add files via upload * Add files via upload * Add files via upload * 添加日志 * Add files via upload * Add files via upload * Add files via upload --------- Co-authored-by: zaodonganqi <3193156071@qq.com> --- repo/js/背包材料统计/README.md | 50 +- .../assets/imageClick/其他/F/config.json | 10 + .../assets/imageClick/其他/F/icon/F_2.png | Bin 0 -> 461 bytes .../assets/imageClick/其他/滚轮/config.json | 10 + .../assets/imageClick/其他/滚轮/icon/F_2.png | Bin 0 -> 461 bytes .../assets/imageClick/其他/调查/Picture/image6.png | Bin 0 -> 14139 bytes .../assets/imageClick/其他/调查/icon/123.png | Bin 0 -> 698 bytes .../assets/imageClick/复苏/Picture/全员复苏.png | Bin 0 -> 15139 bytes .../assets/imageClick/复苏/icon/确认.png | Bin 0 -> 3810 bytes .../assets/imageClick/对话/config.json | 15 + .../assets/imageClick/对话/icon/NoUI.png | Bin 0 -> 1616 bytes .../assets/imageClick/月卡/config.json | 6 + .../assets/imageClick/登录/Picture/load.png | Bin 0 -> 16327 bytes .../assets/imageClick/登录/config.json | 1 + .../assets/imageClick/登录/icon/setting.png | Bin 0 -> 2337 bytes repo/js/背包材料统计/lib/autoPick.js | 21 +- repo/js/背包材料统计/lib/backStats.js | 5 +- repo/js/背包材料统计/lib/file.js | 16 +- repo/js/背包材料统计/lib/imageClick.js | 494 ++++++++++++++---- repo/js/背包材料统计/main.js | 22 +- repo/js/背包材料统计/manifest.json | 2 +- repo/js/背包材料统计/settings.json | 26 +- 22 files changed, 512 insertions(+), 166 deletions(-) create mode 100644 repo/js/背包材料统计/assets/imageClick/其他/F/config.json create mode 100644 repo/js/背包材料统计/assets/imageClick/其他/F/icon/F_2.png create mode 100644 repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json create mode 100644 repo/js/背包材料统计/assets/imageClick/其他/滚轮/icon/F_2.png create mode 100644 repo/js/背包材料统计/assets/imageClick/其他/调查/Picture/image6.png create mode 100644 repo/js/背包材料统计/assets/imageClick/其他/调查/icon/123.png create mode 100644 repo/js/背包材料统计/assets/imageClick/复苏/Picture/全员复苏.png create mode 100644 repo/js/背包材料统计/assets/imageClick/复苏/icon/确认.png create mode 100644 repo/js/背包材料统计/assets/imageClick/对话/config.json create mode 100644 repo/js/背包材料统计/assets/imageClick/对话/icon/NoUI.png create mode 100644 repo/js/背包材料统计/assets/imageClick/月卡/config.json create mode 100644 repo/js/背包材料统计/assets/imageClick/登录/Picture/load.png create mode 100644 repo/js/背包材料统计/assets/imageClick/登录/config.json create mode 100644 repo/js/背包材料统计/assets/imageClick/登录/icon/setting.png diff --git a/repo/js/背包材料统计/README.md b/repo/js/背包材料统计/README.md index 45963bd74..0df212229 100644 --- a/repo/js/背包材料统计/README.md +++ b/repo/js/背包材料统计/README.md @@ -38,14 +38,15 @@ ### 核心优势 1. **自动CD判断**:无需手动关注材料刷新状态,脚本自动识别CD是否就绪; 2. **灵活路径管理**:支持自定义添加路径,自动排除低效/无效路径; -3. **独立名单识别**:不与路边NPC、神像交互;可自定义识别名单(操作见「四、问题解答」); +3. **独立名单识别**:不与路边NPC、神像交互;可自定义识别名单(操作见「四、问题解答Q4」); 4. **实时弹窗保护**:内置弹窗模块(覆盖路边信件、过期物品、月卡、调查等场景),运行时全程保护路径不被弹窗干扰。 5. **自动黑名单**:内置拾取模块,联动材料统计,可识别爆满的路径材料,自动屏蔽。 ## 二、用前须知 1. 需具备基础电脑操作能力(如文件夹复制、路径查找); -2. 脚本不自带路径文件,需手动对目标文件夹进行操作(步骤见「三、使用方法」)。 +2. 非1080p显示器,使用前需要根据显示器调整背包物品界面的 拖动距离 ,推荐“一次划页稍小于4行材料的距离”; +3. 脚本不自带路径文件,需手动对目标文件夹进行操作(步骤见「三、使用方法」)。 ## 三、使用方法 @@ -110,14 +111,16 @@ | 配置项 | 功能说明 | 操作建议 | |----------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------| | 1. 目标数量 | 仅当背包材料数量**低于此值**时,该材料的路径才会被纳入执行序列 | 这是个统一值,管理路径下全部材料的目标数量 | -| 2. 优先级材料 | 无视“目标数量”限制,直接纳入执行序列顶层(最高优先级) | 填写当前急需材料(例:“虹滴晶”“巡陆艇”) | +| 2. 优先级材料 | 无视“目标数量”限制,直接纳入执行序列顶层(最高优先级) | 填写当前急需材料(例:虹滴晶,巡陆艇) | | 3. 时间成本 | 当一个路径有3-5次运行记录后,自动计算“单材料获取时间”;超过30秒则跳过该路径 | 保持默认30秒即可,无需频繁修改(可过滤低效路径) | | 4. 发送通知 | ① 每类材料跑完通知一次;② 全部材料跑完汇总通知一次(需开启BGI通知) | 建议开启,方便实时了解进度(接收端如企业微信需自行配置) | | 5. 取消扫描 | 取消“每个路径执行后”的背包扫描,仅保留“全部执行前/后”2次扫描 | 有效路径记录达3条以上时可以开启,可节约运行时间 | | 6. 仅 pathing 材料 | 仅扫描 `pathing` 文件夹内的材料,跳过其他分类,大幅缩短扫描时间 | 路径配置完成后开启,提升脚本运行效率 | -| 7. 弹窗名 | 不填则默认循环执行 `assets\imageClick` 文件夹下所有弹窗;填写则仅执行指定弹窗 | 推荐默认,需单独适配某类弹窗时填写(例:仅处理月卡弹窗则填:月卡) | -| 8. 采用的 CD 分类 | 仅执行 `materialsCD` 文件夹内配置的材料路径(支持新增CD分类txt) | 新增材料时,需在该文件夹同步配置CD规则(操作见「四、问题解答4」) | -| 9. 拖动距离 | 解决非1080p分辨率下“划页过头”问题,需调整到“一次划页≤4行” | 拖动点建议选“第五行材料附近”;大于1080p屏可适当减小数值 | +| 7. 弹窗名 | 不填则默认循环执行 `assets\imageClick` 文件夹下所有弹窗;填写则仅执行指定弹窗 | 推荐默认,需单独适配某类弹窗时填写(例:月卡,复苏) | +| 8. 采用的 CD 分类 | 不填则默认执行 `materialsCD` 文件夹内配置的CD分类;填写则仅执行指定CD分类 | 新增材料时,需在该文件夹同步配置CD规则(操作见「四、问题解答Q2」) | +| 9. 采用的识别名单 | 不填则默认执行 `targetText` 文件夹内配置的识别名单;填写则仅执行指定识别名单 | 新增名单时,需符合配置规则(操作见「四、问题解答Q4」) | +| 10. 超量阈值 | 首次扫描后,超量的路径材料,将从识别名单中剔除,默认5000 | 不推荐9999,怪物材料有几千就够了,采用默认数值,可自动避免爆背包 | +| 11. 拖动距离 | 解决非1080p分辨率下“划页过头”问题,需调整到“一次划页≤4行” | 拖动点建议选“第五行材料附近”;大于1080p屏可适当减小数值 | ## 四、注意事项 @@ -134,23 +137,9 @@ ### Q1:如何排除不想要的路径? A:1. 打开 `pathing` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\pathing`); 2. 直接删除/移走目标材料/怪物的路径文件夹; - 3. **注意**:不要将路径文件放入 `targetText` 或 `materialsCD` 文件夹(① 这两个文件夹默认全部读取,增加负担;② 也不安全,会被更新覆盖)。 + **其他方法**:看「四、问题解答Q2」,按格式要求填入对应的材料名(或者从其他CD文件中复制过来),在「JS 自定义设置」【采用的 CD 分类】中输入新建的文件名,即可实现只加载该CD文件里材料的路径。 -### Q2:如何自定义识别名单? -A:1. 打开 `targetText` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\targetText`); - 2. 新建/编辑txt文件,按格式填写:`自定义名称:目标1,目标2`(英文冒号+英文逗号,例:“新材料:霜盏花,便携轴承,”); - 3. 若需排除怪物掉落材料:找到“掉落.txt”,删除对应材料名即可; - 4. 操作参考截图: -
- 自定义识别名单操作截图1 - 自定义识别名单操作截图2 -
- -### Q3:如何识别不规范命名的路径文件夹(如“纳塔食材一条龙”“果园.json”)? -A:将不规范的文件夹/文件,放入**适配的材料文件夹**中即可(路径CD由“所在材料文件夹”决定)。 - 例:“果园.json”放入“苹果”文件夹,将按“苹果”的CD规则执行。 - -### Q4:如何添加新材料? +### Q2:如何添加新材料? A:1. 打开 `materialsCD` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\materialsCD`); 2. 新建/编辑txt文件,按格式填写:`CD规则:材料1,材料2`(中文冒号+中文逗号,CD规则参考自带文件,例:“1次0点:月落银,宿影花,”),`材料1`或`材料2`将会作为标准名; 3. **关键要求**:路径文件夹名、材料图片名必须与“材料1或2”完全一致(多层文件夹默认读取最外层标准名文件夹); @@ -160,6 +149,20 @@ A:1. 打开 `materialsCD` 文件夹(脚本路径:`BetterGI\User\JsScript\ 添加新材料操作截图2 +### Q3:如何识别不规范命名的路径文件夹(如“纳塔食材一条龙”“果园.json”)? +A:将不规范的文件夹/文件,放入**适配的材料文件夹**中即可(路径CD由“所在材料文件夹”决定)。 + 例:“果园.json”放入“苹果”文件夹,将按“苹果”的CD规则执行。 + +### Q4:如何自定义识别名单? +A:1. 打开 `targetText` 文件夹(脚本路径:`BetterGI\User\JsScript\背包材料统计\targetText`); + 2. 新建/编辑txt文件,按格式填写:`自定义名称:目标1,目标2`(英文冒号+英文逗号,例:“新材料:霜盏花,便携轴承,”); + 3. 若需排除怪物掉落材料:找到“掉落.txt”,删除对应材料名即可; + 4. 操作参考截图: +
+ 自定义识别名单操作截图1 + 自定义识别名单操作截图2 +
+ ### Q5:如何取消路径执行后扫描背包? A:在「JS自定义设置」中勾选“取消扫描”(依旧会保留“全部材料执行始/末”的2次扫描)。 @@ -209,4 +212,5 @@ A:记录文件夹位于 `BetterGI\User\JsScript\背包材料统计\` 下,各 | v2.41 | 修复“勾选分类的本地记录”bug;新增“仅背包统计”选项;补充记录损坏处理说明 | | v2.42 | 新增“无路径间扫描”“noRecord模式”(适合成熟路径);新增怪物材料CD文件 | | v2.50 | 新增独立名单拾取、弹窗模块;支持怪物名识别 | -| v2.51 | 自定义设置新增“拖动距离/拖动点”;新增月卡弹窗识别;路径材料达9999自动上黑名单;修复怪物0收获记录 | \ No newline at end of file +| v2.51 | 自定义设置新增“拖动距离/拖动点”;新增月卡弹窗识别;路径材料达9999自动上黑名单;修复怪物0收获记录 | +| v2.52 | 自定义设置新增“超量阈值”和“识别名单”输入框;新增多层弹窗逻辑 | \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/其他/F/config.json b/repo/js/背包材料统计/assets/imageClick/其他/F/config.json new file mode 100644 index 000000000..4a23daa02 --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/其他/F/config.json @@ -0,0 +1,10 @@ +{ + "isSpecial": true, + "operationType": "key_press", + "keyCode": "VK_F", + "xOffset": 0, + "yOffset": 0, + "detectRegion": { "x": 1207, "y": 783, "width": 42, "height": 42 }, + "nextLevelOnSuccess": "", + "nextLevelOnFailure": "" +} \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/其他/F/icon/F_2.png b/repo/js/背包材料统计/assets/imageClick/其他/F/icon/F_2.png new file mode 100644 index 0000000000000000000000000000000000000000..cef22ceb2b338cdd95b9d9dce776d9a43e2f8b37 GIT binary patch literal 461 zcmV;;0W$uHP)dd^7jlnM5Q>qA5^P2vOwvlnPz_4FE_J zQyj;f&u5}2GAk-2uIo}NrSH!OV7uKi91aP?kaD@4>OUL~Y&IME{eG(7s0F_7vs^B5 zU6*>jPP5sh(P+Hfp%4Pww%P4=#BrQj+vo%Uf*_z$sc^g95JGUhURkYHRIAlbzQ^Oi zdc9`9-{%V;rNnU@lu`ghQADfN!f~7@Z?D(ma=BnxR(=3K)hn=9DtrtJ2guX{Aq19X zF`v(0_BjXweBVdP^zxZAP^;CL&1O$YKb=lYr&B!7`&|H~6r<7TX$%28&ojEPG6QI> zQA#nHOpMe1l?o5x8z2)vk|fx+O&*_fnE=8tq}%NpC;dD9XbQB}-0yde$0Liy!sxNd z6`U6om@^pXsrwVFZi2N@CJAT{40PDge!3tq>wS(00000NkvXXu0mjf D9NWm+ literal 0 HcmV?d00001 diff --git a/repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json new file mode 100644 index 000000000..2326821af --- /dev/null +++ b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/config.json @@ -0,0 +1,10 @@ +{ + "isSpecial": true, + "operationType": "key_mouse_script", + "scriptPath": "assets/滚轮下翻.json", + "xOffset": 0, + "yOffset": 0, + "detectRegion": { "x": 1206, "y": 175, "width": 44, "height": 735 }, + "nextLevelOnSuccess": "assets\\imageClick\\其他\\F", + "nextLevelOnFailure": "" +} \ No newline at end of file diff --git a/repo/js/背包材料统计/assets/imageClick/其他/滚轮/icon/F_2.png b/repo/js/背包材料统计/assets/imageClick/其他/滚轮/icon/F_2.png new file mode 100644 index 0000000000000000000000000000000000000000..cef22ceb2b338cdd95b9d9dce776d9a43e2f8b37 GIT binary patch literal 461 zcmV;;0W$uHP)dd^7jlnM5Q>qA5^P2vOwvlnPz_4FE_J zQyj;f&u5}2GAk-2uIo}NrSH!OV7uKi91aP?kaD@4>OUL~Y&IME{eG(7s0F_7vs^B5 zU6*>jPP5sh(P+Hfp%4Pww%P4=#BrQj+vo%Uf*_z$sc^g95JGUhURkYHRIAlbzQ^Oi zdc9`9-{%V;rNnU@lu`ghQADfN!f~7@Z?D(ma=BnxR(=3K)hn=9DtrtJ2guX{Aq19X zF`v(0_BjXweBVdP^zxZAP^;CL&1O$YKb=lYr&B!7`&|H~6r<7TX$%28&ojEPG6QI> zQA#nHOpMe1l?o5x8z2)vk|fx+O&*_fnE=8tq}%NpC;dD9XbQB}-0yde$0Liy!sxNd z6`U6om@^pXsrwVFZi2N@CJAT{40PDge!3tq>wS(00000NkvXXu0mjf D9NWm+ literal 0 HcmV?d00001 diff --git a/repo/js/背包材料统计/assets/imageClick/其他/调查/Picture/image6.png b/repo/js/背包材料统计/assets/imageClick/其他/调查/Picture/image6.png new file mode 100644 index 0000000000000000000000000000000000000000..1f6c9c4bd0be8ae5ce87146cf592a737119142de GIT binary patch literal 14139 zcmeHN`8Skb{C{jU$dYYD_Glv_K9($zj3Odiku75z4Oz!JNSm@GX$(Us$)K_?!^m1Q zh$8#Gq`}z7GWZIe^Znub4}8y=d(Ly7``mfYeeQkD`}LapexLUfntFwD!_Jmk@0|0yd--|BsEW028uo$}P=olNjzTU(Ck}J%?)gtiuy88e0?D(po4+=zrc$P;t%x=b z>ueu(mkxfen~h z*4JPHx^oix!9aJ$@f;BFoDM*p(mO$~RSDRcg_}szAGR0ufdISIj*4t~hl{Mq0CdWr6p;IvOazn

-(vY5xE_sA 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 Vw%q)#0{d&JuY3J+zP4S&{{TO;yF35@ literal 0 HcmV?d00001 diff --git a/repo/js/背包材料统计/assets/imageClick/其他/调查/icon/123.png b/repo/js/背包材料统计/assets/imageClick/其他/调查/icon/123.png new file mode 100644 index 0000000000000000000000000000000000000000..f4534713312101c5aa0fc3d48ca6931e60ad63db GIT binary patch literal 698 zcmV;r0!96aP)^Q5eU6XU@#*r6slt5?B-lMUk{nNz$DP!6G6- zgM={OgdTbdB-(Usk1 zb^BiC;hXvY&iVe|neT{+LTVbZNQkG;UNipr3&-mA zu(PTX!_-kq<~zn4p$ z-Hd&lKuU@4&+gx%0ly;}Hc8uQp7gz-wf!2e-i#0lg-QniFn=Ay;SWhpw{>v$VIQV3 z)7E(b05k#7a0uH@alWGqk<(cPx+ow(%SuQMP>}l{nfDHS-)AaiBTb!_<2B^^Jg*(N zu1CgGtSB#|rRgBMwn8!=}UMTg}Pl!$cz?lniipm8vp5D8V0BF%yj zLJ<(8*FZ!{AQ3{SH-T^N+&|!(xxeJhWOgTc-?O`CpL5Rc$@ARANQd?K`Qrcpu0Bql>AG&lgHf{hoZs@M1Wn%KcC(tM0fsZer zu9g;`ufLD0JH!P5LPv7VLab6rXVoT2E+;KChgK^yA<=+cDkWG zC!Oei+c6{1O);QUnDw3 z^-8n3#t+}OiGO;_;kng$k+!o=b5`;eN%BrSR@Z_#xm%F;@k~PWaZ#t2nCu?2e}z0s zPS&w9u+n-byd?OMqn^o8c+zmOBGPgrBxz>b&C zd7cYDSHx9wP6}aAc%(lQ(SGOZZy9cc+sl(Leax+{Y+QkH!aF$9mC`NJBhqp2@0bUe zs}Dfmv)m~$KUrdD@vuD| z604p61YB^(spiMI1pc!>WL(y)Gza}H7#77nUm7~zt0FHM1RvSv4wecxFw+Wn?}~DT zyXMaOb_h4wqn%P?xA2^eA;LjVQe~5;oE_;<)U;e0F(p1Rvy^ ztC1_7yRPJ=L<(ZASE#>TA6q{f)bm%Z-)RxLz`WkE&bBb#tF!L)uV5YY7e9#a<-K-Q zj!7Z;97OuM(`sk3(;v^5?zO$EkEh?A!aS&le}7QWt6OlXR%GR=+pK&jNX-@HbHNcM zH{mkosdC@ZU1s#n_cyKBCef}m&n(ya-jDr(ot^FCW2hk%dMQzHAjcDVziGH(bSEM9 z?X8?vy*P!qwv3Ss80JWZVFn5H(n8GwYO!wd8>KsFvnamkHE0Pj;kL{^k*$=C`Sh+d zyYzc-w%6pDnKMbEeWL4T-XqdWJ4?rxvX+i6D{kE15Z>_G=;7(Sz`--99H~WQRJN$hDo10zf#{<-M?Ot z{W*KAblB_Hc&BRX!;)=7$!u=4?Kf5Yjen~os~Nw}+1#S+qOZhcoRnR#oweO|<3-~S zIU>lxt6uG+-|tN|V!|Di)Kp!zx)&JKeyGvsh2=eMDr^#%RBaNB7>qzf9NR%|D{oDiPHY-}n9g`Z2Q6C@g1+#H-h^$hzBODLx0q~#Qtvbtw{6#IR(=5md(tC(0SD;7HM z)xn+mw1b2dBnJ>jIU%tIy#*gl%S_nL8c4kp3%$p6FSHii2)_GEcG7k4FIkY~(OFny za)n84#fPEVQ0uiRf#zGE&kDwwfkGsuZB6T)L)};V9%3F~sxo#>W~@xj2_DcfLZWF* z-`lrZVvj84(u>p381J%wck7%u@25MP<2^D5?pZ`$0j1lcd{OVpHY>Di=VvU9><3`$ zP_L1Z3B8(*DT8*e>a z_wHMHG@f+ck-NTmK4QF9C&aiFnH2su{Mg1V{Gvyo4`i&oxw(qj%TyuZB3Q|HE<4I+ zBzrk4q}uoH6}GQ8=Du8-EQE#6n*4dy{cY@9*CZmcW}Rsf3>E8BY7Rn46nN#UzG}q( zi7&|IvE$Kcs@adgOV2t5waF)0BJ*=EtFDo;k5XrgoQsgTM(V1+5Mxl1!4AUH_O@-K zn^q4}9Td1?P-9O*6DM#mqrx@%rK)ABIuX9R)8O)ZK8r3S%(A6QS>Qjz zMde<6f8>@&%Rs>I&EJx?dC*4uk0tIJZ;|l}wZir5o535)ppfG$24l8iNulLCO(VO1 zk8Pd&8~Jgs(xt^~YSW>`q34_RNjw|#Q{-OSe*e?yKAldT0YPoSgrrH$%R3r-CdA$?r?#S>#w0ZyY;Q-3^q9CU+?OXyIc@SeflUnoAWRql zqGAAmyiaKt03hfp0Q_|T0Hv1z0P;z(Z`B5Xt0uZPZ`=tTSsJ&Ge;Z=kw$>9c-_so& z!r~QO78|TD_U~l0$JSF1Ulw_d;+u@AcIReeMY!a-USFH|lMWKGfpb0Mnq|F~mCt^| zjQvQ~3zKXMldY%p6653E8^-RLD=SN#WT@X2hd$?fvnSSM%PnO<0o}4asW9AMMLaP) zqMu8UGGD6f>|c|mKkP=a(Nkth6%A%OuESpbV}Npmql$?pKs{JfOUs#D>*B-hXN?F@xACb3qSx3wUxaP31? z)x$|u)!?0lhHcv#oUuAV`hc1>=5OfVUyUBfG=!34eDN?sx zg1&^$CDd)}SY)Q83x8mw=-9kXU3W$4yrAMW_`Y zNt%Pty~W;w$WxbCAxPysw0s)QxrD)>r5d0eQoCbmD4`^^9N6Y*w7Pn7jG{%q9JcifJ&c8QKkh z5$)lR2{N0264SaWhs+N6IaF{BC2}VXiz5mR`qN^3va0q61g&|=Uz?L)Gy3Qb2y*T% zoXnCoMus7!wKq`l7nGz4nfu8(N=j9*>Gp(jW4ryoqTEDxKUT`*qIkM52~lCi?c^Nq ztf24xnIG-%{|8KnJ$6~`8gi)Y3SShJ)M-E{n44~l8sBh?T{!R$ujCjRP-N! z@)MWS8W`tUZcv4|po$nHQJ}Lk(^L5|EhkK}4*yDs8(ku4B-!X(S#!XvI6wkx;lfif znAC8lv(Th^w#vqxDuOBfs<>h!QUwa}OMF}Hj4yNcUX!AXqQP;n>Z3%m3Q=0-M>{Ou zd22G)E#BE(p=X7&6-0StD)w;yUZ%v0D#9GsFi2apd8PX@* z!2TL`RzMtR6*vI3b5;sjoABpVYi@gqq@Fj-)_-1@ajFYig?}V^%{J9pSTRfh`ge1t z_a|i-;9wL5L}Bak2`ROg&VqH2NdMI*t&5A?xX3p|m=JgNfk$m8=qgZgB)IuUR#&^qM?$e8vSa;Nin33x z|L7N?A{{nfy}iGADdnoj21u6;6&rim2Qw_GPRD-#AjrFy-P+XX6B*z?RgLzs&rKvW-)HOx0#Zy*xyD$s3|O z#G#wdv09O;grRBCkg=2t*OP6CPq0~wDV=%c^=1(jeqHmj=y7{PHK-3I2Bi)RJKmC} zXaLmz6U7wtC5O^s+h77N?;QE})58Cagu+tISJ9?b5lT>k6=A9Xt!l5ovhPaRgu|=N z*ZvR6l3L}2K`4=;9+7>*I(Bi=NR2q>JUIccPH)}DDWvqf6;Zt4f+C``@YeN>Avem< zQ6Ld2*zUQ;!xvPlgIz?=eSqVPE}hKN(@r5sbdEMcW zYdlX*QZy`q4?N8!Vy=vYt&?@Ds;pc;6&eqxJ~YZUku|y9vdwy!A5`eiQvfQ{ysg{} zZwx14wRrbFpi0YJgM#K7!s(muIK@LC-t)t5#&hHNde!FrXcBHlUsC39o>@P*3Ms=; z#iT_rpSoQXp)rmRGCjo-|IPmdR_&LDqYsiYU*uvq?CmLv{?qjTp>8^8Qc*?~115)q z7Kmb|qdrZMtNau=9f%0yf9P#3kAeSC#$U3x9R5aAM)9lvs?DBL47KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(`>RI+y?e7jKeZ#YO-C z1P@6>K~#9!td?7ERAm%~pY`p%=Rjv_JLNEKrG=s#Du{%GMw%iQC=k48e}HXboO%P7B}zGKT0fu zW0v8{zJ2?aep}0&&z3apEpq%2jg8=eLI{8X0SL2v{|o1?^21GyIC#!u?`sv`(y)OK z+U4Y{LR`tw)x+&RCLR!4O~j=!&A}Z+t2zD}%}tVs@%2?jd8GaBBbug?oNkkot%6J6 zea>IzdKbMTWTw?xl5uTFOY>$<9O6g|4VyXLCdGgY*VJ`bsa z>3XH89e?QA%Xr4Rj#_KW8*5dy!qeeNJ#&%SjJlP&e>>ajC9qF7k2h`ZwG8-ok?NBQwrfTlD%H&YBz)e?7J2iIZe76{2qaifRWYW8*%=f`E-F zHq{U=d6)rYQI(n`C=UzV8)Mbja13)rhQ>j0VkDvqUN3R&yk@Fa6?xKdQN=Mf?&W_ z?xC#(-kyH)M^(3MQgwNhlDpHI*0x7SFPLs!W4B3tWdJG>;Q!TEMuaQ4*v~? z=P;h-b{}^JWONMiS!Wh(U3^IY5FOX~{5!t9!f!nq^KhLf*h9HKi>mPD5P9iSfQX zZ}0gn^XAUX<--`euS?V`jWk#H%=zE{IcNUoh^3iP%?*}7+~|071v*588{Zg$L@Wk? zrxZaLmf4yg1W*crVHy}x0-yn{qq__Ox>JfzPqGzmuX{djFgXDOp%vUC0{YOu*`EWTwvt#b;3}SFO(SUZRhCW z&(goUjfM0Im5zfn#JfSIlw{ZQ{QSfz{_*N1OaX~R41s2TImi8<{}}fUf1I98yLTrHQs7}!#zkcR4 zr(gOPNym<=Rfnv*E}yz%h_4+UrY2d*!tyH5pZO1#xxB8ln$s&I2;=O z1l380k}C1NADv+I+BB}~A%sL~Sj`pr@GU(&_Q*lnTedTD)?e*B9+68avtWtCN#A8J~g>v3W> z`J&6YS4MCgYx81DYkBtdck=k72We?)K=}azL5N1mw<(zr*tWsgwK>org%H@5xv4N^ z)eL9^Do{A_7?x?FwIm2&b*;!yUpwD^1=BTL6lRsqI&34Zv~KQJ+qM){gOyIXkp$Y<#8Xu=Od zeG^s53PK&C(OZfXYE8nndGPS3NhTf6oPUE%wt!_CWO6S1`aAf}u|qWPXkd9Y zi|+?$LW-_pWhKkP!V-Z7%QVm$ilq`&l@;`K?jluJT^8;#?G3f2zP5%tK03t3|6F5b zHOJa|k^KYRJn@bDXh_vEx46uFdWE7}LJ;*)*HcJCVi^)o`S?M=&c+m-?K`QebfO0Q zexOnc=F&_2{^VJ%j?a_LyX+tA;=9N1qqe4s$+<;l=F()=b6AE%Xc_s_8sGCNg&{k( zrD$ntptdF%iNkx$CFP((GCjA<$uqBE5VCKui^q@MPfJrHnQR`@Ffk2-WTk`9f>48P znp9N8N!3(QpQ<4qv(TYN-(boqYvMu(imuCV|MViGS10)Tm+z*ltqD&l1RxCqDMX|i z2=vBKq_su|L}34?_CY}*QVq*8ky4;MpTOVh_;3H=HM-n!@D89Gfc_81C8cCi$os4S O0000EU0N}4?zSt6u^Gg80E+fzL=S@v-`UUz0 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 F{}1Rtq$U6W literal 0 HcmV?d00001 diff --git a/repo/js/背包材料统计/lib/autoPick.js b/repo/js/背包材料统计/lib/autoPick.js index e0038dec1..941db67c4 100644 --- a/repo/js/背包材料统计/lib/autoPick.js +++ b/repo/js/背包材料统计/lib/autoPick.js @@ -31,18 +31,31 @@ function readtargetTextCategories(targetTextDir) { const targetTextFilePaths = readAllFilePaths(targetTextDir, 0, 1); const materialCategories = {}; + // 解析筛选名单 + const pickTextNames = (settings.PickCategories || "") + .split(/[,,、 \s]+/).map(n => n.trim()).filter(n => n); + + // 【新增:兜底日志】确认pickTextNames是否为空,方便排查 + log.info(`筛选名单状态:${pickTextNames.length === 0 ? '未指定(空),将加载所有文件' : '指定了:' + pickTextNames.join(',')}`); + for (const filePath of targetTextFilePaths) { if (state.cancelRequested) break; const content = file.readTextSync(filePath); if (!content) { log.error(`加载文件失败:${filePath}`); - continue; // 跳过当前文件 + continue; } const sourceCategory = basename(filePath).replace('.txt', ''); // 去掉文件扩展名 + // 【核心筛选:空名单直接跳过判断,加载所有】 + if (pickTextNames.length === 0) { + // 空名单时,直接保留当前文件,不跳过 + } else if (!pickTextNames.includes(sourceCategory)) { + // 非空名单且不在列表里,才跳过 + continue; + } materialCategories[sourceCategory] = parseCategoryContent(content); } - // log.info(`完成材料分类信息读取,分类信息:${JSON.stringify(materialCategories, null, 2)}`); return materialCategories; } // 定义替换映射表 @@ -171,7 +184,7 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text log.info("检测中..."); lastLogTime = currentTime; } - await sleep(50); // 关键50时可避免F多目标滚动中拾取错,背包js这边有弹窗模块,就没必要增加延迟降低效率了 + await sleep(50); cachedFrame?.dispose(); cachedFrame = captureGameRegion(); @@ -192,7 +205,6 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text for (let targetText of targetTexts) { let targetResult = ocrResults.find(res => res.text.includes(targetText)); if (targetResult) { - // log.info(`找到目标文本: ${targetText}`); // 生成唯一标识并更新识别计数(文本+Y坐标) const materialId = `${targetText}-${targetResult.y}`; @@ -203,7 +215,6 @@ async function alignAndInteractTarget(targetTexts, fDialogueRo, textxRange, text // log.info(`目标文本 '${targetText}' 和 F 图标水平对齐`); if (recognitionCount.get(materialId) >= 1) { keyPress("F"); // 执行交互操作 - // log.info(`F键执行成功,识别计数: ${recognitionCount.get(materialId)}`); log.info(`交互或拾取: ${targetText}`); // F键后清除计数,确保单次交互 diff --git a/repo/js/背包材料统计/lib/backStats.js b/repo/js/背包材料统计/lib/backStats.js index e75e40183..76320e18c 100644 --- a/repo/js/背包材料统计/lib/backStats.js +++ b/repo/js/背包材料统计/lib/backStats.js @@ -4,6 +4,7 @@ const holdX = Math.min(1920, Math.max(0, Math.floor(Number(settings.HoldX) || 10 const holdY = Math.min(1080, Math.max(0, Math.floor(Number(settings.HoldY) || 750))); const totalPageDistance = Math.max(10, Math.floor(Number(settings.PageScrollDistance) || 711)); const targetCount = Math.min(9999, Math.max(0, Math.floor(Number(settings.TargetCount) || 5000))); // 设定的目标数量 +const exceedCount = Math.min(9999, Math.max(0, Math.floor(Number(settings.ExceedCount) || 5000))); // 设定的超量目标数量 const imageDelay = Math.min(1000, Math.max(0, Math.floor(Number(settings.ImageDelay) || 0))); // 识图基准时长 await sleep(imageDelay); // 配置参数 @@ -383,7 +384,7 @@ async function scanMaterials(materialsCategory, materialCategoryMap) { width: 66 + 2 * tolerance, height: 22 + 2 * tolerance }; - const ocrResult = await recognizeText(ocrRegion, 1000, 10, 5, 2, ra); + const ocrResult = await recognizeText(ocrRegion, 200, 10, 5, 2, ra); materialInfo.push({ name, count: ocrResult || "?" }); if (!hasFoundFirstMaterial) { @@ -516,7 +517,7 @@ var excessMaterialNames = []; // 超量材料名单 function filterLowCountMaterials(pathingMaterialCounts, materialCategoryMap) { // 新增:超量阈值(普通材料9999,矿石处理后也是9999) - const EXCESS_THRESHOLD = 9999; + const EXCESS_THRESHOLD = exceedCount; // 新增:临时存储本次超量材料 const tempExcess = []; diff --git a/repo/js/背包材料统计/lib/file.js b/repo/js/背包材料统计/lib/file.js index a7d83cca6..a5d3d5666 100644 --- a/repo/js/背包材料统计/lib/file.js +++ b/repo/js/背包材料统计/lib/file.js @@ -1,16 +1,20 @@ +// ======================== 全局工具函数(只定义1次,所有函数共用)======================== +// 1. 路径标准化函数(统一处理,消除重复) +function normalizePath(path) { + if (!path || typeof path !== 'string') return ''; + let standardPath = path.replace(/\\/g, '/').replace(/\/+/g, '/'); + return standardPath.endsWith('/') ? standardPath.slice(0, -1) : standardPath; +} - -// ============================================== -// 5. 角色识别与策略执行相关函数(保留原始功能) -// ============================================== -// 工具函数 +// 2. 提取路径最后一级名称 function basename(filePath) { if (!filePath || typeof filePath !== 'string') return ''; - const normalizedPath = filePath.replace(/\\/g, '/'); + const normalizedPath = normalizePath(filePath); const lastSlashIndex = normalizedPath.lastIndexOf('/'); return lastSlashIndex !== -1 ? normalizedPath.substring(lastSlashIndex + 1) : normalizedPath; } + /* // 如果路径存在且返回的是数组,则认为是目录Folder function pathExists(path) { diff --git a/repo/js/背包材料统计/lib/imageClick.js b/repo/js/背包材料统计/lib/imageClick.js index b82271560..1eeecf484 100644 --- a/repo/js/背包材料统计/lib/imageClick.js +++ b/repo/js/背包材料统计/lib/imageClick.js @@ -1,195 +1,457 @@ -// 新增:独立的预加载函数,负责所有资源预处理 +// ======================== 1. 预加载函数(精简日志版)======================== async function preloadImageResources(specificNames) { - log.info("开始预加载所有图片资源"); + // log.info("开始预加载所有图片资源"); + + function hasIconFolder(dirPath) { + try { + const entries = readAllFilePaths(dirPath, 0, 0, [], true); + return entries.some(entry => normalizePath(entry).endsWith('/icon')); + } catch (e) { + log.error(`检查目录【${dirPath}】是否有icon文件夹失败:${e.message}`); + return false; + } + } - // 统一参数格式(与原逻辑一致) let preSpecificNames = specificNames; - if (typeof specificNames === 'string') { - preSpecificNames = [specificNames]; - } + if (typeof specificNames === 'string') preSpecificNames = [specificNames]; const isAll = !preSpecificNames || preSpecificNames.length === 0; - if (isAll) { - log.info("未指定具体弹窗名称,将执行所有弹窗目录处理"); - } else { - log.info(`指定处理弹窗名称:${preSpecificNames.join(', ')}`); - } + // if (isAll) log.info("未指定具体弹窗名称,将执行所有含icon文件夹的弹窗目录处理"); + // else log.info(`指定处理弹窗名称:${preSpecificNames.join(', ')}(仅含icon文件夹的目录)`); - // 定义根目录(与原代码一致) const rootDir = "assets/imageClick"; + const rootDirNormalized = normalizePath(rootDir); + const subDirs = readAllFilePaths(rootDir, 0, 2, [], true); - // 获取所有子目录(与原代码一致) - const subDirs = readAllFilePaths(rootDir, 0, 0, [], true); - - // 筛选目标目录(与原代码一致) - const targetDirs = isAll - ? subDirs - : subDirs.filter(subDir => { - const dirName = basename(subDir); - return preSpecificNames.includes(dirName); - }); + const targetDirs = subDirs.filter(subDir => { + const dirName = basename(subDir); + const hasIcon = hasIconFolder(subDir); + const matchName = isAll ? true : preSpecificNames.includes(dirName); + return hasIcon && matchName; + }); if (targetDirs.length === 0) { - log.info(`未找到与指定名称匹配的目录,名称列表:${preSpecificNames?.join(', ') || '所有'}`); + // log.info("未找到符合条件的弹窗目录"); return []; } - // 预加载所有目录的资源(原imageClick内的资源加载逻辑) const preloadedResources = []; for (const subDir of targetDirs) { const dirName = basename(subDir); - // log.info(`开始预处理弹窗类型:${dirName}`); + const fullPath = normalizePath(subDir); + const pathSegments = fullPath.slice(rootDirNormalized.length + 1).split('/'); + const level = pathSegments.length; + const isFirstLevel = level === 1; - // 查找icon和Picture文件夹(与原代码一致) - const entries = readAllFilePaths(subDir, 0, 1, [], true); - const iconDir = entries.find(entry => entry.endsWith('\icon')); - const pictureDir = entries.find(entry => entry.endsWith('\Picture')); + let popupConfig = { + isSpecial: false, + operationType: "click", + ocrConfig: null, + xOffset: 0, + yOffset: 0, + detectRegion: null, + nextLevelOnSuccess: "", + nextLevelOnFailure: "", + loopCount: 1, + loopDelay: 0 + }; + const configPath = normalizePath(`${subDir}/config.json`); + let isSpecialModule = false; + let specialDetectRegion = null; - if (!iconDir) { - log.warn(`未找到 icon 文件夹,跳过分类文件夹:${subDir}`); - continue; - } - if (!pictureDir) { - log.warn(`未找到 Picture 文件夹,跳过分类文件夹:${subDir}`); - continue; + if (fileExists(configPath)) { + try { + const configContent = file.readTextSync(configPath); + popupConfig = { ...popupConfig, ...JSON.parse(configContent) }; + isSpecialModule = popupConfig.isSpecial === true + && typeof popupConfig.detectRegion === 'object' + && popupConfig.detectRegion !== null + && popupConfig.detectRegion.x != null + && popupConfig.detectRegion.y != null + && popupConfig.detectRegion.width != null + && popupConfig.detectRegion.height != null + && popupConfig.detectRegion.width > 0 + && popupConfig.detectRegion.height > 0; + specialDetectRegion = isSpecialModule ? popupConfig.detectRegion : null; + // log.info(`【${dirName}】加载配置成功:${isFirstLevel ? '第一级' : '第二级'} | 模块类型:${isSpecialModule ? '特殊模块' : '普通模块'}`); + } catch (e) { + log.error(`【${dirName}】解析配置失败,使用默认配置:${e.message}`); + isSpecialModule = false; + } } - // 读取图片文件(与原代码一致) - const iconFilePaths = readAllFilePaths(iconDir, 0, 0, ['.png', '.jpg', '.jpeg']); - const pictureFilePaths = readAllFilePaths(pictureDir, 0, 0, ['.png', '.jpg', '.jpeg']); - - // 预创建icon识别对象(与原代码一致) - const iconRecognitionObjects = []; - for (const filePath of iconFilePaths) { - const mat = file.readImageMatSync(filePath); - if (mat.empty()) { - log.error(`加载图标失败:${filePath}`); + if (isSpecialModule) { + const entries = readAllFilePaths(subDir, 0, 1, [], true); + const iconDir = entries.find(entry => normalizePath(entry).endsWith('/icon')); + const iconFilePaths = readAllFilePaths(iconDir, 0, 0, ['.png', '.jpg', '.jpeg']); + + if (iconFilePaths.length === 0) { + log.warn(`【${dirName}】特殊模块无有效icon文件,跳过`); continue; } - const recognitionObject = RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080); - iconRecognitionObjects.push({ name: basename(filePath), ro: recognitionObject, iconDir }); - } - // 预创建图库区域(与原代码一致) - const pictureRegions = []; - for (const filePath of pictureFilePaths) { - const mat = file.readImageMatSync(filePath); - if (mat.empty()) { - log.error(`加载图库图片失败:${filePath}`); + const iconRecognitionObjects = []; + for (const filePath of iconFilePaths) { + const mat = file.readImageMatSync(filePath); + if (mat.empty()) { + log.error(`【${dirName}】特殊模块加载图标失败:${filePath}`); + continue; + } + iconRecognitionObjects.push({ + name: basename(filePath), + ro: RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080), + iconDir, + mat: mat + }); + } + + const targetIcon = iconRecognitionObjects[0]; + const manualRegion = new ImageRegion(targetIcon.mat, specialDetectRegion.x, specialDetectRegion.y); + manualRegion.width = specialDetectRegion.width; + manualRegion.height = specialDetectRegion.height; + + const foundRegions = [{ + pictureName: "特殊模块", + iconName: targetIcon.name, + region: manualRegion, + iconDir: iconDir + }]; + // log.info(`【${dirName}】特殊模块生成识别区域:x=${manualRegion.x}, y=${manualRegion.y}, 宽=${manualRegion.width}, 高=${manualRegion.height}`); + + preloadedResources.push({ + dirName, + fullPath, + foundRegions, + popupConfig, + isFirstLevel: isFirstLevel, + level: level + }); + + } else { + const entries = readAllFilePaths(subDir, 0, 1, [], true); + const iconDir = entries.find(entry => normalizePath(entry).endsWith('/icon')); + const pictureDir = entries.find(entry => normalizePath(entry).endsWith('/Picture')); + + if (!pictureDir) { + log.warn(`【${dirName}】普通模块无Picture文件夹,跳过`); continue; } - pictureRegions.push({ name: basename(filePath), region: new ImageRegion(mat, 0, 0) }); - } - // 预计算匹配区域(与原代码一致) - const foundRegions = []; - for (const picture of pictureRegions) { - for (const icon of iconRecognitionObjects) { - const foundRegion = picture.region.find(icon.ro); - if (foundRegion.isExist()) { - foundRegions.push({ - pictureName: picture.name, - iconName: icon.name, - region: foundRegion, - iconDir: icon.iconDir - }); + const iconFilePaths = readAllFilePaths(iconDir, 0, 0, ['.png', '.jpg', '.jpeg']); + const pictureFilePaths = readAllFilePaths(pictureDir, 0, 0, ['.png', '.jpg', '.jpeg']); + + // 仅在资源为空时警告 + if (iconFilePaths.length === 0) { + log.warn(`【${dirName}】普通模块无有效icon文件,跳过`); + continue; + } + if (pictureFilePaths.length === 0) { + log.warn(`【${dirName}】普通模块无有效Picture文件,跳过`); + continue; + } + + const iconRecognitionObjects = []; + for (const filePath of iconFilePaths) { + const mat = file.readImageMatSync(filePath); + if (mat.empty()) { + log.error(`【${dirName}】加载图标失败:${filePath}`); + continue; + } + iconRecognitionObjects.push({ + name: basename(filePath), + ro: RecognitionObject.TemplateMatch(mat, 0, 0, 1920, 1080), + iconDir + }); + } + + const pictureRegions = []; + for (const filePath of pictureFilePaths) { + const mat = file.readImageMatSync(filePath); + if (mat.empty()) { + log.error(`【${dirName}】加载图库图片失败:${filePath}`); + continue; + } + pictureRegions.push({ + name: basename(filePath), + region: new ImageRegion(mat, 0, 0) + }); + } + + const foundRegions = []; + for (const picture of pictureRegions) { + for (const icon of iconRecognitionObjects) { + const foundRegion = picture.region.find(icon.ro); + if (foundRegion.isExist()) { + foundRegions.push({ + pictureName: picture.name, + iconName: icon.name, + region: foundRegion, + iconDir: icon.iconDir + }); + } } } - } + if (foundRegions.length === 0) { + log.warn(`【${dirName}】普通模块无匹配图标,跳过`); + continue; + } - // 保存预处理结果 - preloadedResources.push({ - dirName, - foundRegions - }); + preloadedResources.push({ + dirName, + fullPath, + foundRegions, + popupConfig, + isFirstLevel: isFirstLevel, + level: level + }); + } } - log.info(`预加载完成,共处理 ${preloadedResources.length} 个目录`); + // log.info(`预加载完成,共${preloadedResources.length}个有效弹窗目录(第一级:${preloadedResources.filter(r => r.isFirstLevel).length}个,第二级:${preloadedResources.filter(r => !r.isFirstLevel).length}个)`); return preloadedResources; } -// 新增:imageClick后台任务函数 +// ======================== 2. 后台任务函数(精简日志版)======================== async function imageClickBackgroundTask() { log.info("imageClick后台任务已启动"); - const imageClickDelay = Math.min(99, Math.max(1, Math.floor(Number(settings.PopupClickDelay) || 5)))*1000; - // 可以根据需要配置要处理的弹窗名称 + + // 配置参数 + const taskDelay = Math.min(999, Math.max(1, Math.floor(Number(settings.PopupClickDelay) || 15)))*1000; const specificNamesStr = settings.PopupNames || ""; const specificNames = specificNamesStr .split(/[,,、 \s]+/) .map(name => name.trim()) .filter(name => name !== ""); - // 调用独立预加载函数(循环前仅执行一次) + // 预加载资源 const preloadedResources = await preloadImageResources(specificNames); if (preloadedResources.length === 0) { - log.info("无可用预加载资源,任务结束"); + log.info("无可用弹窗资源,任务结束"); return { success: false }; } - // 循环执行,仅使用预加载资源 + // 筛选一级弹窗 + const firstLevelDirs = preloadedResources.filter(res => res.isFirstLevel); + if (firstLevelDirs.length === 0) { + log.warn("无第一级弹窗目录,任务终止"); + return { success: false }; + } + + // 打印资源检测结果 + log.info("\n==================== 现有弹窗加载结果 ===================="); + log.info("1. 一级弹窗(共" + firstLevelDirs.length + "个):"); + firstLevelDirs.forEach((res, idx) => log.info(` ${idx+1}. 【${res.dirName}】`)); + const secondLevelResources = preloadedResources.filter(res => !res.isFirstLevel); + log.info("\n2. 二级弹窗(共" + secondLevelResources.length + "个):"); + secondLevelResources.forEach((res, idx) => log.info(` ${idx+1}. 【${res.dirName}】`)); + log.info("=============================================================\n"); + + // 核心逻辑:外循环遍历所有一级弹窗 while (!state.completed && !state.cancelRequested) { - try { - // 调用imageClick时传入预加载资源 - await imageClick(preloadedResources, null, specificNames, true); - } catch (error) { - log.info(`弹窗识别失败(继续重试):${error.message}`); + // log.info(`\n===== 外循环开始:遍历所有一级弹窗(共${firstLevelDirs.length}个) =====`); + + // 遍历所有一级弹窗 + for (const currentFirstLevel of firstLevelDirs) { + // 检查当前一级弹窗是否被触发 + const levelResult = await imageClick([currentFirstLevel], null, [currentFirstLevel.dirName], true); + + if (levelResult.success) { + log.info(`【${currentFirstLevel.dirName}】触发成功,进入内部流程...`); + const levelStack = [currentFirstLevel]; + + // 内循环处理内部流程 + while (levelStack.length > 0 && !state.completed && !state.cancelRequested) { + const currentResource = levelStack[levelStack.length - 1]; + const innerResult = await imageClick([currentResource], null, [currentResource.dirName], true); + + if (innerResult.success) { + const nextPath = normalizePath(currentResource.popupConfig.nextLevelOnSuccess); + if (nextPath && nextPath.trim()) { + const nextResource = preloadedResources.find(res => res.fullPath === nextPath); + if (nextResource) { + levelStack.push(nextResource); + } else { + // log.warn(`内循环:下一级(${nextPath})不存在,回退`); + levelStack.pop(); + } + } else { + levelStack.pop(); + } + } else { + const nextPath = normalizePath(currentResource.popupConfig.nextLevelOnFailure); + if (nextPath && nextPath.trim()) { + const nextResource = preloadedResources.find(res => res.fullPath === nextPath); + if (nextResource) { + levelStack.push(nextResource); + } else { + // log.warn(`内循环:下一级(${nextPath})不存在,回退`); + levelStack.pop(); + } + } else { + levelStack.pop(); + } + } + + await sleep(100); + } + + log.info(`【${currentFirstLevel.dirName}】内部流程处理完毕`); + } } - // 短暂等待后再次执行 - await sleep(imageClickDelay); + + // log.info(`===== 外循环结束:等待${taskDelay/1000}秒后开始下一次循环 =====`); + await sleep(taskDelay); } - log.info("imageClick后台任务已结束"); + log.info("imageClick后台任务结束"); return { success: true }; } -// 优化:使用预加载资源,保留所有原执行逻辑 +// ======================== 3. 识别与操作函数(精简日志版)======================== async function imageClick(preloadedResources, ra = null, specificNames = null, useNewScreenshot = false) { - // 保留原参数格式处理(兼容历史调用) if (typeof specificNames === 'string') { specificNames = [specificNames]; } const isAll = !specificNames || specificNames.length === 0; + let isAnySuccess = false; - // 遍历预处理好的资源(原targetDirs循环逻辑) for (const resource of preloadedResources) { - const { dirName, foundRegions } = resource; + const { dirName, foundRegions, popupConfig } = resource; + const { xOffset, yOffset } = popupConfig; + let hasAnyIconDetected = false; // 标记是否有图标被识别 + for (const foundRegion of foundRegions) { - // 保留原识别对象创建逻辑(使用预处理的路径) const tolerance = 1; - const iconMat = file.readImageMatSync(`${foundRegion.iconDir}/${foundRegion.iconName}`); + const iconMat = file.readImageMatSync(`${normalizePath(foundRegion.iconDir)}/${foundRegion.iconName}`); + + const { detectRegion } = popupConfig; + const defaultX = foundRegion.region.x - tolerance; + const defaultY = foundRegion.region.y - tolerance; + const defaultWidth = foundRegion.region.width + 2 * tolerance; + const defaultHeight = foundRegion.region.height + 2 * tolerance; const recognitionObject = RecognitionObject.TemplateMatch( iconMat, - foundRegion.region.x - tolerance, - foundRegion.region.y - tolerance, - foundRegion.region.width + 2 * tolerance, - foundRegion.region.height + 2 * tolerance + detectRegion?.x ?? defaultX, + detectRegion?.y ?? defaultY, + detectRegion?.width ?? defaultWidth, + detectRegion?.height ?? defaultHeight ); recognitionObject.threshold = 0.85; - // 保留原识别逻辑 const result = await recognizeImage( recognitionObject, ra, - 1000, // timeout - 500, // interval + 1000, + 500, useNewScreenshot, - dirName // iconType + dirName ); - // 保留原点击逻辑 if (result.isDetected && result.x !== 0 && result.y !== 0) { - const x = Math.round(result.x + result.width / 2); - const y = Math.round(result.y + result.height / 2); - log.info(`即将点击【${dirName}】类型下的图标:${foundRegion.iconName},位置: (${x}, ${y})`); - await click(x, y); - log.info(`点击【${dirName}】类型下的${foundRegion.iconName}成功`); - await sleep(10); - return { success: true }; - } else { - // log.info(`未发现弹窗【${dirName}】的图标:${foundRegion.iconName}`); + hasAnyIconDetected = true; + isAnySuccess = true; + const centerX = Math.round(result.x + result.width / 2); + const centerY = Math.round(result.y + result.height / 2); + const actualX = centerX + xOffset; + const actualY = centerY + yOffset; + log.info(`识别到【${dirName}】弹窗,偏移后位置(${actualX}, ${actualY})`); + + if (!popupConfig.isSpecial) { + // log.info(`点击【${dirName}】弹窗:(${actualX}, ${actualY})`); + // 新增:普通点击加循环(默认1次,0间隔,与原逻辑一致) + const clickCount = popupConfig.loopCount; + const clickDelay = popupConfig.loopDelay; + for (let i = 0; i < clickCount; i++) { + await click(actualX, actualY); // 保留原始点击逻辑 + if (i < clickCount - 1) await sleep(clickDelay); // 非最后一次加间隔 + } + } else { + switch (popupConfig.operationType) { + case "key_press": { + const targetKey = popupConfig.keyCode || "VK_SPACE"; + // 新增:key_press用循环(默认3次,1000ms间隔,与原硬编码逻辑一致) + const pressCount = popupConfig.loopCount || 3; + const pressDelay = popupConfig.loopDelay || 1000; + for (let i = 0; i < pressCount; i++) { + keyPress(targetKey); // 保留原始按键逻辑 + if (i < pressCount - 1) await sleep(pressDelay); // 非最后一次加间隔 + } + log.info(`【${dirName}】弹窗触发按键【${targetKey}】,共${pressCount}次,间隔${pressDelay}ms`); + break; + } + case "ocr_click": { + isAnySuccess = false; + const { targetTexts, xRange, yRange, timeout = 2000 } = popupConfig.ocrConfig || {}; + if (!targetTexts || !xRange || !yRange) { + log.error(`【${dirName}】弹窗OCR配置不全,跳过`); + break; + } + const ocrResults = await performOcr(targetTexts, xRange, yRange, timeout, ra); + if (ocrResults.length > 0) { + const ocrActualX = Math.round(ocrResults[0].x + ocrResults[0].width/2) + xOffset; + const ocrActualY = Math.round(ocrResults[0].y + ocrResults[0].height/2) + yOffset; + // 新增:OCR点击加循环(默认1次,0间隔,与原逻辑一致) + const ocrCount = popupConfig.loopCount; + const ocrDelay = popupConfig.loopDelay; + for (let i = 0; i < ocrCount; i++) { + await click(ocrActualX, ocrActualY); // 保留原始OCR点击逻辑 + if (i < ocrCount - 1) await sleep(ocrDelay); // 非最后一次加间隔 + } + log.info(`【${dirName}】弹窗OCR点击“${ocrResults[0].text}”:(${ocrActualX}, ${ocrActualY}),共${ocrCount}次,间隔${ocrDelay}ms`); + isAnySuccess = true; + } else { + log.warn(`【${dirName}】弹窗OCR未识别到文本`); + } + break; + } + case "key_mouse_script": { + try { + const scriptPath = normalizePath(popupConfig.scriptPath || ""); + if (!scriptPath) { + log.error(`【${dirName}】弹窗未配置键鼠脚本路径,跳过执行`); + isAnySuccess = false; + break; + } + if (!fileExists(scriptPath)) { + log.error(`【${dirName}】弹窗键鼠脚本不存在:${scriptPath}`); + isAnySuccess = false; + break; + } + // 新增:键鼠脚本加循环(默认1次,0间隔,与原逻辑一致) + const scriptCount = popupConfig.loopCount; + const scriptDelay = popupConfig.loopDelay; + for (let i = 0; i < scriptCount; i++) { + await keyMouseScript.runFile(scriptPath); // 保留原始脚本执行逻辑 + if (i < scriptCount - 1) await sleep(scriptDelay); // 非最后一次加间隔 + } + log.info(`【${dirName}】弹窗键鼠脚本执行完成,共${scriptCount}次,间隔${scriptDelay}ms`); + isAnySuccess = true; + } catch (error) { + log.error(`【${dirName}】弹窗键鼠脚本执行失败:${error.message}`); + isAnySuccess = false; + } + break; + } + default: + log.error(`【${dirName}】弹窗未知操作类型:${popupConfig.operationType},默认执行点击`); + // 新增:默认操作加循环(默认1次,0间隔,与原逻辑一致) + const defaultCount = popupConfig.loopCount; + const defaultDelay = popupConfig.loopDelay; + for (let i = 0; i < defaultCount; i++) { + await click(actualX, actualY); // 保留原始默认点击逻辑 + if (i < defaultCount - 1) await sleep(defaultDelay); // 非最后一次加间隔 + } + isAnySuccess = true; + } + } } } + + // 仅在该弹窗无任何图标识别时输出警告 + if (!hasAnyIconDetected && !isAnySuccess) { + // log.warn(`【${dirName}】弹窗未发现任何有效图标`); + } + await sleep(10); } - // 所有目标处理完毕仍未成功(保留原返回逻辑) - return { success: false }; + return { success: isAnySuccess, message: isAnySuccess ? "弹窗识别操作成功" : "未识别到弹窗" }; } diff --git a/repo/js/背包材料统计/main.js b/repo/js/背包材料统计/main.js index a96f2eb4d..c2b189502 100644 --- a/repo/js/背包材料统计/main.js +++ b/repo/js/背包材料统计/main.js @@ -111,7 +111,6 @@ function parseMonsterMaterials() { } materialToMonsters[mat].add(monsterName); }); - log.debug(`${CONSTANTS.LOG_MODULES.MONSTER}解析怪物材料:${monsterName} -> [${materials.join(', ')}]`); } }); } catch (error) { @@ -155,7 +154,11 @@ function getSelectedMaterialCategories() { return acc; }, {}); - const finalSettings = { ...initialSettings, ...settings }; + const finalSettings = Object.keys(initialSettings).reduce((acc, key) => { + // 若settings中有该键则使用其值,否则用默认的false(确保只处理material_mapping中的键) + acc[key] = settings.hasOwnProperty(key) ? settings[key] : initialSettings[key]; + return acc; + }, {}); return Object.keys(finalSettings) .filter(key => key !== "unselected") @@ -294,6 +297,7 @@ function readMaterialCD() { const materialCDCategories = {}; for (const filePath of materialFilePaths) { + if (state.cancelRequested) break; const content = file.readTextSync(filePath); if (!content) { log.error(`${CONSTANTS.LOG_MODULES.CD}加载文件失败:${filePath}`); @@ -1156,7 +1160,6 @@ function classifyNormalPathFiles(pathingDir, targetResourceNames, lowCountMateri if (pathEntries.length > 0) { log.info(`${CONSTANTS.LOG_MODULES.PATH}\n===== 匹配到的材料路径列表 =====`); pathEntries.forEach((entry, index) => { - log.info(`${index + 1}. 材料:${entry.resourceName},路径:${entry.path}`); }); log.info(`=================================\n`); } else { @@ -1355,6 +1358,11 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个 =========================================\n\n`; writeContentToFile(summaryPath, content); log.info(`${CONSTANTS.LOG_MODULES.RECORD}最终汇总已记录至 ${summaryPath}`); + // ============================================== + // 新增:汇总后强制触发结束指令,确保程序终止 + // ============================================== + state.completed = true; // 标记任务完全完成 + state.cancelRequested = true; // 终止所有后台任务(如图像点击、OCR) } // ============================================== @@ -1387,11 +1395,15 @@ ${Object.entries(totalDifferences).map(([name, diff]) => ` ${name}: +${diff}个 } // 关键补充:等待超量名单生成(由filterLowCountMaterials更新) let waitTimes = 0; - while (excessMaterialNames.length === 0 && waitTimes < 300) { + while (excessMaterialNames.length === 0 && !state.cancelRequested && waitTimes < 100) { await sleep(1000); // 每1秒查一次 waitTimes++; } - + // 若收到终止信号,直接退出OCR任务(不再执行后续逻辑) + if (state.cancelRequested) { + log.info(`${CONSTANTS.LOG_MODULES.MAIN}OCR任务收到终止信号,已退出`); + return; + } // 现在过滤才有效(确保excessMaterialNames已生成) allTargetTexts = allTargetTexts.filter(name => !excessMaterialNames.includes(name)); log.info(`OCR最终目标文本(已过滤超量):${allTargetTexts.join('、')}`); diff --git a/repo/js/背包材料统计/manifest.json b/repo/js/背包材料统计/manifest.json index eeca672ea..ece08cf61 100644 --- a/repo/js/背包材料统计/manifest.json +++ b/repo/js/背包材料统计/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "背包统计采集系统", - "version": "2.52", + "version": "2.53", "bgi_version": "0.44.8", "description": "可统计背包养成道具、部分食物、素材的数量;根据设定数量、根据材料刷新CD执行挖矿、采集、刷怪等的路径。优势:\n+ 1. 自动判断材料CD,不需要管材料CD有没有好;\n+ 2. 可以随意添加路径,能自动排除低效、无效路径;\n+ 3. 有独立名单识别,不会交互路边的npc或是神像;可自定义识别名单,具体方法看【问题解答】增减识别名单\n+ 4. 有实时的弹窗模块,提供了常见的几种:路边信件、过期物品、月卡、调查;\n+ 5. 可识别爆满的路径材料,自动屏蔽;更多详细内容查看readme.md", "saved_files": [ diff --git a/repo/js/背包材料统计/settings.json b/repo/js/背包材料统计/settings.json index 10b129e01..a1dca168c 100644 --- a/repo/js/背包材料统计/settings.json +++ b/repo/js/背包材料统计/settings.json @@ -102,12 +102,27 @@ { "name": "PopupClickDelay", "type": "input-text", - "label": "如 过期物品,信件,自定义文件夹名。注意分隔符和文件夹格式\n----------------------------------\n弹窗循环间隔(默认:5 秒)" + "label": "如 过期物品,信件,自定义文件夹名。注意文件夹结构\n----------------------------------\n弹窗循环间隔(默认:15 秒)" }, { "name": "CDCategories", "type": "input-text", - "label": "----------------------------------\n\n采用的CD分类(默认:全部)" + "label": "====================\n\n采用的CD分类(默认:全部) 举例:采集,怪物,木材" + }, + { + "name": "PickCategories", + "type": "input-text", + "label": "根据CD分类来加载路径文件,具体看materialsCD目录\n====================\n\n采用的识别名单(默认:全部) 举例:交互,采集,宝箱" + }, + { + "name": "ExceedCount", + "type": "input-text", + "label": "根据拾取分类来加载OCR名单,具体看targetText目录\n----------------------------------\n\n超量阈值(默认:5000)超量的路径材料将不拾取" + }, + { + "name": "PageScrollDistance", + "type": "input-text", + "label": "====================\n拖动距离:(默认711像素点)" }, { "name": "HoldX", @@ -118,10 +133,5 @@ "name": "HoldY", "type": "input-text", "label": "------------------------\n翻页拖动点Y坐标:0~1080(默认750)" - }, - { - "name": "PageScrollDistance", - "type": "input-text", - "label": "------------------------\n拖动距离:(默认711像素点)" } -] \ No newline at end of file +]