From d6254f06b78c5b958f354dedcb0f710aa327a7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=91=E7=AB=AF=E5=AE=A2?= <107686912+Kirito520Asuna@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:33:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E4=BD=93=E5=8A=9B=E8=AE=A1?= =?UTF-8?q?=E5=88=92=200.0.3=20=E4=BF=AE=E5=A4=8D=EF=BC=9A=E5=81=A5?= =?UTF-8?q?=E5=85=A8=E4=BD=93=E5=8A=9B=E8=AF=86=E5=88=AB(=E5=8F=8C?= =?UTF-8?q?=E9=87=8D=E8=AF=86=E5=88=AB=E5=85=9C=E5=BA=95)=20(#2987)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/js/AutoPlan/README.md | 16 +- repo/js/AutoPlan/assets/original_resin.png | Bin 0 -> 2235 bytes repo/js/AutoPlan/config/config.js | 3 + repo/js/AutoPlan/main.js | 40 ++- repo/js/AutoPlan/manifest.json | 2 +- repo/js/AutoPlan/md/check.jpg | Bin 0 -> 29872 bytes repo/js/AutoPlan/settings.json | 5 + repo/js/AutoPlan/utils/bgi_tools.js | 2 +- repo/js/AutoPlan/utils/physical.js | 366 +++++++++++++++++++-- repo/js/AutoPlan/utils/tool.js | 58 ++++ 10 files changed, 440 insertions(+), 52 deletions(-) create mode 100644 repo/js/AutoPlan/assets/original_resin.png create mode 100644 repo/js/AutoPlan/md/check.jpg diff --git a/repo/js/AutoPlan/README.md b/repo/js/AutoPlan/README.md index d112f4d1b..31a8d2ba8 100644 --- a/repo/js/AutoPlan/README.md +++ b/repo/js/AutoPlan/README.md @@ -12,6 +12,8 @@ - 限制只在特定星期几执行(例如周日限定本、周本) - 设置执行优先级(数字越大越先跑) - 支持三种配置来源(输入 / UID专属 / bgi_tools远程) +- 自动检测幽境开启状态 + ![check](md/check.jpg) ## 配置项说明 @@ -293,18 +295,20 @@ 欢迎提交 issue 或 PR 改进脚本~ 祝刷本愉快! -## 版本密钥 +## 版本对应表 -| 版本 | 密钥 | -|-------|---------------------| -| 0.0.1 | oiJbmjU2R0NniiwiZxh | -| 0.0.2 | oiJbmjU2R0NniiwiZxh | +| 版本 | 密钥 | bettergi-scripts-tools对应版本 | +|:-----:|:-------------------:|:--------------------------:| +| 0.0.1 | oiJbmjU2R0NniiwiZxh | 0.0.4+ | +| 0.0.2 | oiJbmjU2R0NniiwiZxh | 0.0.5+ | +| 0.0.3 | oiJbmjU2R0NniiwiZxh | 0.0.6+ | ## 版本历史(简要) +### 0.0.3 2026.03.13 +- 健全体力识别(双重识别兜底) ### 0.0.2 2026.03.07 - 新增幽境支持 ### 0.0.1 2026.01.30 - - 基本功能完成 - 支持三种配置加载方式 - 支持 bgi_tools http 拉取/推送配置 diff --git a/repo/js/AutoPlan/assets/original_resin.png b/repo/js/AutoPlan/assets/original_resin.png new file mode 100644 index 0000000000000000000000000000000000000000..7aea4d586f4c82233a8f7eec0fec617026ced687 GIT binary patch literal 2235 zcmV;s2t@aZP)eh4bY)!^&fNe202g#cSael$WpW@x zZ*6U5Zgc=sV{&C>ZgXgFboX@)M*si_ElET{R9JtxG9|Bq6D#6#)P@Bj7zN4`B*|!Z`~V zx@iOu84&rBq$Ej1tG3fRZTGSs-4w;PElpJck%(Mhv|M_3`(rusVN6jpO#>odO5c}6 zbT_(dw;&=S8iv7{s_Fqi1_Ua{QaRsfG#w#}F)$T~d z6X8HAs>FllJAXQH{^PeD$A46^;!9eg-p4$ENXN(`dsc5t{`}jW0gX4CG8y5&_`_6_ zClL@49-!jh!`RW}hFdJlw%Y7O=TNF=cQ`qW?qWn=AZCAd&D-7|YBTV}sim1~pWgiM zyR~Xjh)2!^Wiw0r26tJzezN~7$z8o>N5nYs-qovD=Zx78FUi+VoehrV8kOAX?0-&F zYp&~wN2RX($LoYK>>1ea1YiFC;Z!VYxI$jdRy?k?cgBh{y+F99&{y?3DtZmjbnXsInwn>)GEzjn;N&7p}=4Sn#-UA1P$DhHp5-}G);T@ zV4q3X^Myyl{n73)>$IWdfG5S&g5ZKO3DA>$>_H9ralk}G0(_U^DoYs9ZG-2@iX(G* ziWs=Pr;Y2D5sW>3aIIEz>P}_Z(E*YsxQ_Ra)8j`*l!Xd;J|sf=a5`iqw=njkhi15H zKJbL!n7PJ09jU@t^UTGkl(GxNntro+f&q8D@iNC*F7PMn{Thx*u1Upi;Cxo+V}>ergzi3nV$ z(#&5k78`|PxhlLjUOO<*ZC{(C>kGn@lyqgoXPOySbdzCGKEX)FL(z`AU;xjp1PpVj z9%P(brunZ6x00DyrrS=%xu!wB=AXJWH$7kX1s12T2)VvbAmT#~h-<(AfJkn6s=u#0 z5jP`Yz2ULZbJJG3-F1E6_ZZ55UYPSdw>ELMx-?CafT)!v_=sT44DMR}%@9?UTZXRa zW}qz`>)PpH(6dz_A}M3BlP&w4!6)C@+MP~Cj@6ZXp>q7YuRQFJ34{ezPelaqKK@QN!K4(@)DWe!#9UT05xIudaSUw$^x8D@$j>Sp(63v_&c z-(N@P-y1Fb>eb$_?dbj9w~p4ENZ@jiqz0q}TL)bM0;mh=qK=4n0tYqv8_YZRWvj(bso@_00)JG#(@Bu zt~hgX=JZD=^UI5OV!Uagk&>2fP0U<9KhPOg6%NR%4p}N6zg4Z2>mv#N%8+X7tlWg` z5;R=dN)QAf0wR(Gjt>nF3N^?sh;x^w7G|@n%j~8nZkbk?AJf|Uw+y6$x}42RYPfT` znH_y89|F~ecPrm?9eH%bd~vIZh;gFByl;|2T3Htoi&=ubt5d#%M9Ake`ceRo>7b?!;{Vit%XVuGpwA`n1! z3F@90&o%z?$KOniU2LwMzRAp`nz( zIb@e1+6J9rK!U0ROAVMUfg@pZa{AA|du!s#Xw$iK%fAgyBs%XiAGZQlTA0X=Uz)FI z6*Y;PqA;vEhyb1sM6ghT`KpXsFjs|_%T=x{t6oqTL8f)+E-}BP8EnS&RUnJh1iiQ;i1fXkFb3hW*s$E` zUcQ)L%=tcf8;!%bb^AUd5|TV2iD item.trim()); diff --git a/repo/js/AutoPlan/main.js b/repo/js/AutoPlan/main.js index d2069aeda..05a634b09 100644 --- a/repo/js/AutoPlan/main.js +++ b/repo/js/AutoPlan/main.js @@ -2,7 +2,7 @@ import {config, initConfig, initSettings, LoadType} from './config/config'; import {ocrUid} from './utils/uid'; import {getDayOfWeek, outDomainUI, outStygianOnslaughtUI, throwError,toMainUi} from './utils/tool'; import {pullJsonConfig, pushAllCountryConfig, pushAllJsonConfig} from './utils/bgi_tools'; -import {ocrPhysical} from "./utils/physical"; +import {countOriginalResin, ocrPhysical,countAllResin} from "./utils/physical"; import {findStygianOnslaught} from "./utils/activity"; /** @@ -54,9 +54,13 @@ async function autoDomain(autoFight) { // fragileResinUseCount: number; await sleep(1000) //流程->返回主页 打开地图 返回主页 - const physicalOcr = await ocrPhysical(true, true) - config.user.physical.current = physicalOcr.current - config.user.physical.min = physicalOcr.min + // const physicalOcr = await ocrPhysical(true, true) + // config.user.physical.current = physicalOcr.current + // config.user.physical.min = physicalOcr.min + + const currentPhysical = await countAllResin() + config.user.physical.current = currentPhysical.originalResinCount; + const physical = config.user.physical if (domainParam.specifyResinUse && physical.current < physical.min && noOriginalSum <= 0 && originalSum > 0) { throwError(`体力不足,当前体力${physical.current},最低体力${physical.min},请手动补充体力后重试`) @@ -104,7 +108,9 @@ async function autoDomain(autoFight) { if (errorMessage.includes("复活") && domainParam.DomainName) { continue; } - throw e; + if (!config.run.exclude_run_exception||config.run.loop_plan) {//排除异常 与循环计划互斥 + throw e; + } } } } finally { @@ -172,7 +178,9 @@ async function autoLeyLineOutcrop(autoLeyLineOutcrop) { if (errorMessage.includes("复活")) { continue; } - throw e; + if (!config.run.exclude_run_exception||config.run.loop_plan) {//排除异常 与循环计划互斥 + throw e; + } } } } @@ -246,9 +254,12 @@ async function autoStygianOnslaught(autoStygianOnslaught) { // fragileResinUseCount: number; await sleep(1000) //流程->返回主页 打开地图 返回主页 - const physicalOcr = await ocrPhysical(true, true) - config.user.physical.current = physicalOcr.current - config.user.physical.min = physicalOcr.min + // const physicalOcr = await ocrPhysical(true, true) + // config.user.physical.current = physicalOcr.current + // config.user.physical.min = physicalOcr.min + const currentPhysical = await countAllResin() + config.user.physical.current = currentPhysical.originalResinCount; + const physical = config.user.physical if (physical.current < physical.min && noOriginalSum <= 0 && originalSum > 0) { throwError(`体力不足,当前体力${physical.current},最低体力${physical.min},请手动补充体力后重试`) @@ -278,7 +289,9 @@ async function autoStygianOnslaught(autoStygianOnslaught) { if (errorMessage.includes("复活")) { continue; } - throw e; + if (!config.run.exclude_run_exception||config.run.loop_plan) {//排除异常 与循环计划互斥 + throw e; + } } } } finally { @@ -676,7 +689,7 @@ async function main() { if (isStygianOnslaught) { //圣遗物秘境名称 const holyRelicDomainNames = config.domainList.filter(item => !item.hasOrder).map(item => item.name); - const filter = list.find(item => item.runType === config.user.runTypes[0] && holyRelicDomainNames.includes(item.autoFight.domainName)); + const filter = list.find(item => item.runType === config.user.runTypes[0] && holyRelicDomainNames.includes(item.autoFight?.domainName)); if (filter) { // 幽境危战添加秘境顺序前 list.forEach(item => { @@ -702,8 +715,9 @@ async function main() { await autoRunList(list); if (config.run.loop_plan) { // 重新获取当前体力值 - const physicalOcr = await ocrPhysical(true, true); - config.user.physical.current = physicalOcr.current; + // const physicalOcr = await ocrPhysical(true, true); + const currentPhysical = await countAllResin() + config.user.physical.current = currentPhysical.originalResinCount; //循环 if (config.user.physical.current < config.user.physical.min) { //体力耗尽 diff --git a/repo/js/AutoPlan/manifest.json b/repo/js/AutoPlan/manifest.json index feb9d4c92..c8077be74 100644 --- a/repo/js/AutoPlan/manifest.json +++ b/repo/js/AutoPlan/manifest.json @@ -1,6 +1,6 @@ { "name": "自动体力计划", - "version": "0.0.2", + "version": "0.0.3", "description": "", "settings_ui": "settings.json", "main": "main.js", diff --git a/repo/js/AutoPlan/md/check.jpg b/repo/js/AutoPlan/md/check.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63ed991fafcb2dc60b7d6ed8c515ee99e8f8501c GIT binary patch literal 29872 zcmcG#1yoyG*Dkz6Z~_!9S}Z_|HAs;nMT!JUaSc%1gKMb-ZE-6u#ihk9ctdg5A|X(m zLUCFsrSfymx$pbE=l$f~`;T#VWRE?v$IO~*uQk{6%=xU}^S@VsYw9X+6#xVR0MOM7 z`27pGu4?CI=Vj$%XUlFK$gbz+@Nr{Px$w*1bC>X9$kdsre z($P~faItYiIoUWlc=#o5^V|}<#lZYrE)lCy$__)?Ohb zr1aMr7@4?vcyHd~6T2fWAt`lN>Atdxsv2BfPv5}M$k@cx#@5c>!O_Xt+sD_>KOpdN zXjpi}iz=K@4p=S7k{oC2jSu4gYk*}@CStFfAs-V z;S+EO6H+Va5LtQBaEb&I(<&zB*LIO`iRvEFS$j>A(nG~oxsU&F=}(XT&m0Q*|K-s? z9r~v~zp(%W47z$?U@G7qa4PNob^Yh;FO^n&tp|80Qs5?#_dlQL(+L1tKpXbIx$S?p zbPJ^olK<1rU#A9;>Eh3T-+=GC#4;*b7%4E3tePTF;Q(Jtb@^r05Nlq{6~une-(4Wg zk~)w@w;43i2%Wj{9^uZpCOHeIcQEcz$6PZ{N4*PFg?h<-S<gt7KNb$M*e1UjIFJ1Gr?I# zV9DO4nViLs;0W>v23%!`U-Q6FHf&&ku_T?!6gwK|(R8-csWL_=4}g;3G5`Qg#sJB^ zfGK*y7tx`;lR7Ih?gLX-ksvQ+Kz7Hz@Q|)Y~JP5~>G{0VQJ}&i- zOY%L}-BgEK?ui@^3^x-rrMoo@x6QPn?i9QT^82_&D-hZIq|Gyptv9z_$K&P%Vc1=n z`W?^ZqT}nCD~nyubXb#Y*8y$X_zTAlwQxQkFQ6 zxE!TT5dE1sR(!wGEyd8lz&%OXm*bU4!jKw!2qjSm^Z?A$n7MqDJ4L*B#9l2<0hgcQ zy?@G2@$@Fp*wE6^j0n;QbmMul9|sj04NgW$eIf_s&b2zG?XP`Y74C5~fLI zLf6d!bqMkBD!LYk?{%}8?$aN=%0{cJC87jDLhE4q_ZjVZQ2?tZAh$caHFPyU_BYWP z!mU!;g+eMS3fuX#AaZ~f^Ye-JW@?l%GsPDFOl@dFn`%xgmtBqLEs-ZL5{h88#tW}vSJ!o7sGoge6Sm; zZc^@ocHeYD&pB~d_L0!s#;(0e0Vjv4EcFjP(~Dqd+_6fJEU{21l1mnlcK2nzv~i-c zy4)n=JJ8E^oAvvRygIqh?zjqf#ygPiumG@T(Ed-sH)NhU48zMbq5n+g$^K9T3@TSmg@ zf@Qg!fMNQ#0)qkV3a&utdD4$kFTB!wB2W7mS?|lv(^mTA+-m0$TAtcZv=zZO3s-6=6#m#XEX2BmiCW2u2TTVkqOYfs;|2w1tZg<~Yo7ER@{xVnu!(;K z*4l@l10hT)rQz78e1i8LzIN4J?y)1gCP70ee=t%Q^G-_c+gj=z-i#^Tt7WdKeVd~X zlh>(TThY18#!s5jd8djbIeika<$ecC5pw>WW=2hh}eT zi=kfqJoJ2d;0BmBZNd-C^b6JL;&*K{KkH3en<8d8@aCyM$>pK zzZ&9tm2rUo@{NKmLjp|S*Yj89v|z((-B80})e2&^0qzj8Cl~EY*Bg=O;4w$=$kQ@W zML)jv+RcQ-*u_GdHlU0K&>~m+e*OaTiy?J+`O?+j@0Zysn9-IfX)&0p?1|fcwvE^$ z)iTDkUu-R}AJ(0laTmCSzv1;8_zc2;&mBl%P9;PN6k%yMq~?r_ zCYg+(9foo6uv!#%n{k(JMP7uVmuU8@_56{K%9gjDTM76{h+qRAT`C%0vK(_DA{JSW z>N_XmM4~=7&?L`LYf4|B94%O;-5Z03p^d5EM;uFeoCo5`Yck zpIEG6{9GKZ$!d!v+BZRG?fZ+gCuT`?tL2kGsMN@qT`f(bZN9xCdg>1}^Q>d&>y!$5 zH@p2sG<+G-FH~LGgr$c8|%V zBVppt^u|;~2oD-SXOGQ)_#0-rRXYjjP&R0}JBeJl3v?QD_#J)oQ=711+_*L7@#qcM zuwNl+UHioLXr#)vCduV(z>}3T-LkAG#6++Cwj98#m#I{ajWL#=mwF~XIfNO7rF}NY& z;j)+9BPIQGCPP1ob0!x5>xOfNXUC>mXPWBbi%g}5qBO^$E$4ZRdKpqU(kP=DqQO!q zz3|i5-O)jin#FYYOt?a-{k(;61nD;S`aB8-WN*R?ep%k&oPJ(^xQEO?YC|->?_xKR zIo(#XQ9NcB%xTNQ_gdNBVgyJ&meI!rpFO9opb7A7&#IIhCa8#()Dyu*GU{zX)DnAqHqpL{oYo0Dkn6h-Kq!6BOpK+}pJpEyh6={5s9Xg8f9?Xg@T z?^*cvnd&@e%}M7puj`wM(b>5{U-$T`P>U1$ZdOxbf{HGIdJkXdH7D)*DRP~V zVyc_JaD=q3e~Z({3n&k&;cT4pDLG1S3(_br4hpnQ+Lf$JP-lG4kuX5zMMp6e)Bl`E zpxNIagq=-s&Geb$QZl~_vO{_zeIk(cLu;IqA$vm@qJ$Oi6W^7M2jmh*2k}2!N){HK zuIu%3f5%iqwV2E?{%)^euQFhGonQ17vJ)e8^!zPFY2>3E_o|_jr41^XyEyr+2M>-_ zn7FMyG}^Ng!WWZ@Zt9X7U?!fj@@75wH9g7OxKJz=@}57?c#yr-L!V2ns)y^4U6S4M z=Q8O#OTNJ(Q)Rm{9QK=AP(8tZyLO~ZS45I%r-Z)u`_75$eqV+1)3eKOZvo3UqBS~O z!^OluD0dbLh!*!GsaT@TBP8q<^T83`9Vg9ZaiGB~+%gBB%6f0Tzb{mhvcH+6QMNXu zaMOV}LWx~@Kwny2l=TNo-Rcq!!d%I$Q=d7;Scw?z4CiUO&w;)Fj26|h@!j{V)Q3o+ z^4sH2-==oA^_z0XHCkpLX!+henR6NF_ex!f&5sKN$9$zto|b3+84q6Gc^g#p+#ZL6 zi`F}J3Hd3zES+$&@|I&;oI5Xbl%3r61i1X})9B(*;i?j$d`PsZT!sG^VhRrbgBbnmpMsoa zAMOeYA@4vQ+2<-Jc7>DCvr?Q$K)ff;~Ct&ZDz_47R#*iAfc$x z1p@01kz!<1R8@3zUeW)Hc;PJS8&0aJ zKAzI`B5!*# zp$e^&Tk7HD@WZV0I%h*-vc2w3St|Ro2%Cu@#Xr%zI)di7swsum>;HmQ<-2^ zucfZkwx#*TV>3psz;;6 z1&u#M7luV%-SRG@cg%)C!E|jXj3wO@kpn56lUgH7-PWT$J?BKk&UcF%LBxoa4B~c7 z84S!G!7*d_d9@da2$(bYjZKfr7GH*hJwy49G%1m|fFuP%srNt2YN zK}eyf=6&ul;7^$z<*LjmcBiS9#vCVKk$gKHd01J>BZ^Wl7;LVQZNb0s7R`;LC^+wW ztS@mYf3)kjBaS90B1C8jK0yb9iE%B<;wb;b*C_!Pal9EZ??3qPBlulNwi{-AJUzt* z`5rwxJ}c~H20+&i5 zm#0_FKu`?Bn98oYya*e)aldC#Zps{3&MD7SgXx2VoMTE&{Q@4nzRr<0WL${PY^cG#C8<>>2ulB)RVcrx0DB4 z&-X9yyXJMX-HhPdQ@GHu6z*G*BA915yQ^Gt48|_+)0bektTxR0y$0xSF+Th>&ky6H z5NYXW2Wxf!ZN=XnPq~n|=aSgfN_eQUjxKWNCvhf4`ne@r`C1l{_opy23=~}%YihOC z@bHx`5$WmGC9{W^l`~!(84*e1Qwz7WVBd1POhB+##4W!}saV0C+5|W3>+yUR9$Onj zO#uQ<`(#nPsr*bv;JoB+ahh+XdJWUot(n;A{g6lExB2)D^^=E2+QymF-tLb{?RrW~ zYjZRnEp;5c^{SPP;$7$#Y;zW-Q*~SnIyAc17Mb#z(H?v4nm;|k7qE3drb#P55N+yK zfDX#Xku)3X8k_?Jogvkc>4LKl=`^^I{ z@ZDTDVvuiK$;k6|veX-6XDpB%!`(8EJ35BkBe*#5RoO1vZJ+KMS<1Exk}V>#G(}D} zbCCWTKZ{8T1L^7QjECI7qHhicff`=fn_GC*FU64JU3m!DAY?kw`fDRYm6f#O;TjYN z2o=!`vUMk;rY&uE;Uy}QC0PQeY4$>Qe-=E z&OyO7t;I#pc#*2Vs5MORzgwX*;hZ@=No02M)cmTap}eC5?BRpkz+0!9_x!r!nsdy#Uffjf93s8M@*RUA^Y3-ApMCK7pc?AYe>b#fu@F5-P8%O+M-YiVM!eZDU9}Gh)3XBnkApH z3hz-INGN$6&V3&hY-5t>A<07QBZ zDCJ-1pf#zF)6{eFrmSMEI>YxDhmpGwIIM}m(q6!r+N(uxY@L3_UgoiVibP~(eq+sO z{k-N)jp&MQ(IQ9;Y*=tm(A_mdd;&H z;}XUbKh-4K?9-SBh|C5VHE=gaT(TTf$@;q_wyk$k0WcYxiNOLOGMV!9wTr z6lm>P2Xl6u#o~}ndz^7mtthS`np(hQWr!}y4xJ*Mj>o_uvKQ9K*#FHFTCXKOJG}3A zPL*EmZXfwhGd?OG5DDgS#~#|PNcDI z)}$a70@d!#I==(b0&w2l*_@!Vr6qyUrfGwCon(5$Ndn`Mu<|4+l7yT^gE0l`Lavcf z&Ga!BbEDrt*dw_Al#!;1o12B?ggU+QEkc&Sn4ZvG3dy&Km*uOHUB@apqMG=^2(wahrK23*Q&NYfQaz0~G)?O46>A37T zpr1L^{+#h`*`3(Yu@a;eiWaH3%A;vW@?#Q8<8H;2fO(BO4n^g}tqp=+uU=kCt18gV z9MY9!&!$NnDY1vJ&fV~8S~>0{7HGbJTZL0*>p6mZj&R>XhSlhopEUjQ{0(R|pW<_k z#xgVKnjnZdlLLv!8jf+%@{Bj~%XS9^zO#JqfsMaiHZvm^7f;exHIA{HG^S9q6bTX+ zjS@&*q3O)GqkrowSNat>k$M)UxzWYMMmr?ru5wk*e%? z27d#;7ztAuHG%b2ilT#pIRPw^$LNDkGc52zHK|_*9rpk?&chMTx*l~$xNeZW$0@NP zK`{-eas-`8q<)rr%~)SQh%iK4pWerstm*4WaFcxGxJaNUhzP24W1tg*dghs!xrV5v zZvA4UMtrI?juc$eN-2_6SqmU)dOUHCx77HipEzWem?LC%Q9A?eLO-V~l{8aLc7uYj zYyPX;D^M{-IeFC1+4@k@u5`4g7(gTD!eXe!<26)^vb5~|PTLPi8CXhH)0iuH7dk~O z{g_WujUNTV1riYV<%x)XnH;b@owK-1adIQNqyMj%`0oh$e_j0(Vg7|LuQHDR70~=Q zbp0=u$o`1Rg#Z2T|59rB8$kaT6IZwVcdhsr(t-b_=fD4#|HrKW@{{|6|8>#m&oU9@?B!}n5UWRN_ByJ6b+utIGfmQA%yIbViN z1lC@$2irNW+K*?d|Lndk^ia!dzIcMvv@gfyMt*Tdm1VGdzKPIgllk%eWtYB9eP6J> z+M`$5`xk!G56C*Hj=tQDkBlzRDBGDe@)Y{CBAG6ht~;cF%~FyY=)3O3UL#iY1(bUC ziHG^8OxfPd_m!$zgkEcN7$-gT;OMYA|1j+yRvUlm)$3dwFsy4TM&`=`Sx73wW$pg+Oly78@-WurG+2RuG5^KZro53}}ry1~vYl?#>G(#kB_UAX2K zNebYE8$xz*FyrUfmK_xD3a*KN3?rMkhExK^qf4 z~ETi4KY^WZIV=*>RUv*V3edhwiMr_OSoT&`@K183)Ojs(kvSEbXa>$j$7D4n~GQr(t3rR z>Pjcr4`R(DZExBPjEOE5Q`!@;84@A#ol+&RLb+2rO|JGGN!T^N7T;`ks)R|^E%F1I zK8(KWs>+lWY&p*!5ul~tIx$ww^1S6_+2DrR3ojmj@uN0EaWn*z?LlF5JHrAazO7RK zqT3Lg>3l^J>eV6u=-~SvznDDcw7{k07#sXk<*zl=9|Qbuu21PboU!a|nb9YRx+m;k zn=(Ns6ovOA&&xaeY%2_tszvZfp=@En%v{0EJ-y!@daT7X$fNJtt-`>T6UdLwZNb+W zAqe>L+5GO<+Asgcnb`J3r46oksjNL=2X9J61THfML zF;uimAivODMB}wA#t`g@DxX^#r1tCxy8H7j?82mF>A z4n6K1JdvWWUCYtZoR;e@;g0PJ=JHFw2pu~~seiHU%`QvCzFuD5kupz96cKd=6z(?0 zHh2s&*((SyLluu}78zGx(ToxIvorYvHAf|dWfo@heb$J@KqfBasdW7ErvQg{< zds3i%A_RaZhrirsl$Q`XZR2I=BiTy%T0|}M|nmV98xc-6k)}Jq=cqbp}Zd0mIy2StBv{feJHn$GK6F0V?T}Kl<&=ubP0syS{J5ZaU6OltxdGaF~+jy3|4tW zf?Fgj41DU~D6kbL6}F{pN6iti=r-B|WAh>J{`Lw`dxBA>jMx?xGn#99>_V2-NVv1; zqMp7i5``haweW&-NxHf`*pRETx}TNqwtN2C9uCwG^kN;zz%9PtW5D7>@jNk7X4yk$ z;2XTgTdIz!28Q^38~dK0JQTXl*r&lpCet5&SOpe4rA9McuXJ%IZRz4TyDg-}+rrBz z8xH-_26`c%vKhKJmx_jIZapvRZ|4kjzVRSJ`y<~q+*LV6B%okL;mytq^;QjUe}cgP zUF4BF7F=d`IaDgGCM*4YgD?m4pIQ0eA&S`c%)Ud-w3Q3J)1J|=!0E4D{nHx{XaShi z6We1)8nl0t7)pzk&Ll^VH|IjcNOcdrzT( zNvx&!xSmryX483yF;0He;Y-eFEM$^W()NO1Y*UTTq<-caB?C_09*1Ev1dv`zciIF~ zGL>nEWl}%Wb2caV`;K#!VPSdn*z3I9s7PNpjZSmB8vFeg`%|QWFc}xitHs>wB`_}- zF*{1BhFKnDU=KP<% zGQP?}IVqQ8W9gY0_e`CT2NnUi@g@-l6-IM;oC?=2qn21@*|7WCaO!N~%xvG7IO26< zJ2CWR^ELE^`)g13TtdG%4>|jx%_)o#@|n(NFF{UaM}hlC$1JRr|Nc^&H@(z+4sn;Z z5kyoOl{)+=g4KCgyJh;6{&fQuyZFGyc&PISy~mz{b|)?sd5&kh1;jY%rop+3;-mfTD~ss(-nPW6o~ z=&a%2fJ$<}{gauZ0KTqHeeOV;?2V1ntFD>rIl=ynjL5P+F){C0lQJ@&5*JM|A_cJN zq`(D{wO0kcsyUe*cLS3_3^^1@l`71$AG3LuVD{EVk|3DNIeSxq0;72^^1*MwGlSnPcWMy=_|%dmL3*se6b6Uw=EVwPVSxMYVI{Sl=5|+AF*MG=o}{C4|RVrw}qy_m+t7 z+Y0hRl~IZy2lLiuEvrqVzbz9xd*aY`y{&|zIi1?yT3kDj7gR_15Wp?4$XD|;*fyLn zYt?hpt%oR9jk+_73M&&tC8oL3L%Lq2T-%Y^& za;{AFQxh6=ZaOmknfwi65j#IH-y>d2@RGbuZQhHRONxknV>?v2p@#AoR(PrRPStA% z=ft;fm8e@xz*TdQk=`Ty^$3QI80xE7@KQ$IvibCRovS(ZQsru7%%W9n=%GOmDreay z$v8>04|cPLPSDp1d6tfSKO2OI#E+O&;_0z6dh-$uK6Qn@t=p)Q@1q1l zFw`z8j@Bk$E~Sc<<0NuEoDVZu^08%+!rq7kBf!;HA>|IM-5vh6pchknYp^$5P!;pK z0=_qI?FKOh;7#_g+cFa=sHwW+8?S{LSr3X!d)-<5;@z93q@bq?F9r=PPhxw!Ljc~z@KSc5qv~g+`7Eo)rwrIo2n3sNHTL`RKfG`4GwJ;V!&PQI(T||G1 z8>WKWQN1BA3XI9K7js2XGJF+CI>6Uh3pCOX)%`VEE1`oU%YgD-sIdcIp*D*~ulDwVCHyaL_hM!y%)eW^lV>ZW!^vL=c2=G}pk}z^w)M?lHS~X+* zju#u-`iUsN^4R7)X;E01n>Kc!f9jC{9*!a5USnDYZ@} zgxS2`r3Ppg>a9O1Re97Z{>nWw8l-2|THo;j+ENEsgr7>ov)ghttTG11+nOFeRPl>w z!VAArfBrpD@Xu;vuq+-afELg2buaCDelyalWjy6_LkL4}wl}sSQX!%uc~ziEy2W8x z*Ed;fV98*aAkY4jm_6P`9$yD8GYAoGNE+zUxn`H13jn@ifCg>CgQHPqRRLFn$v#H4 zl@LlLd3ug%p(?S9!!;@~@%hu|Pl9Kt-{xfCR7_Ol`^C)g<{q@R)wi_0ywB0tpFZ^< z@kHqwU`<9%*UTuR{uuonjy3g$QXGpq4km$u0eCrpz_01RuLc4M&AC;cNHo;^1sQnKo__7ss&{5C;$u`B5^+ z%$%~rDtxluUR3D9-k|61*+rG@ne9+cV#FoL(zvG9XAdOGi_RnzCUaKapvddzs|@N@^Ex z2q0yt1PLL(686^Bw9eSW7BJJ2=DkMKrCV@-q9}n7ju$--7~^Ps8x49h4>FRJ?Z5#K zLMRyA>{uMC9_x5;&opFJ3OVVn+%m4RYdoc@t2kcwlV0DlP-A`8!0W+{p155#TDN!s znG+xP$=_BxiaMxF+JMByc#y2;_7h;-Dhrkz4;gYbOl-0|BM>gHQtS+@+ajhDNQfB0 z`3DsE2L$=6f|>_h=*T?_C-LCxi>q*O4I77wWmlK98dsTEO9E}hRTGr-kay<=(n#wP z$`-p{8Cug;`O`E6$B}Sm`OT2g|6-nTNLfj`U2n$SWB5z?eVtU2vR|x+D&s;-Hf4r) zpPCTrtxbuAv-D_%^sJ1#b(ev6Zc)NrLf%P(^rNESHjwU>KJ^cka^sasIp9fiao_t9 zkZVkQn$%9{bRoy5&nX^!dU!pLpYF}|_3=w>v_XcDvm&Wz@omfkMD4obx0M%}Mc=_lTSiWNy;Eq4D z&E@;EgweFcdg`Wxb9=m+jt(dsg5R~i@w??F_+xrr23DOdrCN6}s9IU=+B}P|W(3pP<)o!@F z)ifhOK62ZA>({a73HcawLr#AKa+^iF6{Od#yR`ht=(}c-S5bt7L8k0}^DuX(o^y!S z!D`}5Ms6iflw%N%V(g5czqTxp(q5=)KbN-IIM_bD=$VZhLcT{bEdugnT#NvySp-XI zky_x2gp~rts0tUgD6%W9ASW%-!vF)J9dsbi4U`jt!bo0czvbf9kb0Kd6i?TKo|jWS z6YOZ;CDCIflW9_qnc{^2L|F>7x2r%~5Ra^e9FzeHB8Pb7~tJ8V7sU=;9&z&B8%AQ9a4*e>&jImep_jZeCk)ZBLdiqtBU1Df>?Cy_>?oU1bVwi&pz0o5v z?R2q0l4?T+@%Fqtnb=XsElmg#+W-yC)0&IP*SZl~FgHn2thUcQIL zV*_Wk*!FAE=V02elOVt8OS!(OwJ|I)2U?}By*VgcBQlddFYGaCjes&?h?SCfaamEZ%2!QUMuh{A zC?x&~Wib;n(DhwRYJzR-?LtGy4UCRiI13P@3VwlgNW2nW2&vLB7{%IZy^3-{nFFd zza9t5*>&#coZ0M4^)sA4$CjLf@9DL+G`vttVve+I6(`#kkus6Yz{<{VhZ#q0*C@5>u;D@#a-r%`YlbCk*W(RDN8S}z*1WfMd=le9Jivn!jb)1m#>5{bgu4w zDOD;_Wy$dcIibD8g*nJ^F8iSV_=jrf4{*1GE+ z0ruVAeI$&QaBB@8Jcnb!nJE=FLD$VlWarS4fSkTpX}TRFRZ16y!qEO@q150|4W#3k zZV5Y(s7jX!-;6+bt@9-Y-fXew1(8v-AG&VG&@&3oMY$b(X>Mq0YSg&dK3a40XY#(=E>c{b6~D#6=nrorp@4;G3$ushzJ_E1UpehO+(o2w71+nUk&B z?mm~&E$6o+D5{cR_|(icH?0GQVFLbaStVp3y3R^Z*EuaF`5QYXCOv$lZUhK>fRwae z=&t3L0bMe{eAj;aVM7fnKr;Yn|MLtGbF)XipmpaXXISQ%xXRlejT5$$hpZIaAv zs-~-1bWH)&1%wcBWimC>IRzIJy}Z5B5HTZ|ohV;ybGm1l@`_$SWn0lRmACq_AzpU? zS}zF^X|KTuZHd!Q)v8g9TehyNU4FxSPgrVvj*Bym53l0!2P{rAkpF4Hcg?ojy#9gK z>^6MxK2H=0ehgkSJCh$?&ogMQFp|BlUczsbnR``5WJ~N8+cENeY$*V!3+>}JYUuX9 zTDjW`3JC~aRRfvSX1&=@7EllcRb4N-$J+(1P^ zooQsw+DS1)WaK&!!}4mO>#{BRjs#abZUAlkCbW^b5t*lx=hX(NoVxp)j9xGgRaufe zvgeE6=N9D_epWv{dwqN8v~%k6%t6}UL2_KO*cM;&&F*ba+3wxzns-L*Zxx9OJ87dx zYsb_uENlwO3D^34y~*2WkImE*#?hm(X5qu%Fs^peLx=}ZXFC}Q#X{q-a1=ZuE^0jb0}0` zh@%c%n7NVBB^!7zhhB}iXy&$k=k=*&E$(?cD~~A}b}k0#`s^6U(_8bz#i{VEz>>tG zR26}PZGoN>IB$6#;onQ#e{B)_Cpr9w81?UD@#^aDnC&kC?4P1p(?2>AB>(=uu2}y& z?fg%6|FZPw)~^510N_phx8jEXP<+Yf2LB^K0#{XR%pa!zvs(D?#qIyi)xXKm2>wz9 z|2usBv&sLIOzYx*A^wL~AScuxy$k+7^!mRs|8L=+?B5~wmAC)#4CyN0&BdQ1E|?N{ z^AzNn<_yKJ?X*6G)Z&)=>{0`{M|7&QpaJt`h;N`Vt!KAD0=aAvAGKQ|%$1&2Ru)O< znkkW353|QJQ$=lWr94hqp_$t(SkY~rhDL2IOC#st0r~strQ%DR#P}?~pk5?k{17>GE$u;O`f986!kJFJLSPe=V$)k|sfCZ1sv1@9JBK&eAuG$`}p%Cipe9v4hQ=S6VP9>(D*=cF$QP=vBqpl`OVSjer z-Z2G(pKB-i@yqMMX&yZB8X?lM_GAOR@jBB+p8O)|T{NnUifs?I#`-yGF>5K9{x@JX ztcmh!S+#}K^1<4cu}-QsmwYtUre9Le#fxtdu)9;c1dT`UGQ3L>y_HT=azz-#J`GC2 zUjCsC1M@_6m71(nu?P1J@^rCCq3Y86$C}<`T!B4Xpw@?piKu3)UCJtD7L0Q7r|&+Q z`{600*pG`JAJhn>iZWm&1PY<>8+*XVC15*Z1tq>mNT=PU`OyGZd;k+uz*dbpzdX9RjM zThE>C@0uQAazo5D&O|Yl;WNhf$0wk%G4o^{S9!jnHP{jZqYwTgEVY#xt2z*bO+K8e+(c(9sn+CQ(Z%wCA(k5=lnQXzUmJ#5sRfcb#okia z1GXN1d0!>*SAy^y@%qY3J5%16?VM=RP9fB6{G=wa?H!4kA%ewZulft?7!zwKv;tAg9=0DU#=%@ZJ6pik(xs5JRT~j3_ zKbb=!Z9ggRA@zMwLTf0-TDXl7&_#eAX@4-%o)yDjqwhDVCoq3o=W;wXIWK_F8GH=a z2@PYpy+qE>E*KDmXqx(=!;2MSZ)-^X3Qg8-F$t~f+YAL>_!+M@)mSy(k`;x&&10r0 zE$x3=`Y59yw!wsAI3QIK&Hmt|G<8Iz@OHyt7b)l>l8-f=(GbZn*+_>pML4+gBa;g0 z+Admf3Yf(ihqqPJcGsT(B^$Erz|H+ z(Qc=jg;+sfHWB8HB)+cs!|1oFU}JkGV!pe)nAK(X~$z8sG98C!5@w6QMnq4o=L@Z<6FLz+&Z$r5@1%F!4P$34`xGipAdXPKhumKbwowSyCv)1)nE3Mk@`PN%M% zxDioW#?*AOW7zsS!r|_Ic4SE%SxBN5hGtMm#fFi=Qr7FKRB=@vqG?X7NmEj;5MP2m zVOg1f7~C$T+3t6gVjiQOq6~%YU4YrCM<6-$XXcOkTAvmAocXOSyEvp|tfOOf1)~#4 zz>?a2)t!WQ#Vaa^t^_rRd}lxLyW+WZyN1qI9Z73zgiU_g`BbP{_-0Prlcp{sGrNZ? zs@G=oMCL3SUc9&NviKnEF5un8IW{gm18?C^Ze-q&+ zxijeB<#AcDk)*fP`!dYZ!-&*ijD%r-Hm4B}x6FRs<5=Q8>w`FdcjyQ1}No^AKmek%~6carfc zLSC4AhG5rx#~<6HE0ClFmB_L^xu#NQ4a)*^KD!dGc#xt9DOL@433DRaisqM=tI|^+ z2Z>zaFKQx~I(-QOse2Dl+^-ucw&xT#0i`k-Pnnuj!nY9OXmI?JU>^&Y$K zzJCq%u4!z_XwH7d?7kgZQv@q*9HtvCLCgig1lnvbGMD*nyOfQI&_VIb-yljlR+rSD z)Ao7_#?2pW{RZGd`Wjrh4+7Z>@%uZ4V=@H`4Z-5S5J^Hv|u-#6VWD{qa&SN(9duLVrt<7l0W-e_nZBBTEo-Ui7aK{{t7n%|y_w+Sb zN-Ps;ijk9!UicyV{CTNM-8^Q>xP6ZQEL=w-VHjrp(rfdKUM4hUa1bxeT$@fz&>#N7 zs68Fm>tN;iZ0b(YASV~1-ob5NVOjBrYOHr~6`fOAYAHIWPv3fX(ZuDWeH?#aUUO3< zl=FC@i1~dAse-C9ZUaV!87dRP?&0gXNL+sgiF&W;WqxEP_gM=1>Y~<~gMo$aeTsL8 zinMP5HrPVSXP>!;IvO!MBk({(>FiZlUk{v0#WLn`)oB-(^y8SV+)Ch*a_=XvL5M9&PC!GaSUf(G{x+&#F3;6+im1_%}i zZWRhAxVvit!3uY$a0#9i0fL0{>fC$!^y$9cJ?MC|_ zS|YWw$5=-j9-T6<`1DBg&@dVRX&L$M zg89+_U0PJ;2z;wG%7k+?%2qR?NQ_3`RVfH&GR&NqQCGc-vdv=&8oi@${F!VYZAdUl zpPg$yOwEyj2x)Y1#}^d=&|2Y=Akd7o1LEZeUjS#kp`Dpvj_q8KciUPVG+*>Hj53@7 z-G*IkM~o~tR!0zgFT;KRvJj5c$JIH)M-z2E#TOyR+AFd4VFt;N2nzfc;mEro>DV*e zslxlYiMQ7KH$e0H(4Ig0C{(-oAo6AnwNpv@+W{rngN!54w<9Patq86fOll5XM6A;Y zt4a+`ejxS*uLxL+OSWM#kRoP)YdF>3t15wPEx1?ZvHmu>IOIP5#r8opkBt@FsH7ck z((Gj<^+{GjeFuC+p;t4qRZgViJQF#<-DNE{9`tp`PNLPaJ!EttsZ02C^E^_W9A}q)yw4aj zkA%HqWUQ3pxbZ(TtVC_L8SKfUR%VBsU=%HatvVZX!7c=HmtF@gcjhI>#Ht4wXcV{& z02}L&T5;G5jy+@SqVJf;&y`O)cFA8(2Dp5`ai3^grBbz1VoWFJ#<7-fGlU%gd3FIS z1I0O)vyPcZhf$evt&FEz$%_3vbwkv7I+P`};GjzA<|ah3GmmTKxFzYQhB4Pl3C~`F zAB)NIerm>5o55@19^0Fb`xY61Qyq(DG~A<^LGh}Y)t4qvN8QroEiW_Q7ygg*dV^#R z>MG_4s+BY!2kNr5cs~`!W|>#mpW*Vy6JlLn^-Zs6M`GXXGwJM+?kbE}(3W?_p_^t! zu+w@G1mcGu*J~)S_ZrhlOD2AkTDhPqjg?u2Q3uy3Lw!C`Xhr{HnHh<*g9ttHoFAw|?=^FBqaS6kJkcL# zX8#DXs&>crL9xBCCmii2yBefKeQT(Hjm9}b^IXzkp*SFVyQALdg070uDx^c^sZ zC4*zrl}{B9GNAu zNfIj7A?C;H&}Hd6|s5@CJf8m!*rG+GN zKpESTRc?;*oPKu*>{}Nf`^x$H{=98#tK0NibYMNrX>@FQugynMN4yCSRTLTfo=@CO z%LVu?)KXKEuKo=H>|F%3Nj!el&X$`RXw1sjPcW^e&u5_Exai*QNNd{!0B zvL5js^mg1>2y09gHPX#SrtrOny`wAS{b{LWeho9aK>Z++#y+^dpAE`WjMq^N5#+sRZp>AwWNn)(k(vXrIF;M{lB9{5WebocPkQ3@Q^@F?5d< z4i+Mlm(KXnzX7(W!u7iwni*=zi8BRyl*I4hpC5Mep1)4jnpzAKv&GyYt zwWO`^1Rau-iclX3!OyUKt6#ZjMMKZ7hTMzVl7v{RrV+yE(2YGzU~@JeWq_ zk(g;u@}>3GXt_VnN`ivqj4RHEg^16{okeTYrz1CumbxnJK)n%Bd#~yw>TkpV!y8d` z6~*okQyxz3qrcLWdlMM4a~TloS`764Hd+Lqyvq6@unPQ|oJ2y)#jKDYwKkM02JijR z+(u=|8Y3p9TPcqZqSaj=D~sZ*KuZFk<(0Ic36%X#P5$Zg`Wq|xmr3SNV)F0hKi0qd zW-rZxBn{`!g`ZyDQIa6G4vb3@cHUrGubMe4*g9aLC(1b)%ru8Y$pUSY2JE)QprBe2 zu5#;(1=jaYr-r|lY}(Gz0m^tYiAm)9-!XDZqb@DfrtR86qwOtqHbllI*I1Sh_HO!h zUmj!OyEnB!sa&-ee^e8my{dCs>1n)^CSO?dSLcp*d7NFJbP~)5`BF!tghN?0Yo*_^ zP}teo+15E%tYXvJjGVlG9eK%?k|gABclnoRBNSI}Bv3 z7yIqPozRQ6FM%Tx&TlE0XRFEAFpo$N+2sQ8K=Eu;4&9-{jb;t`kL@r%j$?&(1+Du_ zJ$Y)>hOE}*5zf6m;xSxUQFUf&NRYf!g0P9a!{Q`S>|-%GNE)IIp@dr54zuReG!o{! z``VPFw2vA342oS>+=osVDlVD1Y<4T19i&YW2!ChFd8RGD9pp0O74a|yjWRJ=0n&St za@s|`hJ>%?_L{+PuXCTq(7iGUFyk_7bMY4Git(qm4U7!t4ofJql6;bJ)BcsuOosJg zc{X^9ttl5XW>#yqX64Sh(Aq%&(=HBUXHQ=d-ek?E^`&3e!Ajh;PJ)2OXtt{QM@u^> zUnHkIt1kIVRRbg6t_v?VgF*o9t_P>76dkcw zI%2TPO+dR$WzaTQEJx&ulm|Ya)29)=%TrNU<#R~&w76G$ExRK1Jid^M#L9R!@HOT~ z8=%$9Q_ULvl{HI6x-#%d#ikgqLV|z{3)46Ak00F8==DE08!fQMTyc%&7%CD@^&e7~ z+hxk3A;T)Fa4Q!X&65P0W|jz$R+rdQwOgQYEDcCg?_|^?uoL3J07OgwJfGf+Aq$&i zJ||Y!;Tov^>S0+_4fZEHEyWY`xTyUQf91;V9>)mIyX6-$zu3+m3e@RSJN zv?Nt5WstqSY{aDifA;Qbt4}bZJaFS-v`?qH9fyDTet2~yppDEw8zi~tXSQcEp+Ads zk#>`~DhUaWWNB{w{x}}Kw`-WRvZgF46m#0f6{e4_=NWg|vs3-ZO7usy9*w5u#Qu)O zx5G%9cPX-jNd{Mu=%-9eMDA0&L5k|8D9MFzvqPiIh_=s-?w-z!2}kNaP(UDT?Mzv& z|26jA`~4vo`8T1J;9%!dTG5q<7@li^wC09PDkfEcY70h$P9yth8Wttxkf|X8H6gXS z&u$9_rm&d%u^h2BBeA4p_NGCBtp0+?+i=M8mt;%x66UdK&Zo*!U`wf4S2t8LBMx#z zT<4hlPR7e&BX%;Z(xn z8je@D(uT~Uv#>he%|LsGX@K7-P72?Msi+w4=1v?fEvnoBimMff&ul_KQj&ILK-FP##CL=-@0Czvj}plRL6gh9Qh5&CL;ZVtoFULSA(lN*9`Nymk!ZJ&<4JxY>q2<=uYcMj}t zG?Nk7`mtKv91!r6vANX_^~;&KEP8TxtP?n6kgC+~^F!|iC z{VA1l=S3m-~DnfhNrW?Pcr|q*z3Oq zKIGm%=AOUf9ODv(-#7pNa~S`tiSKWdB5L;^;fqN9?sxa`|6BCxQ^5aT!ThUwhXFA9 zGr>XznPBn%s9*l?%J2Ss6o0M;kT(VQ>JJyR3Mukg;=Kf>8Qum`Afli&**C8~$(^9s zO^ltParUv;#2BH}1Fsw5#YG~X!|qIxDtZDIp%y(6Xe{7^!*L2!D(e)ST&hCHz4(|8 zu|5u3BH6EwfEW(;48};26cb8LCj3Uy9sprU2t8f#*usUmqAOP)=7xx(oZ<5!H21Fp zZ|p;uO3)ij&HG#l`dbi1f@g>#TSnUzH=o@1ST!5~ZJ;M;?^gjbK=5mS{Y{k7nMp7(bLcqR$?r$}MZ`st5-7s+)#IssX7JTf zR{6*1L#X~jJ^Y1iKn9bt=*jik z#TQyv^zijEgvj0!$vzoOBKYh0RDWJQUYm9m>;L+qkYs3|(cbdfA0f`yRUAX7MoU>(JzbQZ@ zK}9cw96++D^UP0C=*RbDe?1=QE!S;zGp^L+F_W}?z^S;mZk;Py8a&DHL#-8+RqT_} zEw2~@=(x$iSaSHD*u};KPm&nl3A_!h(dCR^{s{AN=Aai?F-sM?w>Yx;rluDx?cxjI z^Yx$7LF9^!iY42;Vmo|U!EmYx`3GwhWQI_UjLLc^5%+d>WPGsbEXwYqm z0vn+Myi*=Z6#Krus5JFRM&}Ei9gCr0@#@-Gq>V(9S`qrLzg6l@!u;fjsOLxV}wU!Z%@dH z(ZW>Vt?mS2-g5+!-MlMjhy&g4gth1cjE&j?RBW=0eyU3Ljux?Gar`uH>X+!?;nXG=0j4;POC9>N?#OeO z)4C8v==4aG%dyCeUJz}CJVI>Y5x2VW9Qzm=>5g&=ZJ*7mHRLQuAz=KyPkOpNFCyU| z4OZ{<87MP-assM;`m9oX8P$z{IU11a``!zojA-fMPwu1%_-H=uYmdRP@LXbL(rDVn zm1nsG3OHq37KnM*#ucCcu5A?(N1xHC9Yje-x@_hL8Ooe*sTcmSuDFqk#tM64Dm*jL zieKGx;Q<(ulibysHbmnDb1Bx>ceJ;K-3FzYrY}eqCE`f2>EQ6UsGSb_O?ZaR?!(ol^*HPCUYb!3KPieC^y&=ANE&m{!_g1PPUI%}%)iop zqsq=M1af1V5xQacjkE=Qz9PD1G%T_FZa_W3Lg6-R zbL;#WZw*iNr+JA<=0?erD9J&(1}x%Qo7T4~;sew?NROY&v_soFwbp6*fY-uniuTH& z;HR2NeO4s4eaZ3Z6N2lhOT@r8KGAQ|TCW909qiDP6>0U>=mv7kcVS5_!SlHygx!8O zS-A>rHwtyFn?v4?E7%Ai;=>CyrDHT&E>hf&UVW860 zHR`RI>&ZZS98Ky41-LAo(c>KvU`bjxG*RQRuN7U9F_2?B2qSmFzgn0reg=n4&qmrm zkD$sI6|>72K1cP#^i^!N^vI!e-fCC-MPSu8Q85GB?48iVaUD9|_uif& z$@#zwbG6&Rm7IQG9YF|v5;WeQPJ>lc(@H!6Jd>}Y)K zExxH4n$I5bQgzxZ`-@BqPOj18*oBh>A9^?q7CU{-!q>TMJtW{av0oed#jDWk zjTF`czf&D1UWIka7<^QnsycP8u>~E!Yi(;K_IF_>DVi1xO^YLL1>RiU_fZ;5$aexH z$a6~I>B8urKEun$t-^+(qQ9nPj5!R~%*H6q6LPftc-MOb2=LUwm>dV)cAwi@p!-kf zB#DUH279T{>ACmfVw-4_BW_|lLt~~b4;y6#m617`l~RPVO3W1$zO zHu|}~vZPo*j`pY41k#Alk}4kc>TlY*b$JyvMG^HD0T=fN_m^GrT65K-)~VQxoCm0f zAL^JQ2)gi7zgX3?*rJ}I$eYD#h3g=^CWZQw^E_FOpPlokp`_{);cz7<`RvjXYrZO@ z`g^S9A1jEzuCsfdq8=_h`!o!4W;HNlgS#>*#Bw{hyRe|WMTVhG2mp;ycTDGN*&;#J zS_pses@F5_lX0reJyoeESlY!ACK?SdxZgO37y3RMU&)|#V2SR|Kmv~FA z@%KiMAs>_UHSTvRsuNl#2kh)GOt}HxcnfB!qL9>1t3?3Joga(g^9%mr$>(3|@DtAV zY5c1T+EN)mx@J+g(sBy(hL2;co;Twy zX0F?J?+hl96{A5#L1ILHZNbu1O;DWp&BfxiCOAT zRx>enD9Od|OwJNMcWQpS`9{|k%?qw|tlDMPDM&JwzA_`10!H?EAjC!|5~rP-?7 zt3O6kv224pS_nzGsuH-vYbq>p8qYY|>|a0=6cQ(j`lVs(G)Q$(b^Gap9JA*Tt7n)W zeNw3v`rI>{`jmlZ>}INFo~u>wlnxTq4$!~7U{nmD*Xd3fl%(j&sb&HXyUdVMKk>)Q zMjO5#Cr2H+dT2&I`C!mSQ0XY5>%8gHuZ#)!@`T_7hY1SBRZZcq{9nkA_tnPD%|+JO zVe7fWbFtf(4NIs5M!L;iDToc7j>vuI8;G(X*C#D4W7_Oo#|D%skZE%hU!QCFy8Po# z@(AXfR2z1NRKPc_{llXmpb0>|+G%#>dC^MFE)>D<5OH7~r|!a1F2?Tr!V-Bl0(3jK z?wVD(@!}(WD(`Wcw6!OqRVp!~O}ca;mf^_*>td1P@@2PTCfiy>P4)*dX69T%9C|tA znc=S%((K`uKm9#~%45@REE?P2OOduW1e{4XnE<3eSF7!-3I`ncEJHzQEGOXMji1IT z&v9S{Fg%}6Jo8mQ+MV;;Q!TZ*d8XPvs8h4~6eMk;(`3a36aSzTH|UwHpZBH{48D4s z?+x6y2~HMN?xY%Y^B>ec&>9XAsVwX6x;ihj5E2!jvX2`Zu=9}R?h|8Y@Cd>nm*E%2 zTRO3=S07%OFdY}384~YcH9%k22|tAUrsd9HB9s8DZs0n2A%_&Srftz*Ikn%>FFi~@Tke_^9bWfw9TWv0 zX1iws((a6OA0{6r9;Q0(DDLn4$M-Jb*PaPozZ@IZP`oV^c{W=$-*C|4{F7P6QC7K1pz1nGHVN6)E zXCcOLEqawNj8D!J5r^--63c+K&>lBAW4*Bo3oQk!;*bDEMx*W6#nG| zigIC{KhJd(_16;~g(kZ7u6QOVFOr!6<`$YA7Zf$Mz8ji!UE@gt5Vmv&xn>NsmoVf` zI*_4#yG{8zd8bY*`owZNN(lYObgP}*x&QFu7xWMWzqv3QyM{DDM=H@Wa2&%B`C4yv zByEbxQO)2WgSmX74JY3;T`m_Ksgt8e8k@OYA1B_Wpd8mYvFnHHws$}g+xY093~D(n zDO4@xmtoip2y8JS6Vo0+!+D2=XYuea7415oIqb!rRP8p>`>^Z)+yJPCsHy*>Ch;Fp z>`dp>Gw1yH61)SI;!k7k0%aBh!<3u6>;9X&-paT&7*SpPo{TBXQmR5m;z&L%JaON|^%5jMF0j3f_?U@~^!-DQ2VvRBqK^$c6C`z> z)^gjLdsSZ*`QnGq8h*@2#Dp*N`GKVSkfkE+N@x~gEVIDP1-F`W?)L-%YkR0qLJ9z58gv-<#3K`-lLx-AG7X9Zec(0y1i9!z`eyf4y^$^l-5RD z>22=5kkga7*tc-qnzUl}GgiaDG}$KJ$2@*)2w*#qP?CBd@?Le;>v)xG`0V_wdVPkU zi^Z_=sV#35pGNG&0j*8m@k*96*7{+lhJ?_Yh?+uwK~2FvT90LJs}I;_ENV-XNjBlI z=Mqy{Z5WA>XRbWt`|4Se`kCY@YipMB^yvmMJNR26tj1_z0$8$8G%KTysY|?9jV=is zpXM23=o{9QrLSo`3PCNh9YSRqB?kD| zEcOOG;k|C!EC#9!G(p?Xwe`R4BuX3CutRoZR(0CayHHpzy83~y94X~5P?A#w1VdTU zsj0`S2cqX={j9mv!PFv&S{=}JBfXMcz@yA@nq9Snq&K@Zs417I^pDq_UbtSwuQiwj zeNv@5Z&gQUQckwkX6<{FYLk>gmddRYG91Hf#qC6HYIhgZfpe+5PjtG*Jq}iYThS2w0g-Xq}$QXZ7AQYf;Z?s4AbigQ+MoHxY)$8 z@lecZGK_ueU~J#mzVrlcKv|>~CHuMwlQyS9d#oUd%%v6_A8WzQZ`t=NOj;Ej(eU|R zgMUP>hxbvTXIyTP4kkuv6!@f;v9tBtSvo|PX_&^f9T~|c+nYwGXwlWvTx_9O>e$g? z=?E>}j_1iK5#$=?$S_yh6dNRGmqK}{LiO*}HVk>@&87S;z64G<_(I_9kODM$*SObv z!D}mpkk}0@OlapRqsHO($$iprXZ~G|yLu~r_Kp*;6)rg`5I92|x_2su{_8AcKTSuV zOO~%=hMSk6>!Lco|7$%mQAHkD|E1q}TR`4c01@1&>4Np*pEFj5NuJ&MmDe_#6HLu3YkJCC0F(b|Dd1y4e(7s`+M<7C(28m z`Dd3#F`9B`X61~DqspnRBJhDa)m_9;?Q9;|OdV;kixa)`opuKK`Lhwkr%8H9qxk9` zcsYJUg#Y@JOV_k58n1Vb(%BxbUB_zyjoHdUbX%42_K$l*V_M?JA%%GWav5W3mN_4l zC@8;wa+C8kaN&E)f?gFGC#nDr?UNrSD24=j4=V_Dn9H77wCBE>8Fzp7E@Z&0d>(C|6Wd2!_aK0j@vf{$ce`CVUjN*-kHBE`eQ1n?W+ zr%JO@C-evu(GIm!gq-pfjoeWP0^wsamkc)fOB{SG+rgZ=JTe=FBuWIGuD}2UyuG@5K>gfm10Q`Ns*b57c z#F_&w>0eHm-Qlp~@Hyn-*!P|#ZR4U38BEn{Q2QHzrT~W9dYiLfba%FYfs?!^rjys% zvhNJQR2`BIL7Ew7>WGEsCLQPsrC-M=CsUwBTIDCaK@bOX0-8a!M^~?aA{{w%kGzqY zjm#e^VSPS0uZQo(H?l9L^cqmoZYN4BlL6q*0_ z>HIm_y!=O<;@`R#|E1*h$HsqDzW&pZ|8CzuX*qwU)Bkyyf4A%XbNT6?hrC4lZTbHI D3h+gJ literal 0 HcmV?d00001 diff --git a/repo/js/AutoPlan/settings.json b/repo/js/AutoPlan/settings.json index 754d7da14..866ac7550 100644 --- a/repo/js/AutoPlan/settings.json +++ b/repo/js/AutoPlan/settings.json @@ -17,6 +17,11 @@ "label": "自动秘境计划配置\n语法:说明太长 去看文档", "default": "" }, + { + "name": "exclude_run_exception", + "type": "checkbox", + "label": "忽略运行异常\n(当前计划出现异常后,会执行下一个计划)与循环体力计划冲突" + }, { "name": "loop_plan", "type": "checkbox", diff --git a/repo/js/AutoPlan/utils/bgi_tools.js b/repo/js/AutoPlan/utils/bgi_tools.js index 45dd88889..97c0ec26a 100644 --- a/repo/js/AutoPlan/utils/bgi_tools.js +++ b/repo/js/AutoPlan/utils/bgi_tools.js @@ -6,7 +6,7 @@ * @returns {Promise} */ async function pullJsonConfig(uid, http_api) { - http_api += "?uid=" + uid + http_api += "?uid=" + uid+"&enable=" + true const res = await http.request("GET", http_api // , JSON.stringify({"Content-Type": "application/json"}) ) diff --git a/repo/js/AutoPlan/utils/physical.js b/repo/js/AutoPlan/utils/physical.js index a399925d4..adc7d2cfb 100644 --- a/repo/js/AutoPlan/utils/physical.js +++ b/repo/js/AutoPlan/utils/physical.js @@ -1,4 +1,4 @@ -import {getJsonPath, toMainUi,throwError} from "./tool"; +import {getJsonPath, toMainUi, throwError, findImgAndClick} from "./tool"; //==================================================== const genshinJson = { width: 1920,//genshin.width, @@ -8,7 +8,40 @@ const genshinJson = { // const OpenModeCountMin = settings.openModeCountMin // let AlreadyRunsCount=0 // let NeedRunsCount=0 -const TemplateOrcJson={x: 1568, y: 16, width: 225, height: 60,} +const TemplateOrcJson = {x: 1568, y: 16, width: 225, height: 60,} + +// ==================== 常量定义 ==================== + +// 树脂图标识别对象 +const RESIN_ICONS = { + ORIGINAL: RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/original_resin.png")), + // CONDENSED: RecognitionObject.TemplateMatch(file.ReadImageMatSync("RecognitionObject/condensed_resin.png")), + // FRAGILE: RecognitionObject.TemplateMatch(file.ReadImageMatSync("RecognitionObject/fragile_resin.png")), + // TRANSIENT: RecognitionObject.TemplateMatch(file.ReadImageMatSync("RecognitionObject/transient_resin.png")), + // REPLENISH_BUTTON: RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/icon/replenish_resin_button.png")) +}; +// 配置常量 +const CONFIG = { + RECOGNITION_TIMEOUT: 2000, // 图像识别超时时间(毫秒) + SLEEP_INTERVAL: 500, // 循环间隔时间(毫秒) + UI_DELAY: 1500, // UI操作延迟时间(毫秒) + MAP_ZOOM_LEVEL: 6, // 地图缩放级别 + + // 点击坐标 + COORDINATES: { + MAP_SWITCH: {x: 1840, y: 1020}, // 地图右下角切换按钮 + MONDSTADT: {x: 1420, y: 180}, // 蒙德选择按钮 + AVOID_SELECTION: {x: 1090, y: 450} // 避免选中效果的点击位置 + }, + + // OCR识别区域配置 + OCR_REGIONS: { + ORIGINAL_RESIN: {width: 200, height: 40}, + CONDENSED_RESIN: {width: 90, height: 40}, + OTHER_RESIN: {width: 0, height: 60} // width会根据图标宽度动态设置 + } +}; + //==================================================== @@ -17,14 +50,14 @@ const TemplateOrcJson={x: 1568, y: 16, width: 225, height: 60,} * @param {string} str - 包含数字的字符串 * @returns {number} - 由字符串中所有数字组合而成的整数 */ -async function saveOnlyNumber(str,defaultValue=0) { +async function saveOnlyNumber(str, defaultValue = 0) { // 使用正则表达式匹配字符串中的所有数字 // \d+ 匹配一个或多个数字 // .join('') 将匹配到的数字数组连接成一个字符串 // parseInt 将连接后的字符串转换为整数 try { return parseInt(str.match(/\d+/g).join('')); - }catch (e) { + } catch (e) { return defaultValue } } @@ -40,7 +73,7 @@ async function saveOnlyNumber(str,defaultValue=0) { * - min {number}: 最小可执行体力值 * - current {number}: 当前剩余体力值 */ -async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isResinExhaustionMode=true) { +async function ocrPhysical(opToMainUi = false, openMap = false, minPhysical = 20, isResinExhaustionMode = true) { // 检查是否启用体力识别功能,如果未启用则直接返回默认结果 if (!isResinExhaustionMode) { log.info(`===未启用===`) @@ -57,14 +90,14 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes await toMainUi(); // 切换到主界面 } - if (openMap){ + if (openMap) { await sleep(ms) //打开地图界面 await keyPress('M') } await sleep(ms) log.debug(`===[点击+]===`) - //点击+ 按钮 x=1264,y=39,width=18,height=19 + // //点击+ 按钮 x=1264,y=39,width=18,height=19 let add_buttonJSON = getJsonPath('add_button'); let add_objJson = { path: `${add_buttonJSON.path}${add_buttonJSON.name}${add_buttonJSON.type}`, @@ -73,23 +106,28 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes width: 52, height: 49, } - let templateMatchAddButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`${add_objJson.path}`), add_objJson.x, add_objJson.y, add_objJson.width, add_objJson.height); - let regionA = captureGameRegion() - // let deriveCrop = regionA.DeriveCrop(add_objJson.x, add_objJson.y, add_objJson.width, add_objJson.height); - try { - let buttonA = regionA.find(templateMatchAddButtonRo); - - await sleep(ms) - if (!buttonA.isExist()) { - log.error(`${add_objJson.path}匹配异常`) - throwError(`${add_objJson.path}匹配异常`) - } - await buttonA.click() - }finally { - // deriveCrop.dispose() - regionA.dispose() + // + // let templateMatchAddButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`${add_objJson.path}`), add_objJson.x, add_objJson.y, add_objJson.width, add_objJson.height); + // let regionA = captureGameRegion() + // // let deriveCrop = regionA.DeriveCrop(add_objJson.x, add_objJson.y, add_objJson.width, add_objJson.height); + // try { + // let buttonA = regionA.find(templateMatchAddButtonRo); + // + // await sleep(ms) + // if (!buttonA.isExist()) { + // log.error(`${add_objJson.path}匹配异常`) + // throwError(`${add_objJson.path}匹配异常`) + // } + // await buttonA.click() + // } finally { + // // deriveCrop.dispose() + // regionA.dispose() + // } + const addClick = await findImgAndClick(`${add_objJson.path}`, 1248, 21, 50, 50); + if (addClick === null) { + log.error(`${add_objJson.path}匹配异常`) + return undefined } - await sleep(ms) log.debug(`===[定位原粹树脂]===`) @@ -103,16 +141,17 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes height: TemplateOrcJson.height, } let templateMatchButtonRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`${tmJson.path}`), tmJson.x, tmJson.y, tmJson.width, tmJson.height); - let region =captureGameRegion() + let region = captureGameRegion() let button try { button = region.find(templateMatchButtonRo); await sleep(ms) - if ((!button)||!button.isExist()) { + if ((!button) || !button.isExist()) { log.error(`${tmJson.path} 匹配异常`) - throwError(`${tmJson.path} 匹配异常`) + // throwError(`${tmJson.path} 匹配异常`) + return undefined } - }finally { + } finally { region.dispose() } @@ -125,7 +164,7 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes // y: 32, y: button.y, // width: 61, - width: Math.abs(genshinJson.width - button.x - button.width), + width: Math.abs(genshinJson.width - button.x - button.width), height: 26 } @@ -133,11 +172,11 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes let region3 = captureGameRegion() try { - let recognitionObjectOcr = RecognitionObject.Ocr(ocr_obj.x, ocr_obj.y, ocr_obj.width, ocr_obj.height); + let recognitionObjectOcr = RecognitionObject.ocr(ocr_obj.x, ocr_obj.y, ocr_obj.width, ocr_obj.height); let res = region3.find(recognitionObjectOcr); log.debug(`[OCR原粹树脂]识别结果: ${res.text}, 原始坐标: x=${res.x}, y=${res.y},width:${res.width},height:${res.height}`); - let text=res.text.split('/')[0] + let text = res.text.split('/')[0] let current = await saveOnlyNumber(text) let execute = (current - minPhysical) >= 0 log.debug(`最小可执行原粹树脂:{min},原粹树脂:{key}`, minPhysical, current,) @@ -149,7 +188,9 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes current: current, } } catch (e) { - throwError(`识别失败,err:${e.message}`) + // throwError(`识别失败,err:${e.message}`) + log.error(`识别失败,err:${e.message}`) + return undefined } finally { region3.dispose() //返回地图操作 @@ -160,6 +201,269 @@ async function ocrPhysical(opToMainUi = false,openMap=false,minPhysical=20,isRes } +// ==================== UI操作函数 ==================== + +/** + * 打开并设置地图界面 + */ +async function openMap() { + log.info("打开地图界面"); + await keyPress("M"); + await sleep(CONFIG.UI_DELAY); + + // 切换到国家选择界面 + // click(CONFIG.COORDINATES.MAP_SWITCH.x, CONFIG.COORDINATES.MAP_SWITCH.y); + // await sleep(CONFIG.UI_DELAY); + + // 选择蒙德 + // click(CONFIG.COORDINATES.MONDSTADT.x, CONFIG.COORDINATES.MONDSTADT.y); + // await sleep(CONFIG.UI_DELAY); + // await switchtoCountrySelection(CONFIG.COORDINATES.MONDSTADT.x, CONFIG.COORDINATES.MONDSTADT.y) + + // 设置地图缩放级别,排除识别干扰 + await genshin.setBigMapZoomLevel(CONFIG.MAP_ZOOM_LEVEL); + log.info("地图界面设置完成"); +} + +/** + * 统计所有树脂数量的主函数 + * @returns {Object} 包含所有树脂数量的对象 + */ +async function countAllResin() { + let shouldRestoreMainUi = false + try { + // setGameMetrics(1920, 1080, 1); + // log.info("开始统计树脂数量"); + let resinCounts = { + original: 0, + transient: undefined, + fragile: undefined, + condensed: undefined + } + await toMainUi(); + await sleep(CONFIG.UI_DELAY); + shouldRestoreMainUi = true + // 打开地图界面统计原粹/浓缩树脂 + await openMap(); + await sleep(CONFIG.UI_DELAY); + let tryPass = true; + try { + // log.info("[开始]统计补充树脂界面中的树脂"); + resinCounts.original = await countOriginalResin(false, false); + moveMouseTo(CONFIG.COORDINATES.AVOID_SELECTION.x, CONFIG.COORDINATES.AVOID_SELECTION.y) + await sleep(500); + // resinCounts.transient = await countTransientResin(); + // resinCounts.fragile = await countFragileResin(); + // log.info("[完成]统计补充树脂界面中的树脂"); + // 点击避免选中效果影响统计 + click(CONFIG.COORDINATES.AVOID_SELECTION.x, CONFIG.COORDINATES.AVOID_SELECTION.y); + } catch (e) { + tryPass = false + } + await sleep(CONFIG.UI_DELAY); + log.info("开始统计地图界面中的树脂"); + if (!tryPass) { + // 如果第一次尝试失败,则切换到蒙德 + await switchtoCountrySelection(CONFIG.COORDINATES.MONDSTADT.x, CONFIG.COORDINATES.MONDSTADT.y) + resinCounts.original = await countOriginalResin(!tryPass); + } + // resinCounts.condensed = await countCondensedResin(); + // if (!tryPass) { + // 打开补充树脂界面统计须臾/脆弱树脂 + // await openReplenishResinUi(); + // await sleep(CONFIG.UI_DELAY); + + // 点击避免选中效果影响统计 + // click(CONFIG.COORDINATES.AVOID_SELECTION.x, CONFIG.COORDINATES.AVOID_SELECTION.y); + // await sleep(500); + + // log.info("开始统计补充树脂界面中的树脂"); + // resinCounts.transient = await countTransientResin(); + // resinCounts.fragile = await countFragileResin(); + // } + // 显示结果 + displayResults(resinCounts); + + // 返回主界面 + await genshin.returnMainUi(); + await sleep(CONFIG.UI_DELAY); + + log.info("树脂统计完成"); + return { + originalResinCount: resinCounts.original, + condensedResinCount: resinCounts.condensed, + transientResinCount: resinCounts.transient, + fragileResinCount: resinCounts.fragile + }; + + } catch (error) { + log.error(`统计树脂数量时发生异常: ${error.message}`); + throw error; + } finally { + if (shouldRestoreMainUi) { + await toMainUi(); + await sleep(CONFIG.UI_DELAY); + } + } +} + +/** + * 切换到国家选择界面的异步函数 + * 通过点击指定坐标并等待界面加载来完成切换操作 + */ +async function switchtoCountrySelection(x, y) { + // 切换到国家选择界面 + click(CONFIG.COORDINATES.MAP_SWITCH.x, CONFIG.COORDINATES.MAP_SWITCH.y); + await sleep(CONFIG.UI_DELAY); + click(x, y); + await sleep(CONFIG.UI_DELAY); +} + +function displayResults(results) { + const resultText = `原粹:${results.original} 浓缩:${results.condensed} 须臾:${results.transient} 脆弱:${results.fragile}`; + + log.info(`============ 树脂统计结果 ============`); + log.info(`原粹树脂数量: ${results.original}`); + log.info(`浓缩树脂数量: ${results.condensed}`); + log.info(`须臾树脂数量: ${results.transient}`); + log.info(`脆弱树脂数量: ${results.fragile}`); + log.info(`====================================`); +} + +/** + * 统计原粹树脂数量 + * @returns {number} 原粹树脂数量 + */ +async function countOriginalResin(tryOriginalMode, opToMainUi, openMap) { + if (tryOriginalMode) { + log.info("尝试使用原始模式"); + return await countOriginalResinBackup() + } else { + log.info('尝试使用优化模式'); + let ocr_physical = await ocrPhysical(opToMainUi, openMap); + + log.debug(`ocrPhysical: {0}`, JSON.stringify(ocr_physical)) + await sleep(600) + // ocrPhysical = false//模拟异常 + if (ocr_physical/* && ocrPhysical.ok*/) { + return ocr_physical?.current; + } else { + //异常 退出至地图 尝试使用原始模式 + await keyPress("VK_ESCAPE") + log.error(`ocrPhysical error`); + throw new Error("ocrPhysical error"); + } + } +} + +async function countOriginalResinBackup() { + const originalResin = await recognizeImage(RESIN_ICONS.ORIGINAL); + if (!originalResin) { + log.warn(`未找到原粹树脂图标`); + return 0; + } + + const ocrRegion = { + x: originalResin.x, + y: originalResin.y, + width: CONFIG.OCR_REGIONS.ORIGINAL_RESIN.width, + height: CONFIG.OCR_REGIONS.ORIGINAL_RESIN.height + }; + + // 匹配 xxx/200 格式中的第一个数字(1-3位) + const count = await recognizeNumberByOCR(ocrRegion, /(\d{1,3})\/\d+/); + if (count !== null) { + log.info(`原粹树脂数量: ${count}`); + return count; + } + + log.warn(`未能识别原粹树脂数量`); + return 0; +} + + +// ==================== 工具函数 ==================== + +/** + * 通用图像识别函数 + * @param {Object} recognitionObject - 识别对象 + * @param {number} timeout - 超时时间(毫秒) + * @returns {Object|null} 识别结果或null + */ +async function recognizeImage(recognitionObject, timeout = CONFIG.RECOGNITION_TIMEOUT) { + const startTime = Date.now(); + + while (Date.now() - startTime < timeout) { + let gameRegion = undefined + try { + gameRegion = captureGameRegion(); + // 直接链式调用,不保存gameRegion变量,避免内存管理问题 + const imageResult = gameRegion.find(recognitionObject); + if (imageResult.isExist()) { + return imageResult; + } + } catch (error) { + log.error(`识别图像时发生异常: ${error.message}`); + } finally { + if (gameRegion) { + gameRegion.dispose(); + } + } + await sleep(CONFIG.SLEEP_INTERVAL); + } + + log.warn(`经过多次尝试,仍然无法识别图像`); + return null; +} + +/** + * 通过OCR识别数字 + * @param {Object} ocrRegion - OCR识别区域 + * @param {RegExp} pattern - 匹配模式 + * @returns {number|null} 识别到的数字或null + */ +async function recognizeNumberByOCR(ocrRegion, pattern) { + let resList = null; + let captureRegion = null; + try { + const ocrRo = RecognitionObject.ocr(ocrRegion.x, ocrRegion.y, ocrRegion.width, ocrRegion.height); + captureRegion = captureGameRegion(); + resList = captureRegion.findMulti(ocrRo); + + if (!resList || resList.length === 0) { + log.warn("OCR未识别到任何文本"); + return null; + } + + for (const res of resList) { + if (!res || !res.text) { + continue; + } + + const numberMatch = res.text.match(pattern); + if (numberMatch) { + const number = parseInt(numberMatch[1] || numberMatch[0]); + if (!isNaN(number)) { + return number; + } + } + } + return null; + } catch (error) { + log.error(`OCR识别时发生异常: ${error.message}`); + return null; + } finally { + if (resList && typeof resList.dispose === 'function') { + resList.dispose(); + } + if (captureRegion && typeof captureRegion.dispose === 'function') { + captureRegion.dispose(); + } + } +} + export { ocrPhysical, + countOriginalResin, + countAllResin, } \ No newline at end of file diff --git a/repo/js/AutoPlan/utils/tool.js b/repo/js/AutoPlan/utils/tool.js index 02d2bb8ff..4422daf93 100644 --- a/repo/js/AutoPlan/utils/tool.js +++ b/repo/js/AutoPlan/utils/tool.js @@ -368,7 +368,64 @@ async function findTextAndClick( return null; } +/** + * 通用找图并点击(支持图片文件路径、Mat) + * @param {string|Mat} target 图片路径或已构造的 Mat + * @param {number} [x=0] 识别区域左上角 X + * @param {number} [y=0] 识别区域左上角 Y + * @param {number} [w=1920] 识别区域宽度 + * @param {number} [h=1080] 识别区域高度 + * @param {number} [timeout=1000] 识别时间上限(毫秒) + * @param {number} [interval=50] 每次识别之间的等待间隔(毫秒) + * @param {number} [preClickDelay=50] 点击前等待时间(毫秒) + * @param {number} [postClickDelay=50] 点击后等待时间(毫秒) + * + * @returns + * - RecognitionResult | null + */ +async function findImgAndClick( + target, + x = 0, + y = 0, + w = 1920, + h = 1080, + timeout = 1000, + interval = 50, + preClickDelay = 50, + postClickDelay = 50 +) { + const ro = + typeof target === 'string' + ? RecognitionObject.TemplateMatch( + file.readImageMatSync(target), + x, y, w, h + ) + : RecognitionObject.TemplateMatch( + target, + x, y, w, h + ); + const start = Date.now(); + + while (Date.now() - start <= timeout) { + const gameRegion = captureGameRegion(); + try { + const res = gameRegion.find(ro); + if (!res.isEmpty()) { + await sleep(preClickDelay); + res.click(); + await sleep(postClickDelay); + return res; + } + } finally { + gameRegion.dispose(); + } + + await sleep(interval); + } + + return null; +} /** * 抛出错误函数 * 该函数用于显示错误通知并抛出错误对象 @@ -395,5 +452,6 @@ export { isInOutStygianOnslaughtUI, outStygianOnslaughtUI, findTextAndClick, + findImgAndClick, throwError, } \ No newline at end of file