From c92efb02f3224fd3fe8fcbac722338abc8318774 Mon Sep 17 00:00:00 2001 From: mno <718135749@qq.com> Date: Sun, 25 Jan 2026 01:55:41 +0800 Subject: [PATCH] =?UTF-8?q?js=EF=BC=9A=E9=94=84=E5=9C=B0=E4=B8=80=E6=9D=A1?= =?UTF-8?q?=E9=BE=992.0.3=20(#2779)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- repo/js/AutoHoeingOneDragon/assets/复苏.png | Bin 0 -> 3063 bytes .../AutoHoeingOneDragon/assets/飞行状态.png | Bin 0 -> 3845 bytes repo/js/AutoHoeingOneDragon/main.js | 320 +++++++++--------- repo/js/AutoHoeingOneDragon/manifest.json | 2 +- repo/js/AutoHoeingOneDragon/settings.json | 4 +- 5 files changed, 171 insertions(+), 155 deletions(-) create mode 100644 repo/js/AutoHoeingOneDragon/assets/复苏.png create mode 100644 repo/js/AutoHoeingOneDragon/assets/飞行状态.png diff --git a/repo/js/AutoHoeingOneDragon/assets/复苏.png b/repo/js/AutoHoeingOneDragon/assets/复苏.png new file mode 100644 index 0000000000000000000000000000000000000000..36e5c6239811a20e651f268a5fc3e1b8fcf6dcff GIT binary patch literal 3063 zcmV&Vr|xrOwxvP|M@Zl-NWd&LIpJ1%QeO6* zM`gwBtu>;V;rDBjvrsdl4(Ck*W&i*(nSAMzC57VP&T#eT`QAv1ulucB_GVnIl&h8U z^Cl6Zz28d$X2^jb3kwSxjl%cmmfinZGB^6YYDScM-Xw4cix5r{iDYAA&G+ZFHT|Ys zd3)aELbUhvC7_i>oJfj;ePMpql60CFuTNh+=c+_KEJ7m5WKu$0v~->ruZcYeBTC>% zEJ7k9!-HjS#?IA$D$&@}l)y1qL=Yq~A!bQBeVX+n%F>j;+Twg$OLKour)t%Z`)->n z;(@-dn(FGB>gu+Z=H-Q1ywFdZ3o_9Z3~H#a8xi#3K?e66k3|GQitlA_u84@aqro+y zH^V+XC$IXbAV zh_k|CYycAOiv)Nft~BHaGMpLe`SkX>yeOH{)-d9^F(jZ|5jQn9SW{V$#p8ZDICIl} z%z_m|CBmDxvmF`!xTCr8Z0T&6r!|d5b@ju8=nQ{~3wNhDD$~x9g9kY>{Jq#=rKRPD zYOmQ})5w+pdG`!yzmMFELcClN641jUHd6nbZKxU$`RJ8^S}C6%AM#*^9v4A8z9x%u zu`v0@PXCHW_%@G|dqD9oC3@?B#0X~peGPE;`4xEi5L^pFEG^7+^XTMAN5zxykWj5O z;yV9`&G`S0CKF9vjZcHPc(TH}I-Y${NRR1ce{3v3DXqxo%E}(bCZyvoA|}PnI|$}a zap4AqU&Di9;?w+t;$V;so$bjA#e-rK(vwp&nwuI8C493-8KAKMjQ<8ck6=)K5rTIe zAoD76(LxuEBoe8r;y%Hmq12!wpJmx>cFsLWgPkMCKPc|kUrUeWs(=4|D^p@2-%iX#Ak$7R1?6K~^4^oz!lzzRB7R9pf<{N_*J zfS5(ej1Jrt%cAyYt&s00RZrw%As+eThHO;O9~zYu8kKZ4A?-=k6FkV^p1pN600`S1 zc~%U*%#i+?24hbE+Z_O)b@FI$XFE4FM6hUZmEajFSHz7C^;T4-EuHP^73k>h4{>z% z;NQv2&A6MV!=`G zqGEg3RutyJesOJa{=tKC7<$}Kcb^agu2=AuZmH1yESj5}J2{y5lwn4BS+sNF`1nR#(dj%{q4rK3W3cGS3hiotW{j48#Ufm)SYAMK6TyGy zNdFoq6{cuqkwhYKIZT2@V`$}Io|-J4e2iADN(TD6@(YSIU3SjBn7CAkuYi$KNaeS+vj~94Wd_0G$#rm)95h*^GyJb5 z-AYWpt;0pfrQ&NHzra|q z%8F`D^6Y7g4GlZswsmAj$ED6ry~2TG`9-~+9L5t$<|jfTuWQoJ@Ck0H-|IN>m?!ak z#Ui$y2!JyM^pp^5m$Stp)bWz+b!JvRWWt)pV)3Gy8yk*dsv{Wa(WFGJMCXJfEM64M z>WXi~*zn6a;fVV-}T-@GKlg6 ze-06QI%kf><%QXt+?~U~eOT!IGaw{hla)p3%&j{)Fcs|KRIB3F<|g=r>gsnVGgmYI z{LH8)D+~*zGVPsw@m+Km&%la`M}{&;_=-gm3HZ2zL1M4_jJ-=ktwa~zPN)8OcaJ$L zljggpNz=+WaX6_$Ve^_%da7dx!C zy91BEr9x+?UO8P3z(#XAJ0d1!M9{|zip6uNe0=zPR5OR8`1e=@0O5Fm=f&V}A^7(+ zw$V+T0Ukwy#*Xt*AsZg-^$Xn5Jv`O3;FCO2~5Do3+2$Mu{2KmzvGfb+F&#A+u*q~}v(%;h=pLo+|*DBV|i4zf%JTkQZhRXXj z$%vq@zP5IK>CKi3{bOTcbhzJ>wPPH)?~cKi3jMSx9})D!;%IJuSw9uvkbO`{QHfCR zfg~PZx5un912h+aiLY(O?#3dQE^0N>K@fzn8PhFRgM_M;=*;A>vj?Bz!i~F@-qYQV zYhAG{s;{eM`b9!SZ0T%gj~z=0NNsp@axWHt$7WGmeLihsR=W5i23wBQ)a;9_TopGr zHo$@mYdpKJAi|dRJ9mEj^s(#dh9ia0*Anq8PM3lx$t5%}QRq@P(ptYqbzo3{OnqcR+ zbJmO@LS@q3c>K_Wou%&XPPm;ThvOfeb~CG_q^za6adf!AQv5qT&)b`rA2j z!lRS%%UN14Ay6?hi?_4mMrx^9Tp)*D#0|jkE@by-0@&4iEG!N@jHL>V4Y8#_`F~!Kbaw<>eK* zd4>O6R2b;%hC{q?Lg38_4-QXkYiWWUAFT67h3xI@_|w*wy4u=~=WP?C1BQJ;z`+ua z%_4DE|DX^}?#agu*@}GbtX*>7+h5%6_;mdy-eF>L*qKm;kHI4GZYLAct0`9XNGuZX zyfa1Bnp~arut*}2hq)uclb@ z-S5<}w4^xLyE9yQTy{ciR4wtH8ahwUOi|Nkzs}fq4c$yj%KPbdMQPFQcMV~i_=F=Q zaLN)eJLU3-;s^<7lK{aYF#?p@lI8Bw^~?^b50*l0o7H%`rrS(bz60TZpqg$d1Xw# z;wjW{n3gZmveqXr?hQ>8YZ$!Tik0(txeYJ3VdYj_@umve9q_*Z@zn_tT0mC|5P#^#kWs`KUtc^W29!{no< zj49M`U=JOCO3OSPdA0U7_9sgnuH%#taC;F;1_HPMf>&$UA`Mfbp^G(ik%m!g+4DO3 z-q6I@vhTZ_556?lp8-tZbxdb1fK0L+s*=h&NW%i4GNwQyZj9-;5-olIg(DbC4KI35 zYb^4{$^jZ-Z`YU=Eooc-Kr(>1hH_q&R;4A@I&MS(*isR}e~ zK6pUX^2!)=P~Z{(#C>1gy!JL4j;Dv_TxuJ+w~;V<|u* z0%fqmLOL8zw+v5}-)W~G^_tpxT%o;R8*ND7LM@jEoCHFof;22JwT3Cwiu-|iI$o{g zKD&C$yK@kZr&r%bFB@23M)W|DF6ij02VJvk!Iqbk=C)_+`$9-aNl%tS07ys&V#%9N z2Fa$*nrAP!j}oT#-Xa}X6VqCOE@e!9DF8xtW(%QhkpKt?*YOuB*u||#IGzs1QrBC% z)mp^F6=`UFW5?I#+Is_I18*077gfe7k97!B6Z%2NpzTY#mu#-4;1^0eM z*>I4C(A|&;0kMie@G!lBGk}e>?C6pg0li+`K2>H`YnX58`q5?I{8r>2H=BkR+^*oR zeaX9Z6#o!VDiS{q3#qleqd+u)fIczaEYk5PaLA6TLX;h_mLMz)lpOGY+}1E{qre%F zc)Dk1;c~gz-1a;eOCiL&eWTS+hqj{$7^V-%6M|6-#Zt@Lk>$XyZ};%=#82lcpa+0u zQ4z7JuyP*m4T@XVpu&n**l;b!KJE)fll8qLztG;WFMC4*ph3^#R^)#ywa&oqdw?>g zMj-=l3F#Meu4~OtsHVqXS?-916y!e$9A0iy;b3Dc27kgr427u>i!Y|*{=zldp1%0q zRqN9BP6Vzfo>c&v6_;7arpFumv2b>tziS}y7W87>^Ot`x)#9L(LkQpvBoEL@h3l=j zUIMBR|Dl$F%B|wD@n@tB zFPAPHlu|HoDvabXio+=$qZ9*@!$}4=Fmy+c=;CaPtnEiXAvwOY|1Zp)3p$Hh!<;Fz zOsuRz=q#X(1EHaJm~4Dh-!pt~VC;wL2g3{QcY%P7L{n2dE}~?xFbH(T$gCg6&?!Ul zbSR#JPdXG&_r6*DoAu7-p|Ei%ID>mN&xbEp(BJWQHxC7+kYOP`w;sIJ)&I(~F}Cdc z+H|{Rcye^f^JH@FA|WO<@CHOdHd5h6?j#V&0oe%Vp?xYSLLe0IGn`0=6KTt%?waR= z;(vuyP)KbY#Kv9z)x9Hh0Pz??DiBR<90>hymcOX9-tD)KJALC$U&XynkYb?JL9@zP zR&JGmNPqq7{{dma8G)r7-i&<75aO1*5HRnz!WS>@XS6e%s;k}%5kTyf}&fng!U2W|eb$T3b|G3Lr-TU$^Nn->HJW6pO zwNXK?fMjs2!i_v`6y*U50ZXwT8BCtt{hXU2pi86$*VBJuZaX}yK_N>*^cITdq<78S z@odcH8*_T;uHF&@4drK%YcYz)NggA`x`Rp&0utpxKp4q6jAZ^*+w{IY zL?F_F)+*AQ?+)4DH+d_TsO=dtbxPXE1aJU$q+muXfkfnthd4E1sqP&LCFH`B6_G53 zj6ft^;S}+oX2|bcXq9kG_No6^QsnrI{)0x@uzXkfp07GWvagzHp zSGRT$%_&kOc|5xrD5|&tcLfAEDsp0#0rEh~OiGESmVJ#Id9~jB)B5&@fsoE5cabnp z`!{j|?Ie%gk+-MyCb+RE2@WB$BBP)bJ){8Qiu9)S!&uG@Kt99gEK&$KfW`IZ-r2>R z^LLWRYx_sPzHUPyLlDZJvZ=*_OmR6(eUUIJOYn)EBp}h$U?-BP=^M`Z3Ih7lQX}Pg zCaoj_>28p-EK?XjfdaBV2o=WvzF|9SFr71) z&KpdZNb{#vTxtCS>cOM6{UfEKk>v6EL5ytfxKd@s4GeM;*cA9rvG>hx+1wr3JS^Fy+itY4};nD9m&}-G2J%yYH)t}U-iw! zgawB+N%jy(d&w<0#jCEicK*i@iV6vx1dAtd+ne?6PmGqK1=oqC5Rk*|Zy9lnulQbh z*RFRwR}(Z&afPVQfelZwqGqa!^`_pR7Ehe)#JahEO{7DKbP#lB$?t0#f2g~^63Kn} zZ$;zxhDPrV*(W`#qsty^_W-!_p~o?bD<nf_q>ro z)f_bWn2{?4m4LoLm{)e*W*;s$O(>q)jwQZfZ(}3_X<+ANMEy8>&R`;29{;ofZs@Gl z5}?pedRA@S{Sz>7cBf}(!8z{oUIaj35d{zhdiyU;H-kd*-xNkU3WM`bwcdmoVV#9d zFt{mEHw-_mE>|+$GjriYI+8q=-$DbbxuexSIpOv>R(umHzL913=ay;_2o!R(C;(*D z1Z`+)Qvl^wXeF|D&R_yxw<3y!g~UeRdL(<=VD6h;dKaj9#PLJjeY?}+Sn)aBe!J8A zxw#sn*o>*g!vQi5CoIjQj-d2Sc;ax#%T#j*Ith|QY=1K!3oJM;$*K+J+P;@^BOpi6 zaKGKw-49EE&*Apjo!(DOw{Qd`uZ0HS#3WOqH`!g@tnf>pu)%oB72YeUxB+Yj{Rgfm z@adB2`hcrQ_6$igT|KM&M<;+ro!;+j?xAlZ#MD2xeCaCc`8hG|GmC;#g4lg*v@8W8 zp+u&ZAgR_5V!t=v0$!H%P@pIf37YQeJ8=PQ#}c!f!7EqU|7&PQ71H4b3e^ve7J=FU zURMH1Z2+x-5H$5yObr48rp`0a`LIn98Rb(VK7+aO<@iqWI3Of9qj6(v zCq~k8ddvQk-WBW6H(cH3k)X_m&7*j$eX1Ci35;T>)-H$JKk4yLdRB+#m(P$Ev_c`p zsqzUrpP&&|=9DmMaAWqg{>JQPARr{iJ*%hmCPY(6fm$j9WlT-7MFjn1dQM3b5R#o! zZ%PbiP>bQGx~{1m0Zn;UiI&G|lE#dD0mO6g&>fq2h1P^3ZfU~fpgRbbNA~x8ORyl{UcL@ zv{6p}hOa|FzvpUk1C6wSwxLx;KUSj=3S7Q{R=%m+OdcyGk^czqAMPwgF9sr^cq)=S z-aHb%z1aX-LC_0GB?=CH^CLgJ?CF?#^ASZCkRVdx?gah+g*zLm{hGCl00000NkvXX Hu0mjf99>Ud literal 0 HcmV?d00001 diff --git a/repo/js/AutoHoeingOneDragon/main.js b/repo/js/AutoHoeingOneDragon/main.js index 279a2416c..1b9fceff9 100644 --- a/repo/js/AutoHoeingOneDragon/main.js +++ b/repo/js/AutoHoeingOneDragon/main.js @@ -1,52 +1,42 @@ -//当前js版本2.0.0 +//当前js版本2.0.3 -//自定义配置解析 -const accountName = settings.accountName || "默认账户"; -let pickup_Mode = settings.pickup_Mode || "模板匹配拾取,拾取狗粮和怪物材料"; -let dumpers = settings.activeDumperMode - ? settings.activeDumperMode.split(',').map(Number).filter(num => [1, 2, 3, 4].includes(num)) - : []; - -let findFInterval = Math.max(16, Math.min(200, +settings.findFInterval || 100)); -let checkDelay = Math.round(findFInterval / 2); -let rollingDelay = (+settings.rollingDelay || 32); -const pickupDelay = (+settings.pickupDelay || 100); -const timeMove = (+settings.timeMove || 1000); -let timeMoveUp = Math.round(timeMove * 0.45); -let timeMoveDown = Math.round(timeMove * 0.55); - -let priorityTags = (settings.priorityTags || "").split(",").map(tag => tag.trim()).filter(tag => tag.length > 0); -let excludeTags = (settings.excludeTags || "").split(",").map(tag => tag.trim()).filter(tag => tag.length > 0); -if (!pickup_Mode.includes("模板匹配")) { - excludeTags.push("沙暴"); - log.warn("拾取模式不是模板匹配,无法处理沙暴路线,自动排除所有沙暴路线"); -} -const operationMode = settings.operationMode || "运行锄地路线"; -let k1 = +settings.eEfficiencyIndex || 2.5; -k1 = Math.max(0, Math.min(10, Number.isNaN(k1) ? 2.5 : k1)); - -let k2 = +settings.mEfficiencyIndex || 0.5; -k2 = Math.max(0, Math.min(4, Number.isNaN(k2) ? 0.5 : k2)); - -let targetEliteNum = Math.max(0, +settings.targetEliteNum || 400) + 5; // 预留漏怪 -let targetMonsterNum = Math.max(0, +settings.targetMonsterNum || 2000) + 25; // 预留漏怪 - -const partyName = settings.partyName || ""; -const groupSettings = Array.from({ length: 10 }, (_, i) => - settings[`tagsForGroup${i + 1}`] || (i === 0 ? '蕈兽' : '') -); -const groupTags = groupSettings.map(str => str.split(',').filter(Boolean)); -groupTags[0] = [...new Set(groupTags.flat())]; +//自定义配置变量预声明 +let accountName; +let pickup_Mode; +let dumpers; +let findFInterval; +let checkDelay; +let rollingDelay; +let pickupDelay; +let timeMove; +let timeMoveUp; +let timeMoveDown; +let priorityTags; +let excludeTags; +let operationMode; +let k1; +let k2; +let targetEliteNum; +let targetMonsterNum; +let partyName; +let groupSettings; +let groupTags; //模板与识别对象预加载 const itemFullRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/itemFull.png"), 0, 0, 1920, 1080); const frozenRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/解除冰冻.png"), 1379, 574, 1463 - 1379, 613 - 574); +const revivalRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/复苏.png"), 755, 915, 1117 - 755, 1037 - 915); +revivalRo.Threshold = 0.95; +revivalRo.InitTemplate(); const cookingRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/烹饪界面.png"), 1547, 965, 1815 - 1547, 1059 - 965); cookingRo.Threshold = 0.95; cookingRo.InitTemplate(); const whiteFurinaRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/白芙图标.png"), 1634, 967, 1750 - 1634, 1070 - 967); -whiteFurinaRo.Threshold = 0.99; +whiteFurinaRo.Threshold = 0.97; whiteFurinaRo.InitTemplate(); +const flyingRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/飞行状态.png"), 1657, 945, 1758 - 1657, 1029 - 945); +flyingRo.Threshold = 0.97; +flyingRo.InitTemplate(); const fIconRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync('assets/F_Dialogue.png'), 1102, 335, 34, 400); fIconRo.Threshold = 0.95; fIconRo.InitTemplate(); @@ -62,12 +52,11 @@ let blacklistSet = new Set(); let state; let pathings; let localeWorks; -let runningFailCount = 0; let lastEatBuff = 0; (async function () { //通用预处理 - await loadOrCreateConfig(); + await loadConfig(); targetItems = await loadTargetItems(); localeWorks = await checkLocaleTimeSupport(); await loadBlacklist(true); @@ -123,70 +112,104 @@ let lastEatBuff = 0; * 3. 无论读写,最终都把结果同步回全局 settings; * 4. 若配置里出现"莫酱""汐酱"关键词,直接终止脚本。 */ -async function loadOrCreateConfig() { - if (operationMode === '启用仅指定怪物模式') { - return; - } +async function loadConfig() { + operationMode = settings.operationMode || "运行锄地路线"; + accountName = settings.accountName || "默认账户"; + if (operationMode !== '启用仅指定怪物模式') { + const FORBIDDEN = ['莫酱', '汐酱']; - const FORBIDDEN = ['莫酱', '汐酱']; - - /* -------- 1. 构造 10 个分组标签 + 其它字段的默认值 -------- */ - const buildCfgObj = () => ({ - tagsForGroup1: settings.tagsForGroup1 || '', - tagsForGroup2: settings.tagsForGroup2 || '', - tagsForGroup3: settings.tagsForGroup3 || '', - tagsForGroup4: settings.tagsForGroup4 || '', - tagsForGroup5: settings.tagsForGroup5 || '', - tagsForGroup6: settings.tagsForGroup6 || '', - tagsForGroup7: settings.tagsForGroup7 || '', - tagsForGroup8: settings.tagsForGroup8 || '', - tagsForGroup9: settings.tagsForGroup9 || '', - tagsForGroup10: settings.tagsForGroup10 || '', - disableSelfOptimization: settings.disableSelfOptimization ?? false, - eEfficiencyIndex: settings.eEfficiencyIndex ?? 2.5, - mEfficiencyIndex: settings.mEfficiencyIndex ?? 0.5, - ignoreRate: settings.ignoreRate ?? 0, - targetEliteNum: settings.targetEliteNum ?? 400, - targetMonsterNum: settings.targetMonsterNum ?? 2000, - priorityTags: settings.priorityTags ?? '', - excludeTags: settings.excludeTags ?? '', - curiosityFactor: settings.curiosityFactor ?? '0' - }); - - /* -------- 2. 关键词黑名单检查 -------- */ - const checkForbidden = (cfgStr) => { - FORBIDDEN.forEach(word => { - if (cfgStr.includes(word)) { - log.error(`路线选择与分组配置中包含关键词(${word}),强制终止!`); - throw new Error('ForbiddenWord'); - } + /* -------- 1. 构造 10 个分组标签 + 其它字段的默认值 -------- */ + const buildCfgObj = () => ({ + tagsForGroup1: settings.tagsForGroup1 || '', + tagsForGroup2: settings.tagsForGroup2 || '', + tagsForGroup3: settings.tagsForGroup3 || '', + tagsForGroup4: settings.tagsForGroup4 || '', + tagsForGroup5: settings.tagsForGroup5 || '', + tagsForGroup6: settings.tagsForGroup6 || '', + tagsForGroup7: settings.tagsForGroup7 || '', + tagsForGroup8: settings.tagsForGroup8 || '', + tagsForGroup9: settings.tagsForGroup9 || '', + tagsForGroup10: settings.tagsForGroup10 || '', + disableSelfOptimization: settings.disableSelfOptimization ?? false, + eEfficiencyIndex: settings.eEfficiencyIndex ?? 2.5, + mEfficiencyIndex: settings.mEfficiencyIndex ?? 0.5, + curiosityFactor: settings.curiosityFactor ?? '0', + ignoreRate: settings.ignoreRate ?? 0, + targetEliteNum: settings.targetEliteNum ?? 400, + targetMonsterNum: settings.targetMonsterNum ?? 2000, + priorityTags: settings.priorityTags ?? '', + excludeTags: settings.excludeTags ?? '' }); - }; - /* -------- 3. 主逻辑 -------- */ - if (settings.groupIndex === '路径组一') { - const cfg = buildCfgObj(); - const cfgStr = JSON.stringify(cfg, null, 2); - checkForbidden(cfgStr); + /* -------- 2. 关键词黑名单检查 -------- */ + const checkForbidden = (cfgStr) => { + FORBIDDEN.forEach(word => { + if (cfgStr.includes(word)) { + log.error(`路线选择与分组配置中包含关键词(${word}),强制终止!`); + throw new Error('禁止以汐酱或莫酱作为关键词'); + } + }); + }; - const filePath = `settings/${accountName}.json`; - file.writeText(filePath, cfgStr, false); - // 此时 UI 上字段已经在 settings 里,无需再回写 - } else { - let cfg; - try { - const raw = await file.readText(`settings/${accountName}.json`); - cfg = JSON.parse(raw); - } catch (e) { - log.error(`配置文件 settings/${accountName}.json 不存在,请先在"路径组一"运行一次!`); - await sleep(10000); - throw e; // 让外层决定是否需要 return / exit - } - /* 把读到的字段同步回全局 settings */ - for (const key in cfg) { - settings[key] = cfg[key]; + /* -------- 3. 主逻辑 -------- */ + if (settings.groupIndex === '路径组一') { + const cfg = buildCfgObj(); + const cfgStr = JSON.stringify(cfg, null, 2); + checkForbidden(cfgStr); + + const filePath = `settings/${accountName}.json`; + file.writeText(filePath, cfgStr, false); + } else { + let cfg; + try { + const raw = await file.readText(`settings/${accountName}.json`); + cfg = JSON.parse(raw); + } catch (e) { + log.error(`配置文件 settings/${accountName}.json 不存在或格式错误,请先在"路径组一"运行一次!`); + throw new Error('无可用的配置文件'); + } + /* 把读到的字段同步回全局 settings */ + for (const key in cfg) { + settings[key] = cfg[key]; + } } } + //加载自定义配置 + pickup_Mode = settings.pickup_Mode || "模板匹配拾取,拾取狗粮和怪物材料"; + dumpers = settings.activeDumperMode + ? settings.activeDumperMode.split(',').map(Number).filter(num => [1, 2, 3, 4].includes(num)) + : []; + + findFInterval = Math.max(16, Math.min(200, +settings.findFInterval || 100)); + checkDelay = Math.round(findFInterval / 2); + rollingDelay = (+settings.rollingDelay || 32); + pickupDelay = (+settings.pickupDelay || 100); + timeMove = (+settings.timeMove || 1000); + timeMoveUp = Math.round(timeMove * 0.45); + timeMoveDown = Math.round(timeMove * 0.55); + + priorityTags = (settings.priorityTags || "").split(",").map(tag => tag.trim()).filter(tag => tag.length > 0); + excludeTags = (settings.excludeTags || "").split(",").map(tag => tag.trim()).filter(tag => tag.length > 0); + if (!pickup_Mode.includes("模板匹配")) { + excludeTags.push("沙暴"); + log.warn("拾取模式不是模板匹配,无法处理沙暴路线,自动排除所有沙暴路线"); + } + + k1 = +settings.eEfficiencyIndex || 2.5; + k1 = Math.max(0, Math.min(10, Number.isNaN(k1) ? 2.5 : k1)); + + k2 = +settings.mEfficiencyIndex || 0.5; + k2 = Math.max(0, Math.min(4, Number.isNaN(k2) ? 0.5 : k2)); + + targetEliteNum = Math.max(0, +settings.targetEliteNum || 400) + 5; // 预留漏怪 + targetMonsterNum = Math.max(0, +settings.targetMonsterNum || 2000) + 25; // 预留漏怪 + + partyName = settings.partyName || ""; + groupSettings = Array.from({ length: 10 }, (_, i) => + settings[`tagsForGroup${i + 1}`] || (i === 0 ? '蕈兽' : '') + ); + groupTags = groupSettings.map(str => str.split(',').filter(Boolean)); + groupTags[0] = [...new Set(groupTags.flat())]; } /** @@ -299,6 +322,7 @@ async function processPathings(groupTags) { pathing.m = 0; // 普通怪物数量 pathing.e = 0; // 精英怪物数量 + pathing.original_e = 0; // 原始精英数量(用于统计被忽略的) pathing.mora_m = 0; // 普通怪物摩拉值 pathing.mora_e = 0; // 精英怪物摩拉值 @@ -313,7 +337,7 @@ async function processPathings(groupTags) { } else if (monster.type === "精英") { pathing.e += count; // 增加精英怪物数量 pathing.mora_e += count * 200 * monster.moraRate; // 增加精英怪物摩拉值 - + pathing.original_e += count; // 记录原始精英数量 } if (monster.moraRate > 1) { @@ -767,7 +791,7 @@ async function validateTeamAndConfig() { * 仅统计 group=1..10 且 selected 的路线,累加精英数、小怪数、总收益(G1)与总时长 * 输出每组的路线条数、精英/小怪数量、预计收益(摩拉)与预计用时(时:分:秒) * 用于“调试路线分配”模式快速核对各组工作量 - * 将汇总结果写入 pathingOut/路线分配结果.txt 文件 + * 将汇总结果写入 调试结果/路线分配结果.txt 文件 * 依赖全局:pathings */ async function printGroupSummary() { @@ -784,18 +808,20 @@ async function printGroupSummary() { const selectedPathings = pathings.filter(p => p.selected); resultText += `总选中路线数: ${selectedPathings.length} 条\n\n`; - let totalElites = 0, totalMonsters = 0, totalGain = 0, totalTime = 0; + let totalElites = 0, totalMonsters = 0, totalGain = 0, totalTime = 0, totalIgnoredElites = 0; for (let g = 1; g <= 10; g++) { const groupPath = pathings.filter(p => p.group === g && p.selected); if (groupPath.length === 0) continue; // 跳过空组 - let elites = 0, monsters = 0, gain = 0, time = 0; + let elites = 0, monsters = 0, gain = 0, time = 0, ignoredElites = 0; + for (const p of groupPath) { elites += p.e || 0; monsters += p.m || 0; gain += p.G1 || 0; time += p.t || 0; + ignoredElites += (p.original_e || 0) - (p.e || 0); } // 累加到总计 @@ -803,6 +829,7 @@ async function printGroupSummary() { totalMonsters += monsters; totalGain += gain; totalTime += time; + totalIgnoredElites += ignoredElites; const h = Math.floor(time / 3600); const m = Math.floor((time % 3600) / 60); @@ -819,6 +846,7 @@ async function printGroupSummary() { ` ${tagType}:【${groupTags}】`, ` 路线条数: ${groupPath.length}`, ` 精英怪数: ${elites.toFixed(0)}`, + ` 被忽视精英数: ${ignoredElites.toFixed(0)}`, ` 小怪数 : ${monsters.toFixed(0)}`, ` 预计收益: ${gain.toFixed(0)} 摩拉`, ` 预计用时: ${h} 时 ${m} 分 ${s.toFixed(0)} 秒`, @@ -846,6 +874,7 @@ async function printGroupSummary() { resultText += "总体统计:\n"; resultText += ` 总路线数: ${selectedPathings.length} 条\n`; resultText += ` 总精英怪: ${totalElites.toFixed(0)}\n`; + resultText += ` 被忽视精英怪数: ${totalIgnoredElites.toFixed(0)}\n`; resultText += ` 总小怪数: ${totalMonsters.toFixed(0)}\n`; resultText += ` 总收益 : ${totalGain.toFixed(0)} 摩拉\n`; resultText += ` 总用时 : ${totalH} 时 ${totalM} 分 ${totalS.toFixed(0)} 秒\n`; @@ -855,6 +884,7 @@ async function printGroupSummary() { resultText += "配置参数:\n"; resultText += ` 精英效率指数: ${settings.eEfficiencyIndex || 2.5}\n`; resultText += ` 小怪效率指数: ${settings.mEfficiencyIndex || 0.5}\n`; + resultText += ` 好奇系数: ${settings.curiosityFactor || 0}\n`; resultText += ` 忽略比例: ${settings.ignoreRate || 0}\n`; resultText += ` 目标精英数: ${settings.targetEliteNum || 400}\n`; resultText += ` 目标小怪数: ${settings.targetMonsterNum || 2000}\n`; @@ -862,7 +892,7 @@ async function printGroupSummary() { resultText += ` 排除标签: ${settings.excludeTags || ''}\n\n`; // 写入文件 - const filePath = "pathingOut/路线分配结果.txt"; + const filePath = "调试结果/路线分配结果.txt"; try { await file.writeText(filePath, resultText, false); log.info(`路线分配结果已保存至: ${filePath}`); @@ -874,7 +904,7 @@ async function printGroupSummary() { /** * 调试-按组导出路线文件 * 仅复制被选中的路线(selected===true)到本地调试目录 - * 输出结构:pathingOut/group{1..10}/原相对路径/文件名.json + * 输出结构:调试结果/group{1..10}/原相对路径/文件名.json * 用于“调试路线分配”模式,人工核对各组最终路线清单 * 依赖:file 读写接口、pathings(已有 selected & group) */ @@ -886,7 +916,7 @@ async function copyPathingsByGroup(pathings) { // 读取文件内容 const content = await file.readText(pathing.fullPath); // 构造目标路径 - const groupFolder = `pathingOut/group${pathing.group}`; + const groupFolder = `调试结果/group${pathing.group}`; const targetPath = `${groupFolder}/${pathing.fullPath}`; // 写入文件内容 await file.writeText(targetPath, content, false); @@ -1020,31 +1050,10 @@ async function runPath(fullPath, map_name, pm, pe) { const errorProcessTask = (async () => { let errorProcessCount = 0; - async function checkRo(recognitionObject) { - const maxAttempts = 1; - let attempts = 0; - let errorProcessGameRegion; - while (attempts < maxAttempts && state.running) { - try { - errorProcessGameRegion = captureGameRegion(); - const result = errorProcessGameRegion.find(recognitionObject); - errorProcessGameRegion.dispose(); - if (result.isExist()) { - return true; - } - } catch (error) { - log.error(`识别图像时发生异常: ${error.message}`); - if (!state.running) break; - return false; - } - attempts++; - } - return false; - } while (state.running) { if (errorProcessCount % 5 === 0) { //每约250毫秒进行一次冻结检测和白芙检测 - if (await checkRo(frozenRo)) { + if (await findAndClick(frozenRo, false, 2, 3)) { log.info("检测到冻结,尝试挣脱"); for (let m = 0; m < 3; m++) { keyPress("VK_SPACE"); @@ -1053,16 +1062,21 @@ async function runPath(fullPath, map_name, pm, pe) { continue; } if (!doFurinaSwitch) { - if (await checkRo(whiteFurinaRo)) { + if (await findAndClick(whiteFurinaRo, false, 2, 3)) { log.info("检测到白芙,本路线运行结束后切换芙宁娜形态"); doFurinaSwitch = true; continue; } } + if (await findAndClick(revivalRo, true, 2, 3)) { + log.info("识别到复苏按钮,点击"); + await sleep(500); + continue; + } } if (errorProcessCount % 100 === 0) { //每约5000毫秒进行一次烹饪检测 - if (await checkRo(cookingRo)) { + if (await findAndClick(cookingRo, false, 2, 3)) { log.info("检测到烹饪界面,尝试脱离"); keyPress("VK_ESCAPE"); await sleep(500); @@ -1070,7 +1084,7 @@ async function runPath(fullPath, map_name, pm, pe) { } } errorProcessCount++; - await sleep(45); + await sleep(50); } })(); @@ -1361,7 +1375,7 @@ async function dumper(pathFilePath, map_name) { while (state.running) { //log.info("调试-泥头车循环"); await sleep(501); - if (await isMainUI()) { + if (await isMainUI() && !await findAndClick(flyingRo, false, 2, 3)) { //log.info("调试-获取坐标"); //在主界面才尝试获取坐标 let dumperDistance = 0; @@ -1596,7 +1610,6 @@ async function processPathingsByGroup(pathings, accountName) { //log.info(`当前位于${pathing.map_name}地图的(${miniMapPosition.X},${miniMapPosition.Y},距离上次距离${(diffX + diffY)}`); } catch (error) { log.error(`获取坐标时发生错误:${error.message}`); - runningFailCount++; } // 调用 runPath 函数处理路径 @@ -1635,7 +1648,7 @@ async function processPathingsByGroup(pathings, accountName) { } } } catch (e) { /* 读文件失败就留 0,0 继续走后面逻辑 */ } - + let coordAbnormal = false; try { await genshin.returnMainUi(); const miniMapPosition = await genshin.getPositionFromMap(pathing.map_name); @@ -1648,17 +1661,17 @@ async function processPathingsByGroup(pathings, accountName) { lastY = miniMapPosition.Y; if ((diffX + diffY) < 5 || (endDiffX + endDiffY) > 30) { - runningFailCount++; - } else { - runningFailCount = 0; + coordAbnormal = true; } } catch (error) { log.error(`获取坐标时发生错误:${error.message}`); - runningFailCount++; + coordAbnormal = true; } - - if (runningFailCount >= 1 && !settings.disableXYCheck) { - log.error("出发点与终点过于接近,终点偏差大于30,或坐标获取异常,不记录运行数据"); + await genshin.returnMainUi(); + let mainUiRes = await isMainUI(2000); + if ((coordAbnormal && settings.enableCoordCheck) && !mainUiRes) { + log.error("路线未正常完成、坐标获取异常或不处于主界面,不记录运行数据"); + notification.send(`路线${pathing.fileName}:路线未正常完成、坐标获取异常或不处于主界面,不记录运行数据`); continue; } @@ -1818,11 +1831,14 @@ async function switchPartyIfNeeded(partyName) { } } -async function isMainUI() { - const maxAttempts = 1; - let attempts = 0; +/** + * 判断当前是否位于主界面 + * @param {number} maxDuration 最大允许耗时(毫秒) + */ +async function isMainUI(maxDuration = 10) { + const start = Date.now(); let dodispose = false; - while (attempts < maxAttempts && state.running) { + while (Date.now() - start < maxDuration) { if (!gameRegion) { gameRegion = captureGameRegion(); dodispose = true; @@ -1832,15 +1848,15 @@ async function isMainUI() { if (result.isExist()) return true; } catch (error) { log.error(`识别图像时发生异常: ${error.message}`); - if (!state.running) break; - return false; + return false; // 一旦出现异常直接退出,不再重试 } - attempts++; - await sleep(checkDelay); + await sleep(checkDelay); // 识别间隔 if (dodispose) { gameRegion.dispose(); + dodispose = false; // 已经释放,标记避免重复 dispose } } + /* 超时仍未识别到,返回失败 */ return false; } @@ -2021,9 +2037,9 @@ async function fakeLog(name, isStart, duration = 0, isJs = false) { /* ---------------- 时间格式化 ---------------- */ const t = new Date(currentTime); - const hh = String(t.getHours()).padStart(2, '0'); - const mm = String(t.getMinutes()).padStart(2, '0'); - const ss = String(t.getSeconds()).padStart(2, '0'); + const hh = String(t.getHours()).padStart(2, '0'); + const mm = String(t.getMinutes()).padStart(2, '0'); + const ss = String(t.getSeconds()).padStart(2, '0'); const msec = String(t.getMilliseconds()).padStart(3, '0'); const formattedTime = `${hh}:${mm}:${ss}.${msec}`; diff --git a/repo/js/AutoHoeingOneDragon/manifest.json b/repo/js/AutoHoeingOneDragon/manifest.json index b1253728c..b92ad16f2 100644 --- a/repo/js/AutoHoeingOneDragon/manifest.json +++ b/repo/js/AutoHoeingOneDragon/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "锄地一条龙", - "version": "2.0.2", + "version": "2.0.3", "description": "一站式解决自动化锄地,支持只拾取狗粮,请仔细阅读README.md后使用", "authors": [ { diff --git a/repo/js/AutoHoeingOneDragon/settings.json b/repo/js/AutoHoeingOneDragon/settings.json index f099402c1..fd8e001bb 100644 --- a/repo/js/AutoHoeingOneDragon/settings.json +++ b/repo/js/AutoHoeingOneDragon/settings.json @@ -102,9 +102,9 @@ "label": "运行路线时输出交互或拾取精英和小怪数量,便于在日志分析中比对" }, { - "name": "disableXYCheck", + "name": "enableCoordCheck", "type": "checkbox", - "label": "路线结尾时不再进行坐标检查\n用于某些路线稳定获取不到坐标时也正常记录cd\n【注意】可能导致部分路线卡死后无法重跑" + "label": "路线结尾时进行坐标检查\n用于在路线出现卡死等放弃时不记录cd信息" }, { "name": "skipCheck",