From 163ed20ec14dfcee36cf6df4162cd838a3affc2f Mon Sep 17 00:00:00 2001 From: LX666-666 Date: Sat, 24 May 2025 16:12:27 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=A7=94=E6=89=98v0.95=20(#9?= =?UTF-8?q?14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/RecognitionObject/Completed.png | Bin 0 -> 1612 bytes .../Data/RecognitionObject/UnCompleted.png | Bin 0 -> 1513 bytes .../Data/RecognitionObject/paimon_menu.png | Bin 0 -> 2372 bytes repo/js/AutoEntrust/README.md | 7 +- .../assets/process/冒险家安娜/process.json | 6 + .../冒险家安娜/冒险家,安娜!-蒙德城-1.json | 31 + .../冒险家安娜/冒险家,安娜!-蒙德城-2.json | 49 + .../冒险家安娜/冒险家,安娜!-蒙德城-3.json | 77 + .../assets/process/父亲能做的事/process.json | 6 + .../父亲能做的事/父亲能做的事-晨曦酒庄-1.json | 112 ++ .../父亲能做的事/父亲能做的事-晨曦酒庄-2.json | 67 + .../父亲能做的事/父亲能做的事-晨曦酒庄-3.json | 112 ++ .../assets/process/诗歌交流/process.json | 6 + .../process/诗歌交流/诗歌交流-坠星山谷-1.json | 67 + .../process/诗歌交流/诗歌交流-坠星山谷-2.json | 40 + .../process/诗歌交流/诗歌交流-坠星山谷-3.json | 31 + .../assets/process/语言交流/语言交流-1.json | 8 +- .../assets/process/语言交流/语言交流-2.json | 8 +- .../assets/process/语言交流/语言交流-3.json | 12 +- .../assets/process/语言交流/语言交流-4.json | 4 +- .../assets/process/说到做到/process.json | 1 - .../assets/process/说到做到/说到做到-1.json | 16 +- .../assets/process/说到做到/说到做到-2.json | 10 +- .../process/那位先生的委托/process.json | 5 + .../那位先生的委托-蒙德城-1.json | 49 + .../那位先生的委托-蒙德城-2.json | 76 + .../那位先生的委托-蒙德城-3.json | 49 + .../钥匙是易丢物品/晨曦酒庄/process.json | 4 + .../晨曦酒庄/钥匙是易丢物品-晨曦酒庄-1.json | 68 + .../晨曦酒庄/钥匙是易丢物品-晨曦酒庄-2 .json | 193 +++ .../钥匙是易丢物品/达达乌帕谷/process.json | 4 + .../达达乌帕谷/钥匙是易丢物品-达达乌帕谷-1.json | 58 + .../达达乌帕谷/钥匙是易丢物品-达达乌帕谷-2.json | 178 +++ .../(上不了塔)新的风暴尚未出现/process.json | 7 + .../新的风暴尚未出现-1.json | 84 ++ .../新的风暴尚未出现-2.json | 80 + .../新的风暴尚未出现-3.json | 30 + .../assets/持盾的危机/千风神殿-1.json | 125 ++ .../assets/邪恶的扩张/摘星崖-1.json | 39 + repo/js/AutoEntrust/main.js | 1310 ++++++++++------- repo/js/AutoEntrust/manifest.json | 8 +- repo/js/AutoEntrust/name.json | 23 + repo/js/AutoEntrust/settings.json | 10 +- repo/js/AutoEntrust/对话委托制做方法.txt | 59 + 44 files changed, 2537 insertions(+), 592 deletions(-) create mode 100644 repo/js/AutoEntrust/Data/RecognitionObject/Completed.png create mode 100644 repo/js/AutoEntrust/Data/RecognitionObject/UnCompleted.png create mode 100644 repo/js/AutoEntrust/Data/RecognitionObject/paimon_menu.png create mode 100644 repo/js/AutoEntrust/assets/process/冒险家安娜/process.json create mode 100644 repo/js/AutoEntrust/assets/process/冒险家安娜/冒险家,安娜!-蒙德城-1.json create mode 100644 repo/js/AutoEntrust/assets/process/冒险家安娜/冒险家,安娜!-蒙德城-2.json create mode 100644 repo/js/AutoEntrust/assets/process/冒险家安娜/冒险家,安娜!-蒙德城-3.json create mode 100644 repo/js/AutoEntrust/assets/process/父亲能做的事/process.json create mode 100644 repo/js/AutoEntrust/assets/process/父亲能做的事/父亲能做的事-晨曦酒庄-1.json create mode 100644 repo/js/AutoEntrust/assets/process/父亲能做的事/父亲能做的事-晨曦酒庄-2.json create mode 100644 repo/js/AutoEntrust/assets/process/父亲能做的事/父亲能做的事-晨曦酒庄-3.json create mode 100644 repo/js/AutoEntrust/assets/process/诗歌交流/process.json create mode 100644 repo/js/AutoEntrust/assets/process/诗歌交流/诗歌交流-坠星山谷-1.json create mode 100644 repo/js/AutoEntrust/assets/process/诗歌交流/诗歌交流-坠星山谷-2.json create mode 100644 repo/js/AutoEntrust/assets/process/诗歌交流/诗歌交流-坠星山谷-3.json create mode 100644 repo/js/AutoEntrust/assets/process/那位先生的委托/process.json create mode 100644 repo/js/AutoEntrust/assets/process/那位先生的委托/那位先生的委托-蒙德城-1.json create mode 100644 repo/js/AutoEntrust/assets/process/那位先生的委托/那位先生的委托-蒙德城-2.json create mode 100644 repo/js/AutoEntrust/assets/process/那位先生的委托/那位先生的委托-蒙德城-3.json create mode 100644 repo/js/AutoEntrust/assets/process/钥匙是易丢物品/晨曦酒庄/process.json create mode 100644 repo/js/AutoEntrust/assets/process/钥匙是易丢物品/晨曦酒庄/钥匙是易丢物品-晨曦酒庄-1.json create mode 100644 repo/js/AutoEntrust/assets/process/钥匙是易丢物品/晨曦酒庄/钥匙是易丢物品-晨曦酒庄-2 .json create mode 100644 repo/js/AutoEntrust/assets/process/钥匙是易丢物品/达达乌帕谷/process.json create mode 100644 repo/js/AutoEntrust/assets/process/钥匙是易丢物品/达达乌帕谷/钥匙是易丢物品-达达乌帕谷-1.json create mode 100644 repo/js/AutoEntrust/assets/process/钥匙是易丢物品/达达乌帕谷/钥匙是易丢物品-达达乌帕谷-2.json create mode 100644 repo/js/AutoEntrust/assets/process/(上不了塔)新的风暴尚未出现/process.json create mode 100644 repo/js/AutoEntrust/assets/process/(上不了塔)新的风暴尚未出现/新的风暴尚未出现-1.json create mode 100644 repo/js/AutoEntrust/assets/process/(上不了塔)新的风暴尚未出现/新的风暴尚未出现-2.json create mode 100644 repo/js/AutoEntrust/assets/process/(上不了塔)新的风暴尚未出现/新的风暴尚未出现-3.json create mode 100644 repo/js/AutoEntrust/assets/持盾的危机/千风神殿-1.json create mode 100644 repo/js/AutoEntrust/assets/邪恶的扩张/摘星崖-1.json create mode 100644 repo/js/AutoEntrust/name.json create mode 100644 repo/js/AutoEntrust/对话委托制做方法.txt diff --git a/repo/js/AutoEntrust/Data/RecognitionObject/Completed.png b/repo/js/AutoEntrust/Data/RecognitionObject/Completed.png new file mode 100644 index 0000000000000000000000000000000000000000..f5786f87e69e3ba825b982185600a6921c0110ad GIT binary patch literal 1612 zcmV-S2DABzP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1>s3VK~z{r%~)Gc zTvZhQ&fJH&albW%TA>t#q-srTq^XzCSdGybAAK+}@fY|{eDgu0QKME9)1)?v;zz<=zNo%nX}KHnf2}Ut;;^j^2~&a5=Kf*B9K_Y zTzHf|C(z>RhSlKE?EyE_bD_vo+sm-bVtz#N|k^rFW&iaGJY{~ z2aWblI89ah{R~Rh0~_&0TwnbX4}$|ZM8+SochR0}yPk~_BcLLw#<3n>!jSI=%tyvi zZ)-=Z>ntpWGJQ=hN``=n6k@3e79zK4=fA+cKpz~&Dzv#@fyZ1=D>AyzMu`wmkw`@` z9lC)V-tRCU9*527#&P#+x^%WSvm^*4Dcd{<_G5VMCoD(rNdUJvyK#)9V^Xa8s{AMk z0uO@MG3fiAQsuPd{aWkO==8jimG8I4|42YZBJkB$TgUZG~+KaBg0U&Ek&#H!MZ($e$1T zF5s^JD&pkw-NH`>_j|v=g#U_ofdiKR#cZ%q);WU)TL%n^QCnOfwk5zpHR=CdIzg_G zj3E7SzxQh*)k}pZEOR@7H-gh+@1swMd@lv>22G~Dr%={u742ILc3Hn0Y)OC(o(lD2 zk}6_2xh}hvN@>F%U&h$_&s1>6bYj_Z$_#$Rsk+XFZsM=iujqYPB$Hy<5GjV+T!&{X z-bJ~&fvyVEeKztEU}G1lq>qx3{=_4FmY`&Q+($Y4Hf52;$OMsKDLjcJ#mZdxCiOKJ zFcTgYvaEM)_aUuum*-6!E;~V2yE8nEyaYHWbC%%r#yz=)+|WuPDTQZj{W8g8Qg$^P z2{LvzG$LtoK5|R8Iz(Err>1bN7pXl+UjQ#BkYOC)1uYrM4RhXYL(k4 z>*N(d!N@rivs~C1?oEOTFE$d3lscEh6MRp0k+EqHnMMZiNvYP_f(pxFij6tyDhH+b zrFWb^q`zxcY-snK!*iAIQM*tra~g6<7K*%wdiF#t6%wPl22A?<;ET^nM#)&Ez+|w9 z1UML3dh8Ji)Qsgi^lMc?gS`XYH6NnRcAV*J^ZP(v{}~lC>_!h9rb^N2GoIH`X=$Rs z(^^~pV=g{6gIliIOM&=`u5}7#(K{QR9vbW>H#$5$lJ|>YJY&blD!iLqcxEXb-VgjC z6*Nj+WL%Bt^&ug(AuqCQG!glBR}WsS{(wBj$HV`KKpJc)8_Y*Oj*;omkVr4dzPWg%soEe zFOKnyUFR@1qSJj|(jXtYbBx*4z6RDgpQ?^JUqW-)NxIr!%FKsd{~{YvX>G!Z@^@&w zO$Dh*^8)R>_O34^aF1VaZ^tp`SvsV1k6(Q3X-k-di?rN9vN%=wKAIgbNLJ#(87wU= z?(y09YU>gDPVr=ujXnF6Nw7SQI8NbA%}0`M`7pu{pcL|k#psa7fz6JSLh7KfX98*P z0mMVCaHr=y6{j|;ALT^MC3S;4%|f0m><2}9yiVOc6)-*`&V>J#MzE18E|n$I0000< KMNUMnLSTYEnEQAD literal 0 HcmV?d00001 diff --git a/repo/js/AutoEntrust/Data/RecognitionObject/UnCompleted.png b/repo/js/AutoEntrust/Data/RecognitionObject/UnCompleted.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd30f70838392f920c69c19fe252e6e1d775d56 GIT binary patch literal 1513 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1%63HK~z{r?U>n5 z8&?#@4_@(ZV=x%wg~e=1fF_}B+9FlTL*Lpy_o07frRJehL>^L_G$9ItG=w!_wZYgd zw(;8YU5~{ucF8jO(#J^_nxXu8mP(}}>GcLOTWr*BbCAVmr>;PldP0NbVzDN(MfxP|XcCvt zoeL{%tuE2d`Z6WKd4Ro6u$#l%*od44h~RXbX%2 zwESj{))rsWc_vNeQb~GO8xR(q{F1_>lVo$aq}2-e@-`L0bpxVYE>SYRN6Yhn($b4( zls!9bS{T^-xJrwB|Jvd!Di#Zx^SG|Kvs98c-n|i4yDrK2E`QEnw6*e%!=WIp)~Bvp z-@ks&C!VD^Cx8;8*-UP4H~IUAMQkC);PS)0E!zLIMyJVywDSUUyM_FbD1GNfrJmwo9o*OeBOHSPa5O?mwak2Y5%9pR9H#mktNl zI4^Z`@DEK))9Ax*$>H&m-e8dKevTeks6;(PS2FOCER4peoL6v68`Hl>WLP%K;6mHUfuApQO0FoKdN8QP&{aU^Mc1 zt{zB(#_p*!U5=@ZG)NZr-;CCg6Kxesq&(!%pU*cwOD zY6XbLKxyIbV0U>qmh{4f5QynTStE@mljt7PkAGsX1T~eHy5iubL~Msgra4-AIZFi; z&da-ns|;)fgl3VgCl7u(U4amdJ)EKF)YmnuKrK|=3h>2fSK+Y_i%F`x&IAkuZMK{=Bhp1-^)EVG%~>=JET=@)U_Od z^(mj;F9z z&@7I>O*HXg4rny=Mj#xpx0fenJSw(=W^r%`8a~QFX!tI9eE!NCH9(>D^@VAOpST=6 z#neh9Eoc*mSdlKo9K;1V^DrDJ!8GIQ@Uhh%T@>5u{Y|3Xtui{e??NErPdRkr86hw@ zN&|NvG*vcg(ULgO6^MwUig>DKp`ObO9$cc6Ux|*;inZjlEDi$!hJ*3x8Bxms;<111 z3sDzuf|kX>CRV$P2JcOC4Xkt!xDuV9(es5?8vd`O5t!ZZ(2b`lj93^X1;V4(bL+Lm zT|zcA)TGH6yczpn1B_T|+^o22JyW}CU{(A}OwNC9xq5sS*D9aIwFc3@%5D^CYNol4 P00000NkvXXu0mjfo|)AD literal 0 HcmV?d00001 diff --git a/repo/js/AutoEntrust/Data/RecognitionObject/paimon_menu.png b/repo/js/AutoEntrust/Data/RecognitionObject/paimon_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..c424325b17a4956f7802352e7609bfcff2721afa GIT binary patch literal 2372 zcmV-K3A^@*P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2-!(QK~z{rt(SRF zR96@7{OrIrs3OB6ST- zP@DTTKA&cd-_x`4ifbQ;v^!UEWYd=r=ft5o>jaWdZ$#*F1Dp=`Lw#i>UdiRG$$NWQ zlS!qJbxWbLunJ)v#?D#+4Ko*L&U1#Kww~+hpIblw8}X6L@I3V>qTOd=&jwpmmE@tP zN6PlU71k%NS0-b{sVwYPN+w3?*E%@$g9jWc1ShNWOUqVas)}Z=D&g zor^(ddmGo&$7^Bli&I(H`jA5yHPeBFc|eOy)65lLn65z7v3Rt#cd}#T%%3oS_UaWY zP8XsoYg`}%_!_r5rlgw?!gfco5(B+3n1mL!af6==BzpXu8{&uT3qW6mS~El zkk8E=nsSFID@2ckOt;?zeTP8kFWU_L<(r{v?++`_P}J1FJ zrGzD=Wn-M71IC)Hz~luUn6cEC1O>prF&HzQw!v`aH!xiFElga#ftK|K6j!LeQ~fcd zu+pkpT)duyod@HvcufdKo4P~8+8;*FJ6JPb9Y(ejI`+W`*mDLlI_jSS1!4JxCG0UD zjGX)uI0x;=R2zR7yM)5TEgZ8vA~DL$8>v}OxltdFg0S1arJ|~;ihCNEj?QkRP&zVj z41%F+IE+Xf^I2P(TeuM)kAkquN&(Ks|A_jR&0Ndi6qZ$E%2INE=P)evj^tWD4GO}l zs|660<>Kz0L`bDQTlvJ_ zd*fhAC$o!g1)mB9VKP}S5-webP*{r6(tI2{eg@gOg$bAczCL=vv$<53XC zkeQu}q9VFy$(tdpeg^M=ojCvFHMF#d*ud8xObT(KlbXT*a1qj;9&~keb5F|ifiUJp zNhv9icK(5G$#b-d>yYrnMFa##prLW#L-Bw@#s88sxTm*NR@Wfn@W}xeH4txvNhA_- zcq#jW+`Iw^Y1d>OFVNK}fV`&#XU@hWEkpUF{2C@MnmesS7C)bBQI0pl_)qhZ`REaH zbDpBOT!@R8ZXho^3q6ur^t9F?BQ1rEbE?8D^!SPMuy^t0T9ku#bS|6c^{Bu}MgWpb_a$GGT7-OE0cORWTp;=~E8}W|#CW zR${z~Eqp`wL7jee<~rcixt~x{E}+8X!^F>HL`^VR!V1gg=di+jLJq*rB>)Q@{g7Lj zN99xl$_gIiNnRc|O*L9+cNVVl#e~`R*dBe9y@5R87vNS>8Xjb1<5}5<`I$FqjvfA{ zJ{#fD2dJ13WAB0QFoW(@F(=Q`N}ACvu7 z94CBkV1wTuWpOIxN6it998EI_F8V*b>3*K`Kbcg3(3`{ZXAAT4bR7q)3 zi8tS63pzXdYS4k;sTtc~=+|VZ=M}SYR<;_3-Qba3-?kI z@t^~J`z(Eg++aT0lFm2#AOF&x$eXiTPDp7 z;rzyS?wDrzH8d>N!Q5>Jw(UO$fv{#+$=c8<2qR6M$*06z3(!p0ZLKAKm_<8f>KcaW zD?;EHxOd1!^DJDqu(_;R)XLV2!$I8I4&R6q7;oW)^}CL8txPKF!PbNS!W0rV$88s8 zyGKB6kw0#xq;n$%hBwJN2#F7dP7#9<6del#`#`8ISPy>D+c2;{HUVl@8(`$M2PW>3 z7-Q~*9Wn7r`7O`VI~H~U2hk!@J|r+OaV_~#E-csV!lVV>RK9J;^`s2e1q2j4nK*SX zGZ)dZKfxvV0K9gdz}34C(5Yl$!kg!F3Wgp>VC)%%!qQ5vd2ljv^5GwL7@MQMN9={` zxc~SmS}1mjByBi&DgoNon-Ca%l1BE@0_1GH$rL%t!bs} 返回 "completed", "uncompleted", 或 "unknown" + */ +async function detectCommissionStatusByImage(buttonIndex) { + try { + const button = COMMISSION_DETAIL_BUTTONS[buttonIndex]; + if (!button) { + log.error("无效的按钮索引: {index}", buttonIndex); + return "unknown"; + } + + log.info("检测委托{id}的完成状态(图像识别)", button.id); + + // 截图 + let captureRegion = captureGameRegion(); + + // 检测区域:按钮位置左右各扩展更大范围 + const checkRegion = captureRegion.deriveCrop( + button.checkX, + button.y - 30, // 稍微向上扩展检测区域 + button.checkWidth, + 60 // 增加高度以确保捕获状态图标 + ); + + + // 加载完成和未完成的模板图像 + let completedTemplate, uncompletedTemplate; + + try { + completedTemplate = file.readImageMatSync(COMPLETED_IMAGE_PATH); + uncompletedTemplate = file.readImageMatSync(UNCOMPLETED_IMAGE_PATH); + } catch (imageError) { + log.error("加载模板图像失败: {error}", imageError); + return "unknown"; + } + + // 创建识别对象,使用更灵活的参数 + const completedRo = RecognitionObject.TemplateMatch(completedTemplate, 0, 0, button.checkWidth, 60); + const uncompletedRo = RecognitionObject.TemplateMatch(uncompletedTemplate, 0, 0, button.checkWidth, 60); + + // 降低匹配阈值,提高识别灵活性 + completedRo.threshold = 0.65; + uncompletedRo.threshold = 0.65; + + // 检测完成状态 + const completedResult = checkRegion.find(completedRo); + if (!completedResult.isEmpty()) { + log.info("委托{id}已完成", button.id); + return "completed"; + } + + // 检测未完成状态 + const uncompletedResult = checkRegion.find(uncompletedRo); + if (!uncompletedResult.isEmpty()) { + log.info("委托{id}未完成", button.id); + return "uncompleted"; + } + + // 尝试使用更低的阈值再次检测 + log.info("使用更低阈值再次检测委托{id}状态", button.id); + completedRo.threshold = 0.6; + uncompletedRo.threshold = 0.6; + + const completedResult2 = checkRegion.find(completedRo); + if (!completedResult2.isEmpty()) { + log.info("委托{id}已完成(低阈值检测)", button.id); + return "completed"; + } + + const uncompletedResult2 = checkRegion.find(uncompletedRo); + if (!uncompletedResult2.isEmpty()) { + log.info("委托{id}未完成(低阈值检测)", button.id); + return "uncompleted"; + } + + log.warn("委托{id}状态识别失败", button.id); + return "unknown"; + + } catch (error) { + log.error("检测委托完成状态时出错: {error}", error); + return "unknown"; + } +} // 读取支持的委托列表 function loadSupportedCommissions() { - let supportedCommissions = []; + let supportedCommissions = { + fight: [], + talk: [] + }; try { // 使用正确的文件读取方法 @@ -71,10 +169,17 @@ const supportListContent = file.readTextSync(SUPPORT_LIST_PATH); if (supportListContent && supportListContent.trim()) { - supportedCommissions = supportListContent.split('\n') - .map(line => line.trim()) - .filter(line => line.length > 0); - log.info("已加载支持的委托列表,共 {count} 个", supportedCommissions.length); + try { + // 解析JSON格式 + const commissionData = JSON.parse(supportListContent); + supportedCommissions.fight = commissionData.fight || []; + supportedCommissions.talk = commissionData.talk || []; + + log.info("已加载支持的战斗委托列表,共 {count} 个", supportedCommissions.fight.length); + log.info("已加载支持的对话委托列表,共 {count} 个", supportedCommissions.talk.length); + } catch (jsonError) { + log.error("解析委托列表JSON失败: {error}", jsonError); + } } else { log.warn("支持的委托列表为空"); } @@ -84,7 +189,13 @@ // 尝试创建文件 try { - const writeResult = file.writeTextSync(SUPPORT_LIST_PATH, ""); + // 创建默认的JSON结构 + const defaultJson = JSON.stringify({ + fight: [], + talk: [] + }, null, 2); + + const writeResult = file.writeTextSync(SUPPORT_LIST_PATH, defaultJson); if (writeResult) { log.info("已创建空的委托列表文件"); } else { @@ -328,165 +439,208 @@ } } - // 原神每日委托识别主函数 - async function Identification() { - try { - log.info("开始执行原神每日委托识别脚本"); - await genshin.returnMainUi(); - setGameMetrics(1920, 1080, 1); - // 加载支持的委托列表 - const supportedCommissions = loadSupportedCommissions(); - - // 确保所有委托的资源目录存在 - for (const commission of supportedCommissions) { - ensureDirectoryExists(`assets/${commission}`); - } - // 进入委托界面 - const enterSuccess = await enterCommissionScreen(); - if (!enterSuccess) { - log.error("无法进入委托界面,脚本终止"); - return; - } - await sleep(1000); // 增加延迟,确保界面完全加载 - - // 步骤1: 执行第一次OCR识别 - log.info("步骤1: 执行第一次OCR识别"); - const ocrRo = RecognitionObject.Ocr( - OCR_REGION_X, - OCR_REGION_Y, - OCR_REGION_WIDTH, - OCR_REGION_HEIGHT - ); - - await sleep(2000); // 等待界面稳定 - - // 第一次截图识别 - log.info("执行第一次OCR识别 ({x}, {y}) ({width}, {height})", - OCR_REGION_X, OCR_REGION_Y, OCR_REGION_X + OCR_REGION_WIDTH, OCR_REGION_Y + OCR_REGION_HEIGHT); - let captureRegion = captureGameRegion(); - let firstResults = captureRegion.findMulti(ocrRo); - log.info("第一次OCR识别结果数量: {count}", firstResults.count); - - // 处理第一次识别结果 - let firstCommissions = []; - for (let i = 0; i < firstResults.count; i++) { - let result = firstResults[i]; - let text = cleanText(result.text); - if (text && text.length >= minTextLength) { - log.info("第{index}个委托: \"{text}\"", i + 1, text); - firstCommissions.push({ - id: i + 1, - name: text, - supported: supportedCommissions.includes(text), - location: "" - }); - } - } - - // 步骤2: 点击1、2、3号委托详情按钮 - log.info("步骤2: 点击1、2、3号委托详情按钮"); - for (let i = 0; i < Math.min(3, firstCommissions.length); i++) { - const commission = firstCommissions[i]; - log.info("查看第{id}个委托详情: {name}", commission.id, commission.name); - // 点击详情按钮 - const detailButton = COMMISSION_DETAIL_BUTTONS[commission.id - 1]; - log.info("点击委托详情按钮 ({x}, {y})", detailButton.x, detailButton.y); - click(detailButton.x, detailButton.y); - await sleep(2500); // 等待详情页面加载 - - // 检测是否成功进入详情界面 - const detailStatus = await checkDetailPageEntered(); - log.info(`委托详情界面状态: ${detailStatus}`); - - // 根据检测结果处理 - if (detailStatus === "已完成") { - log.info("该委托已完成,跳过地点识别和退出操作"); - commission.location = "已完成"; - continue; // 跳过后续的地点识别和退出操作 - } else if (detailStatus === "未知" || detailStatus === "错误") { - log.warn("无法确认是否进入详情界面,尝试继续执行"); - // 尝试识别委托地点 - const location = recognizeCommissionLocation(); - commission.location = location; - log.info("委托 {name} 的地点: {location}", commission.name, location); - } else { - // 成功进入详情界面,记录地点 - const location = recognizeCommissionLocation(); - commission.location = location; - log.info("委托 {name} 的地点: {location}", commission.name, location); - } - - // 如果不是已完成状态,需要执行退出操作 - if (detailStatus !== "未知") { - // 退出详情页面 - log.info("退出详情页面 - 按ESC"); - keyDown("VK_ESCAPE"); - await sleep(300); - keyUp("VK_ESCAPE"); - await sleep(1200); - - keyDown("VK_ESCAPE"); - await sleep(300); - keyUp("VK_ESCAPE"); - await sleep(1200); - } - } - - // 步骤3: 执行下滑操作 - log.info("步骤3: 执行下滑操作"); - await PageScroll(1); - await sleep(2000); // 等待滑动完成 - - // 步骤4: 执行第二次OCR识别 - log.info("步骤4: 执行第二次OCR识别({x}, {y}) ({width}, {height})", + +/** + * 优化后的委托识别主函数 + */ +async function Identification() { + try { + log.info("开始执行原神每日委托识别脚本"); + await genshin.returnMainUi(); + setGameMetrics(1920, 1080, 1); + + // 加载支持的委托列表 + const supportedCommissions = loadSupportedCommissions(); + + // 确保所有委托的资源目录存在 + for (const commission of supportedCommissions.fight) { + ensureDirectoryExists(`assets/${commission}`); + } + for (const commission of supportedCommissions.talk) { + ensureDirectoryExists(`assets/process/${commission}`); + } + + // 进入委托界面 + const enterSuccess = await enterCommissionScreen(); + if (!enterSuccess) { + log.error("无法进入委托界面,脚本终止"); + return; + } + await sleep(1000); + + // 步骤1: 执行第一次OCR识别 + log.info("步骤1: 执行第一次OCR识别"); + const ocrRo = RecognitionObject.Ocr( + OCR_REGION_X, + OCR_REGION_Y, + OCR_REGION_WIDTH, + OCR_REGION_HEIGHT + ); + + await sleep(2000); + + // 第一次截图识别 + log.info("执行第一次OCR识别 ({x}, {y}) ({width}, {height})", OCR_REGION_X, OCR_REGION_Y, OCR_REGION_X + OCR_REGION_WIDTH, OCR_REGION_Y + OCR_REGION_HEIGHT); - captureRegion = captureGameRegion(); - let secondResults = captureRegion.findMulti(ocrRo); - log.info("第二次OCR识别结果数量: {count}", secondResults.count); - - - // 处理第二次识别结果 - let fourthCommission = null; - for (let i = 0; i < secondResults.count; i++) { - let result = secondResults[i]; - let text = cleanText(result.text); - if (text && text.length >= minTextLength) { - log.info("第4个委托: \"{text}\"", text); - fourthCommission = { - id: 4, - name: text, - supported: supportedCommissions.includes(text), - location: "" - }; - // 移除break,继续循环,取最后一个有效结果 - } + let captureRegion = captureGameRegion(); + let firstResults = captureRegion.findMulti(ocrRo); + log.info("第一次OCR识别结果数量: {count}", firstResults.count); + + // 处理第一次识别结果 + let firstCommissions = []; + for (let i = 0; i < firstResults.count; i++) { + let result = firstResults[i]; + let text = cleanText(result.text); + if (text && text.length >= minTextLength) { + log.info("第{index}个委托: \"{text}\"", i + 1, text); + + // 检查委托类型 + const isFightCommission = supportedCommissions.fight.includes(text); + const isTalkCommission = supportedCommissions.talk.includes(text); + const isSupported = isFightCommission || isTalkCommission; + const commissionType = isFightCommission ? COMMISSION_TYPE.FIGHT : + (isTalkCommission ? COMMISSION_TYPE.TALK : ""); + + firstCommissions.push({ + id: i + 1, + name: text, + supported: isSupported, + type: commissionType, + location: "" + }); } + } + + // 步骤2: 使用图像识别检测前3个委托的完成状态 + log.info("步骤2: 检测前3个委托的完成状态"); + for (let i = 0; i < Math.min(3, firstCommissions.length); i++) { + const commission = firstCommissions[i]; + + // 使用图像识别检测完成状态 + const status = await detectCommissionStatusByImage(i); + + if (status === "completed") { + log.info("委托{id} {name} 已完成,跳过详情查看", commission.id, commission.name); + commission.location = "已完成"; + continue; + } else if (status === "uncompleted") { + log.info("委托{id} {name} 未完成,查看详情", commission.id, commission.name); + } else { + log.warn("委托{id} {name} 状态未知,尝试查看详情", commission.id, commission.name); + } + + // 只有未完成或状态未知的委托才点击查看详情 + log.info("查看第{id}个委托详情: {name}", commission.id, commission.name); + + // 点击详情按钮 + const detailButton = COMMISSION_DETAIL_BUTTONS[commission.id - 1]; + log.info("点击委托详情按钮 ({x}, {y})", detailButton.x, detailButton.y); + click(detailButton.x, detailButton.y); + await sleep(2500); + + // 检测是否成功进入详情界面 + const detailStatus = await checkDetailPageEntered(); + log.info(`委托详情界面状态: ${detailStatus}`); + + // 根据检测结果处理 + if (detailStatus === "已完成") { + log.info("该委托已完成,跳过地点识别和退出操作"); + commission.location = "已完成"; + continue; + } else if (detailStatus === "未知" || detailStatus === "错误") { + log.warn("无法确认是否进入详情界面,尝试继续执行"); + const location = recognizeCommissionLocation(); + commission.location = location; + log.info("委托 {name} 的地点: {location}", commission.name, location); + } else { + const location = recognizeCommissionLocation(); + commission.location = location; + log.info("委托 {name} 的地点: {location}", commission.name, location); + } + + // 退出详情页面 + if (detailStatus !== "未知") { + log.info("退出详情页面 - 按ESC"); + keyDown("VK_ESCAPE"); + await sleep(300); + keyUp("VK_ESCAPE"); + await sleep(1200); + + keyDown("VK_ESCAPE"); + await sleep(300); + keyUp("VK_ESCAPE"); + await sleep(1200); + } + } + + // 步骤3: 执行下滑操作 + log.info("步骤3: 执行下滑操作"); + await PageScroll(1); + await sleep(2000); + + // 步骤4: 执行第二次OCR识别 + log.info("步骤4: 执行第二次OCR识别({x}, {y}) ({width}, {height})", + OCR_REGION_X, OCR_REGION_Y, OCR_REGION_X + OCR_REGION_WIDTH, OCR_REGION_Y + OCR_REGION_HEIGHT); + captureRegion = captureGameRegion(); + let secondResults = captureRegion.findMulti(ocrRo); + log.info("第二次OCR识别结果数量: {count}", secondResults.count); + + // 处理第二次识别结果 + let fourthCommission = null; + for (let i = 0; i < secondResults.count; i++) { + let result = secondResults[i]; + let text = cleanText(result.text); + if (text && text.length >= minTextLength) { + log.info("第4个委托: \"{text}\"", text); + + // 检查委托类型 + const isFightCommission = supportedCommissions.fight.includes(text); + const isTalkCommission = supportedCommissions.talk.includes(text); + const isSupported = isFightCommission || isTalkCommission; + const commissionType = isFightCommission ? COMMISSION_TYPE.FIGHT : + (isTalkCommission ? COMMISSION_TYPE.TALK : ""); + + fourthCommission = { + id: 4, + name: text, + supported: isSupported, + type: commissionType, + location: "" + }; + } + } - // 步骤5: 点击委托4详情按钮 - if (fourthCommission) { - log.info("步骤5: 点击委托4详情按钮"); - log.info("查看第4个委托详情: {name}", fourthCommission.name); + // 步骤5: 检测第4个委托的完成状态 + if (fourthCommission) { + log.info("步骤5: 检测第4个委托的完成状态"); + + // 使用图像识别检测完成状态 + const status = await detectCommissionStatusByImage(3); // 第4个按钮索引为3 + + if (status === "completed") { + log.info("委托4 {name} 已完成,跳过详情查看", fourthCommission.name); + fourthCommission.location = "已完成"; + } else if (status === "uncompleted" || status === "unknown") { + log.info("委托4 {name} 需要查看详情", fourthCommission.name); // 点击详情按钮 - const detailButton = COMMISSION_DETAIL_BUTTONS[3]; // 第4个按钮 + const detailButton = COMMISSION_DETAIL_BUTTONS[3]; log.info("点击委托详情按钮 ({x}, {y})", detailButton.x, detailButton.y); click(detailButton.x, detailButton.y); - await sleep(2500); // 等待详情页面加载 + await sleep(2500); // 检测是否成功进入详情界面 const detailStatus = await checkDetailPageEntered(); log.info(`委托详情界面状态: ${detailStatus}`); - const commission = firstCommissions[4]; - + // 根据检测结果处理 if (detailStatus === "已完成") { log.info("该委托已完成,跳过地点识别和退出操作"); fourthCommission.location = "已完成"; } else if (detailStatus === "未知" || detailStatus === "错误") { log.warn("无法确认是否进入详情界面,尝试继续执行"); - // 尝试识别委托地点 - location = recognizeCommissionLocation(); + const location = recognizeCommissionLocation(); fourthCommission.location = location; log.info("委托 {name} 的地点: {location}", fourthCommission.name, location); } else { @@ -495,9 +649,8 @@ log.info("委托 {name} 的地点: {location}", fourthCommission.name, location); } - // 如果不是已完成状态,需要执行退出操作 + // 退出详情页面 if (detailStatus !== "未知") { - // 退出详情页面 log.info("退出详情页面 - 按ESC"); keyDown("VK_ESCAPE"); await sleep(300); @@ -509,341 +662,447 @@ keyUp("VK_ESCAPE"); await sleep(1200); } - } - - // 合并所有委托结果 - let allCommissions = [...firstCommissions]; - if (fourthCommission) { - allCommissions.push(fourthCommission); - } - - // 输出完整委托列表 - log.info("完整委托列表:"); - for (const commission of allCommissions) { - const supportStatus = commission.supported ? "✅ 支持" : "❌ 不支持"; - const locationInfo = commission.location ? `(${commission.location})` : ""; - log.info("{id}. {name} {location} - {status}", - commission.id, commission.name, locationInfo, supportStatus); - } - - // 保存委托数据 - saveCommissionsData(allCommissions); - - log.info("原神每日委托识别脚本执行完成"); - } catch (error) { - log.error("脚本执行出错: {error}", error); - } - } - - - - - -/** - * 检测委托是否触发 - * @returns {Promise} 是否触发委托 - */ -async function detectCommissionTrigger() { - try { - log.info("检测委托触发 ({x}, {y}) ({width}, {height})...", - COMMISSION_TRIGGER_OCR_X, COMMISSION_TRIGGER_OCR_Y, - COMMISSION_TRIGGER_OCR_X + COMMISSION_TRIGGER_OCR_WIDTH, - COMMISSION_TRIGGER_OCR_Y + COMMISSION_TRIGGER_OCR_HEIGHT); - - // 创建OCR识别对象 - const triggerOcrRo = RecognitionObject.Ocr( - COMMISSION_TRIGGER_OCR_X, - COMMISSION_TRIGGER_OCR_Y, - COMMISSION_TRIGGER_OCR_WIDTH, - COMMISSION_TRIGGER_OCR_HEIGHT - ); - - // 截图识别 - let captureRegion = captureGameRegion(); - let results = captureRegion.findMulti(triggerOcrRo); - - if (results.count > 0) { - for (let i = 0; i < results.count; i++) { - const text = results[i].text.trim(); - log.info("检测到文本: {text}", text); - - // 检查是否包含委托触发相关文字 - if (text.includes("触发委托")) { - log.info("检测到委托触发"); - return true; - } } } - return false; + // 合并所有委托结果 + let allCommissions = [...firstCommissions]; + if (fourthCommission) { + allCommissions.push(fourthCommission); + } + + // 输出完整委托列表 + log.info("完整委托列表:"); + for (const commission of allCommissions) { + const supportStatus = commission.supported ? "✅ 支持" : "❌ 不支持"; + const locationInfo = commission.location ? `(${commission.location})` : ""; + const typeInfo = commission.type ? `[${commission.type}]` : ""; + log.info("{id}. {name} {location} {type} - {status}", + commission.id, commission.name, locationInfo, typeInfo, supportStatus); + } + + // 保存委托数据 + saveCommissionsData(allCommissions); + + log.info("原神每日委托识别脚本执行完成"); } catch (error) { - log.error("检测委托触发时出错: {error}", error); - return false; + log.error("脚本执行出错: {error}", error); } } /** - * 检测委托是否完成 - * @returns {Promise} 是否完成委托 + * 执行对话委托流程 + * @param {string} commissionName - 委托名称 + * @param {string} location - 委托地点 + * @returns {Promise} - 执行结果 */ -async function detectCommissionComplete() { +async function executeTalkCommission(commissionName, location) { try { - log.info("检测委托完成 ({x}, {y}) ({width}, {height})...", - COMMISSION_COMPLETE_OCR_X, COMMISSION_COMPLETE_OCR_Y, - COMMISSION_COMPLETE_OCR_X + COMMISSION_COMPLETE_OCR_WIDTH, - COMMISSION_COMPLETE_OCR_Y + COMMISSION_COMPLETE_OCR_HEIGHT); + log.info("开始执行对话委托: {name} ({location})", commissionName, location); - // 创建OCR识别对象 - const completeOcrRo = RecognitionObject.Ocr( - COMMISSION_COMPLETE_OCR_X, - COMMISSION_COMPLETE_OCR_Y, - COMMISSION_COMPLETE_OCR_WIDTH, - COMMISSION_COMPLETE_OCR_HEIGHT - ); + // 构建可能的流程文件路径 + const processBasePath = `${TALK_PROCESS_BASE_PATH}/${commissionName}`; + const processFilePaths = [ + `${processBasePath}/process.json`, + `${processBasePath}/${location}/process.json` + ]; - // 截图识别 - let captureRegion = captureGameRegion(); - let results = captureRegion.findMulti(completeOcrRo); + let processFilePath = null; - if (results.count > 0) { - for (let i = 0; i < results.count; i++) { - const text = results[i].text.trim(); - log.info("检测到文本: {text}", text); - - // 检查是否包含委托完成相关文字 - if (text.includes("委托完成")) { - log.info("检测到委托完成"); - return true; - } + // 查找流程文件 + for (const path of processFilePaths) { + try { + await file.readText(path); + processFilePath = path; + log.info("找到对话委托流程文件: {path}", path); + break; + } catch (error) { + log.info("对话委托流程文件不存在: {path}", path); } } - return false; - } catch (error) { - log.error("检测委托完成时出错: {error}", error); - return false; - } -} - - - -/** - * 自动战斗 - * @param {number} timeout - 超时时间 - * @returns {Promise} 战斗是否成功 - */ -async function autoFight(timeout) { - const cts = new CancellationTokenSource(); - try { - log.info("开始战斗"); - dispatcher.RunTask(new SoloTask("AutoFight"), cts); - let fightResult = await detectCommissionComplete(); - log.info(`战斗结束,战斗结果:${fightResult ? "成功" : "失败"}`); - cts.cancel(); - return fightResult; - } catch (error) { - log.error(`执行过程中出错: ${error}`); - cts.cancel(); - return false; - } -} - -/** - * 执行委托追踪脚本 - * @param {Object} commission - 委托信息对象 - * @returns {Promise} 是否成功完成委托 - */ -async function executeTrackingScript(commission) { - log.info("准备执行委托追踪脚本: {name} ({location})", - commission.name, - commission.location || "未知地点"); - - try { - // 检查委托是否已完成 - if (commission.location === "已完成") { - log.info("该委托已完成,跳过执行"); - return true; - } - - // 检查委托名称和地点是否有效 - if (!commission.name || !commission.location || commission.location === "未知地点" || commission.location === "识别失败") { - log.warn("委托信息不完整,无法执行追踪脚本"); + if (!processFilePath) { + log.warn("未找到对话委托 {name} 在 {location} 的流程文件", commissionName, location); return false; } - // 构建可能的路径追踪脚本路径 - const location = commission.location.trim(); - const scriptPaths = [ - `assets/${commission.name}/${location}-1.json`, - `assets/${commission.name}/${location}-2.json` - ]; + // 读取流程文件内容 + const processContent = await file.readText(processFilePath); - let scriptFound = false; - let scriptCompleted = false; + // 判断是简单格式还是JSON格式 + let isJsonFormat = false; + try { + JSON.parse(processContent); + isJsonFormat = true; + } catch (error) { + isJsonFormat = false; + } - // 设置全局超时时间(30分钟) - const GLOBAL_TIMEOUT = 30 * 60 * 1000; - const startTime = Date.now(); + if (isJsonFormat) { + // 处理JSON格式的流程 + return await executeJsonProcess(processContent, commissionName, location); + } else { + // 处理简单格式的流程 + return await executeSimpleProcess(processContent, commissionName, location); + } - // 尝试执行每个可能的脚本路径 - for (const scriptPath of scriptPaths) { - try { - // 检查是否超过全局超时时间 - if (Date.now() - startTime > GLOBAL_TIMEOUT) { - log.warn("委托追踪超过全局超时时间,终止执行"); - return false; - } - - // 检查脚本文件是否存在 - log.info("检查路径追踪脚本: {path}", scriptPath); - + } catch (error) { + log.error("执行对话委托时出错: {error}", error); + return false; + } +} + +/** + * 执行简单格式的对话委托流程 + * @param {string} processContent - 流程内容 + * @param {string} commissionName - 委托名称 + * @param {string} location - 委托地点 + * @returns {Promise} - 执行结果 + */ +async function executeSimpleProcess(processContent, commissionName, location) { + try { + log.info("执行简单格式的对话委托流程: {name}", commissionName); + + // 按行分割流程内容 + const lines = processContent.split('\n').map(line => line.trim()).filter(line => line.length > 0); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + log.info("执行流程步骤 {step}: {line}", i + 1, line); + + if (line.endsWith(".json")) { + // 地图追踪文件 + const trackingPath = `assets/process/${commissionName}/${line}`; + log.info("执行地图追踪: {path}", trackingPath); try { - // 尝试读取文件内容来检查是否存在 - await file.readText(scriptPath); - log.info("找到路径追踪脚本: {path}", scriptPath); - scriptFound = true; - } catch (readError) { - log.info("路径追踪脚本不存在: {path}", scriptPath); - continue; // 尝试下一个脚本路径 + await pathingScript.runFile(trackingPath); + log.info("地图追踪执行完成"); + } catch (error) { + log.error("执行地图追踪时出错: {error}", error); } + } else if (line === "F") { + // 按F键并等待对话结束 + log.info("按下F键并等待对话结束"); + keyPress("F"); - // 执行路径追踪脚本 - log.info("开始执行路径追踪脚本: {path}", scriptPath); + // 定义识别对象 + const paimonMenuRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync("Data/RecognitionObject/paimon_menu.png"), + 0, 0, genshin.width / 3.0, genshin.width / 5.0 + ); - // 单个脚本执行超时时间(10分钟) - const SCRIPT_TIMEOUT = 10 * 60 * 1000; + // 判断是否在主界面的函数 + const isInMainUI = () => { + let captureRegion = captureGameRegion(); // 获取一张截图 + let res = captureRegion.Find(paimonMenuRo); + return !res.isEmpty(); + }; - // 创建路径追踪任务(异步执行) - const pathingTask = pathingScript.runFile(scriptPath); + // 重复执行自动剧情,直到返回主界面 + let maxAttempts = 100; // 设置最大尝试次数,防止无限循环 + let attempts = 0; + await sleep(1000); + log.info("开始执行自动剧情循环"); - // 实时检测委托触发和完成状态 - let triggered = false; - let completed = false; - - // 开始时间记录 - const scriptStartTime = Date.now(); - - // 实时检测循环 - while (Date.now() - scriptStartTime < SCRIPT_TIMEOUT) { - // 检测委托是否触发 - if (!triggered && await detectCommissionTrigger()) { - triggered = true; - log.info("委托已触发,开始自动战斗"); - await autoFight(FIGHT_TIMEOUT); - - // 战斗后等待一段时间再检测完成状态 - await sleep(3000); - } + while (!isInMainUI() && attempts < maxAttempts) { + attempts++; + log.info(`执行第 ${attempts} 次自动剧情选择`); + await genshin.chooseTalkOption("", 10, false); - // 检测委托是否完成 - if (await detectCommissionComplete()) { - log.info("委托已完成: {name} ({location})", commission.name, location); - commission.completed = true; - scriptCompleted = true; - - // 尝试取消路径追踪任务(如果API支持) - try { - pathingTask.cancel && pathingTask.cancel(); - } catch (cancelError) { - log.warn("取消路径追踪任务失败: {error}", cancelError); - } - - return true; - } - - // 每秒检测一次 - await sleep(1000); - } - - log.warn("脚本执行超时,尝试下一个脚本"); - - // 尝试取消路径追踪任务(如果API支持) - try { - pathingTask.cancel && pathingTask.cancel(); - } catch (cancelError) { - log.warn("取消路径追踪任务失败: {error}", cancelError); - } - - // 额外等待检测 - log.info("额外等待5秒检测委托触发..."); - let finalTriggered = false; - - for (let i = 0; i < 5; i++) { - await sleep(1000); - - if (!triggered && await detectCommissionTrigger()) { - finalTriggered = true; - log.info("在额外等待期间检测到委托触发"); - - // 开始自动战斗 - log.info("委托已触发,开始自动战斗"); - await autoFight(FIGHT_TIMEOUT); - - // 战斗后等待一段时间再检测完成状态 - await sleep(3000); - - if (await detectCommissionComplete()) { - log.info("委托已完成: {name} ({location})", commission.name, location); - commission.completed = true; - scriptCompleted = true; - return true; - } - + // 检查是否已返回主界面 + if (isInMainUI()) { + log.info("检测到已返回主界面,结束循环"); break; } } - if (!finalTriggered && !triggered) { - log.info("未检测到委托触发,尝试下一个脚本"); + if (isInMainUI()) { + log.info("已返回主界面,自动剧情执行完成"); + } else { + log.warn(`已达到最大尝试次数 ${maxAttempts},但未检测到返回主界面`); } - - } catch (scriptError) { - log.error("执行路径追踪脚本时出错: {error}", scriptError); + } else { + // 其他未知命令 + log.warn("未知的流程命令: {command}", line); } + + // 每个步骤之间等待一段时间 + await sleep(2000); } - if (!scriptFound) { - log.warn("未找到委托 {name} 在 {location} 的路径追踪脚本", commission.name, location); - return false; - } - - return scriptCompleted; + log.info("简单格式的对话委托流程执行完成: {name}", commissionName); + return true; } catch (error) { - log.error("执行追踪脚本时出错: {error}", error); + log.error("执行简单格式的对话委托流程时出错: {error}", error); return false; } } /** - * 从Data文件夹加载委托数据 - * @returns {Promise} 委托数据数组 + * 执行JSON格式的对话委托流程 + * @param {string} processContent - 流程内容 + * @param {string} commissionName - 委托名称 + * @param {string} location - 委托地点 + * @returns {Promise} - 执行结果 */ -async function loadCommissionsFromData() { +async function executeJsonProcess(processContent, commissionName, location) { try { - log.info("尝试从Data文件夹加载委托数据"); - const dataPath = `${OUTPUT_DIR}/commissions_data.json`; + log.info("执行JSON格式的对话委托流程: {name}", commissionName); - try { - const dataContent = await file.readText(dataPath); - const commissionsData = JSON.parse(dataContent); - - if (commissionsData && commissionsData.commissions && commissionsData.commissions.length > 0) { - log.info("成功从Data文件夹加载委托数据,共 {count} 个", commissionsData.commissions.length); - return commissionsData.commissions; - } else { - log.warn("Data文件夹中的委托数据为空"); - return null; + // 解析JSON内容 + const processSteps = JSON.parse(processContent); + + // 如果是数组,则按顺序执行每个步骤 + if (Array.isArray(processSteps)) { + for (let i = 0; i < processSteps.length; i++) { + const step = processSteps[i]; + log.info("执行流程步骤 {step}: {type}", i + 1, step.type); + + // 如果有注释,输出注释 + if (step.note) { + log.info("步骤说明: {note}", step.note); + } + + switch (step.type) { + case "地图追踪": + // 执行地图追踪 + log.info("执行地图追踪: {path}", step.data); + try { + await pathingScript.runFile(step.data); + log.info("地图追踪执行完成"); + } catch (error) { + log.error("执行地图追踪时出错: {error}", error); + } + break; + + case "键鼠脚本": + // 执行键鼠脚本 + log.info("执行键鼠脚本: {path}", step.data); + try { + await runFile(step.data); + log.info("键鼠脚本执行完成"); + } catch (error) { + log.error("执行键鼠脚本时出错: {error}", error); + } + break; + + case "对话": + // 执行对话 + log.info("执行对话流程"); + keyPress("F"); + + // 定义识别对象 + const paimonMenuRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync("Data/RecognitionObject/paimon_menu.png"), + 0, 0, genshin.width / 3.0, genshin.width / 5.0 + ); + + // 判断是否在主界面的函数 + const isInMainUI = () => { + let captureRegion = captureGameRegion(); // 获取一张截图 + let res = captureRegion.Find(paimonMenuRo); + return !res.isEmpty(); + }; + + // 重复执行自动剧情,直到返回主界面 + let maxAttempts = 100; // 设置最大尝试次数,防止无限循环 + let attempts = 0; + await sleep(1000); + log.info("开始执行自动剧情循环"); + + const skipCount = step.data || 10; // 默认跳过10次对话 + + while (!isInMainUI() && attempts < maxAttempts) { + attempts++; + log.info(`执行第 ${attempts} 次自动剧情选择`); + await genshin.chooseTalkOption("", skipCount, false); + + // 检查是否已返回主界面 + if (isInMainUI()) { + log.info("检测到已返回主界面,结束循环"); + break; + } + } + + if (isInMainUI()) { + log.info("已返回主界面,自动剧情执行完成"); + } else { + log.warn(`已达到最大尝试次数 ${maxAttempts},但未检测到返回主界面`); + } + break; + + case "按键": + // 执行按键操作 + if (typeof step.data === "string") { + // 单个按键 + log.info("执行按键: {key}", step.data); + keyPress(step.data); + } else if (typeof step.data === "object") { + // 复杂按键操作 + if (step.data.action === "down") { + log.info("按下按键: {key}", step.data.key); + keyDown(step.data.key); + } else if (step.data.action === "up") { + log.info("释放按键: {key}", step.data.key); + keyUp(step.data.key); + } else if (step.data.action === "press") { + log.info("点击按键: {key}", step.data.key); + keyPress(step.data.key); + } + } + break; + + case "tp": + // 执行传送 + if (Array.isArray(step.data) && step.data.length >= 2) { + log.info("执行传送: {x}, {y}", step.data[0], step.data[1]); + const force = step.data.length > 2 ? step.data[2] : false; + await genshin.tp(step.data[0], step.data[1], force); + log.info("传送完成"); + } else { + log.error("传送参数格式错误"); + } + break; + + default: + log.warn("未知的流程类型: {type}", step.type); + } + + // 每个步骤之间等待一段时间 + await sleep(2000); } - } catch (readError) { - log.warn("读取委托数据文件失败: {error}", readError); - return null; + } else { + log.error("JSON流程格式错误,应为数组"); + return false; } + + log.info("JSON格式的对话委托流程执行完成: {name}", commissionName); + return true; } catch (error) { - log.error("加载委托数据时出错: {error}", error); - return null; + log.error("执行JSON格式的对话委托流程时出错: {error}", error); + return false; } } +/** + * 执行委托追踪 + * 新版本:执行所有委托而不检测触发状态 + */ +async function executeCommissionTracking_old() { + try { + log.info("开始执行委托追踪 - 全部执行模式"); + + // 获取已识别的委托列表 + let commissions = []; + try { + const commissionsData = JSON.parse(file.readTextSync(`${OUTPUT_DIR}/commissions_data.json`)); + commissions = commissionsData.commissions.filter(c => c.supported); + log.info("已加载支持的委托数据,共 {count} 个", commissions.length); + } catch (error) { + log.error("读取委托数据失败: {error}", error); + return false; + } + + if (commissions.length === 0) { + log.warn("没有找到支持的委托,请先运行识别脚本"); + return false; + } + + // 确保回到主界面 + await genshin.returnMainUi(); + + // 执行每个委托 + let completedCount = 0; + for (const commission of commissions) { + // 跳过已完成的委托 + if (commission.location === "已完成") { + log.info("委托 {name} 已完成,跳过", commission.name); + completedCount++; + continue; + } + + // 跳过没有地点信息的委托 + if (!commission.location || commission.location === "未知地点" || commission.location === "识别失败") { + log.warn("委托 {name} 缺少地点信息,跳过", commission.name); + continue; + } + + log.info("开始执行委托: {name} ({location}) [{type}]", + commission.name, commission.location, commission.type || "未知类型"); + + // 根据委托类型执行不同的处理逻辑 + if (commission.type === COMMISSION_TYPE.TALK) { + // 执行对话委托 + const success = await executeTalkCommission(commission.name, commission.location); + if (success) { + completedCount++; + log.info("对话委托 {name} 执行完成", commission.name); + } else { + log.warn("对话委托 {name} 执行失败", commission.name); + } + } else { + // 默认执行战斗委托 + // 构建可能的路径追踪脚本路径 + const location = commission.location.trim(); + const scriptPaths = [ + `assets/${commission.name}/${location}-1.json`, + `assets/${commission.name}/${location}-2.json`, + `assets/${commission.name}/${location}-3.json`, + ]; + + let scriptFound = false; + let scriptExecuted = false; + + // 尝试执行每个可能的脚本路径 + for (const scriptPath of scriptPaths) { + try { + // 检查脚本文件是否存在 + log.info("检查路径追踪脚本: {path}", scriptPath); + + try { + // 尝试读取文件内容来检查是否存在 + await file.readText(scriptPath); + log.info("找到路径追踪脚本: {path}", scriptPath); + scriptFound = true; + scriptExecuted = true; + } catch (readError) { + log.info("路径追踪脚本不存在: {path}", scriptPath); + continue; // 尝试下一个脚本路径 + } + + // 执行路径追踪脚本 + log.info("开始执行路径追踪脚本: {path}", scriptPath); + + // 执行脚本并等待完成 + await pathingScript.runFile(scriptPath); + log.info("路径追踪脚本执行完成"); + } catch (scriptError) { + log.error("执行路径追踪脚本时出错: {error}", scriptError); + } + } + + if (!scriptFound) { + log.warn("未找到委托 {name} 在 {location} 的路径追踪脚本", commission.name, location); + } else if (scriptExecuted) { + completedCount++; + log.info("战斗委托 {name} 执行完成", commission.name); + } + } + + // 每个委托之间等待一段时间 + log.info("等待5秒后执行下一个委托..."); + await sleep(5000); + } + + log.info("委托追踪全部执行完成,共执行 {count}/{total} 个委托", + completedCount, commissions.length); + + return completedCount > 0; + } catch (error) { + log.error("执行委托追踪时出错: {error}", error); + return false; + } +} /** * 执行委托追踪 @@ -888,56 +1147,66 @@ async function executeCommissionTracking() { continue; } - log.info("开始执行委托: {name} ({location})", commission.name, commission.location); + log.info("开始执行委托: {name} ({location}) [{type}]", + commission.name, commission.location, commission.type || "未知类型"); - // 构建可能的路径追踪脚本路径 - const location = commission.location.trim(); - const scriptPaths = [ - `assets/${commission.name}/${location}-1.json`, - `assets/${commission.name}/${location}-2.json`, - `assets/${commission.name}/${location}-3.json`, - ]; - - let scriptFound = false; - let scriptExecuted = false; - - // 尝试执行每个可能的脚本路径 - for (const scriptPath of scriptPaths) { - try { - // 检查脚本文件是否存在 - log.info("检查路径追踪脚本: {path}", scriptPath); - - try { - // 尝试读取文件内容来检查是否存在 - await file.readText(scriptPath); - log.info("找到路径追踪脚本: {path}", scriptPath); - scriptFound = true; - scriptExecuted = true; - } catch (readError) { - log.info("路径追踪脚本不存在: {path}", scriptPath); - continue; // 尝试下一个脚本路径 - } - - // 执行路径追踪脚本 - log.info("开始执行路径追踪脚本: {path}", scriptPath); - - // 执行脚本并等待完成 - await pathingScript.runFile(scriptPath); - log.info("路径追踪脚本执行完成"); - - //log.info("委托 {name} 执行完成", commission.name); - - // 成功执行一个脚本后,跳出循环 - //break; - } catch (scriptError) { - log.error("执行路径追踪脚本时出错: {error}", scriptError); + // 根据委托类型执行不同的处理逻辑 + if (commission.type === COMMISSION_TYPE.TALK) { + // 执行对话委托 + const success = await executeTalkCommission(commission.name, commission.location); + if (success) { + completedCount++; + log.info("对话委托 {name} 执行完成", commission.name); + } else { + log.warn("对话委托 {name} 执行失败", commission.name); + } + } else { + // 默认执行战斗委托 + // 构建可能的路径追踪脚本路径 + const location = commission.location.trim(); + const scriptPaths = [ + `assets/${commission.name}/${location}-1.json`, + `assets/${commission.name}/${location}-2.json`, + `assets/${commission.name}/${location}-3.json`, + ]; + + let scriptFound = false; + let scriptExecuted = false; + + // 尝试执行每个可能的脚本路径 + for (const scriptPath of scriptPaths) { + try { + // 检查脚本文件是否存在 + log.info("检查路径追踪脚本: {path}", scriptPath); + + try { + // 尝试读取文件内容来检查是否存在 + await file.readText(scriptPath); + log.info("找到路径追踪脚本: {path}", scriptPath); + scriptFound = true; + scriptExecuted = true; + } catch (readError) { + log.info("路径追踪脚本不存在: {path}", scriptPath); + continue; // 尝试下一个脚本路径 + } + + // 执行路径追踪脚本 + log.info("开始执行路径追踪脚本: {path}", scriptPath); + + // 执行脚本并等待完成 + await pathingScript.runFile(scriptPath); + log.info("路径追踪脚本执行完成"); + } catch (scriptError) { + log.error("执行路径追踪脚本时出错: {error}", scriptError); + } + } + + if (!scriptFound) { + log.warn("未找到委托 {name} 在 {location} 的路径追踪脚本", commission.name, location); + } else if (scriptExecuted) { + completedCount++; + log.info("战斗委托 {name} 执行完成", commission.name); } - } - - if (!scriptFound) { - log.warn("未找到委托 {name} 在 {location} 的路径追踪脚本", commission.name, location); - } else if (scriptExecuted) { - completedCount++; } // 每个委托之间等待一段时间 @@ -955,67 +1224,6 @@ async function executeCommissionTracking() { } } -/** - * 执行委托追踪主函数 - * @returns {Promise} - */ -async function executeCommissionTracking_old() { - try { - log.info("开始执行委托追踪"); - await genshin.returnMainUi(); - - // 加载支持的委托列表 - const supportedCommissions = loadSupportedCommissions(); - - // 尝试从Data文件夹加载委托数据 - let commissions = await loadCommissionsFromData(); - - // 如果Data文件夹没有数据,则执行识别 - if (!commissions) { - log.info("未找到已保存的委托数据,开始执行委托识别"); - await Identification(); - - // 再次尝试加载委托数据 - commissions = await loadCommissionsFromData(); - - if (!commissions) { - log.error("无法获取委托数据,脚本终止"); - return; - } - } - - // 过滤出支持的委托 - const supportedCommissionsList = commissions.filter(c => c.supported); - - if (supportedCommissionsList.length === 0) { - log.warn("没有找到支持的委托"); - return; - } - - log.info("找到 {count} 个支持的委托", supportedCommissionsList.length); - - // 执行每个支持的委托 - for (const commission of supportedCommissionsList) { - log.info("开始处理委托: {name} ({location})", commission.name, commission.location || "未知地点"); - - // 执行委托追踪脚本 - //const success = await executeTrackingScript(commission); - - if (success) { - log.info("委托 {name} 已成功完成", commission.name); - } else { - log.warn("委托 {name} 未能完成", commission.name); - } - - await sleep(2000); // 等待一段时间再处理下一个委托 - } - - log.info("委托追踪执行完成"); - } catch (error) { - log.error("执行委托追踪时出错: {error}", error); - } -} - // 主函数 async function main() { @@ -1039,4 +1247,4 @@ async function executeCommissionTracking_old() { // 修改这里:使用 Promise 包装 main 函数的执行 return main(); //log.info(""); -})(); \ No newline at end of file +})(); diff --git a/repo/js/AutoEntrust/manifest.json b/repo/js/AutoEntrust/manifest.json index 20446252..0ec64da8 100644 --- a/repo/js/AutoEntrust/manifest.json +++ b/repo/js/AutoEntrust/manifest.json @@ -1,12 +1,12 @@ { "manifest_version": 1, - "name": "(Beta)全自动寻路蒙德地区每日委托", - "version": "0.94.1", + "name": "(Beta)全自动蒙德地区每日委托", + "version": "0.95", "bgi_version": "0.44.3", - "description": "基于BetterGI的原神自动化脚本,可自动识别每日委托内容、OCR识别委托地点和状态、自动追踪委托路径。目前支持蒙德地区委托,未来将支持全国家委托。使用前请阅读README.md(可用记事本打开)获取详细说明和常见问题解答。请关闭战斗后自动拾取,不然部分委托会发生错误并且浪费过多时间。如果发生错误,如不支持的战斗委托,请将脚本data/文件夹下面的数据,log/日志下今日日志,每日委托任务包含地点截图反馈给作者。反馈方法1.加3302871403QQ好友2.发送截图,日志给3302871403@qq.com3.加1036100501bgi v8群@星野(bgi v7群也可以)。目前项目正处于开发阶段急需人手,有意者可私信作者(会在地图上标点即可)", + "description": "基于BetterGI的原神自动化脚本,可自动识别每日委托内容、OCR识别委托地点和状态、自动追踪委托路径。目前支持蒙德地区委托,未来将支持全国家委托。使用前请阅读README.md(可用记事本打开)获取详细说明和常见问题解答。请关闭战斗后自动拾取,不然部分委托会发生错误并且浪费过多时间。如果发生错误,如不支持的战斗委托,请将脚本data/文件夹下面的数据,log/日志下今日日志,每日委托任务包含地点截图反馈给作者。反馈方法1.加3302871403QQ好友2.发送截图,日志给3302871403@qq.com。目前项目正处于开发阶段急需人手,有意者可私信作者(会在地图上标点即可)。感谢云闲vsv & 换苏伟u的共同开发", "authors": [ { - "name": "星野(Main) & 换苏伟u", + "name": "星野", "link": "https://github.com/LX666-666" } ], diff --git a/repo/js/AutoEntrust/name.json b/repo/js/AutoEntrust/name.json new file mode 100644 index 00000000..ff168457 --- /dev/null +++ b/repo/js/AutoEntrust/name.json @@ -0,0 +1,23 @@ +{ + "fight": [ + "圆滚滚的易爆品", + "丘丘人的一小步", + "临危受命", + "持盾的危机", + "攀高危险", + "邪恶的扩张", + "冷冰冰的大麻烦", + "为了执行官大人", + "极速前进", + "冷凉凉的大团危机" + ], + "talk": [ + "说到做到", + "那位先生的委托", + "父亲能做的事", + "冒险家安娜", + "诗歌交流", + "钥匙是易丢物品", + "语言交流" + ] +} \ No newline at end of file diff --git a/repo/js/AutoEntrust/settings.json b/repo/js/AutoEntrust/settings.json index c670af77..ac13422e 100644 --- a/repo/js/AutoEntrust/settings.json +++ b/repo/js/AutoEntrust/settings.json @@ -10,14 +10,14 @@ "label": "最小文本长度(小于此长度的文本将被过滤)", "default": "4" }, - { - "name": "debugMode", - "type": "checkbox", - "label": "调试模式(输出更多日志信息)" - }, { "name": "team", "type": "input-text", "label": "战斗的队伍名称,不填表示不切换队伍" + }, + { + "name": "fight", + "type": "input-text", + "label": "自定义战斗脚本(研究中)" } ] \ No newline at end of file diff --git a/repo/js/AutoEntrust/对话委托制做方法.txt b/repo/js/AutoEntrust/对话委托制做方法.txt new file mode 100644 index 00000000..58255afe --- /dev/null +++ b/repo/js/AutoEntrust/对话委托制做方法.txt @@ -0,0 +1,59 @@ +编写方法 +创建 JSON 文件 +使用文本编辑器创建一个新的 JSON 文件process.json。 +根据需要定义对话委托流程的各个步骤,每个步骤包含type(类型)和data(数据)等属性,可选地添加note(注释)属性来描述步骤。 +定义流程步骤类型 +地图追踪 :设置type为"地图追踪",data为地图追踪文件的路径。 +键鼠脚本 :设置type为"键鼠脚本",data为键鼠脚本文件的路径。 +对话 :设置type为"对话",可选地在data中指定跳过对话的次数(默认为10)。 +按键 :设置type为"按键",data为按键值(如"F");也可以是复杂按键操作对象,包含action(动作,如"down"、"up"、"press")和key(按键值)。 +传送 :设置type为"tp",data为一个数组,包含传送的目标坐标(x、y)以及可选的强制传送标志。 +组织流程步骤 +将所有步骤组织成一个数组,按照执行顺序排列。 +可以在每个步骤中添加note属性,用于记录步骤的详细说明或备注信息。 +[ + { + "type": "地图追踪", + "data": "assets/process/example/tracking1.json", + "note": "执行第一个地图追踪步骤" + }, + { + "type": "对话", + "note": "执行对话步骤,默认跳过10次对话" + }, + { + "type": "按键", + "data": "F", + "note": "按下F键" + }, + { + "type": "按键", + "data": { + "action": "press", + "key": "E" + }, + "note": "点击E键" + }, + { + "type": "tp", + "data": [100, 200, true], + "note": "传送到坐标(100, 200),强制传送" + }, + { + "type": "键鼠脚本", + "data": "assets/process/example/script1.js", + "note": "执行键鼠脚本" + } +] + +我不会json +可以用简单版 +格式如下 +XXX-1.json +F +XXX-2.json +F +XXX-3.json + +XXX-1.json:地图追踪 +F:对话 \ No newline at end of file