From b69f61d3dd170da47aeff223d2faa86318f3514c Mon Sep 17 00:00:00 2001 From: mno <718135749@qq.com> Date: Wed, 4 Mar 2026 18:35:10 +0800 Subject: [PATCH] =?UTF-8?q?js=EF=BC=9A=E5=B8=A6=E7=8A=B6=E6=80=81=E6=9C=BA?= =?UTF-8?q?=E7=9A=84=E8=B4=A6=E5=8F=B7=E5=88=87=E6=8D=A2=20(#2949)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * js:带状态机的账号切换 使用状态机管理账号切换过程中的各种页面 提高流程稳定性并方便管理和新增异常处理 支持自动截图保存,双模式切换账号 * Update main.js 将状态切换需要用到的函数挂载到globalThis,以适配AutoJs作为其中的账号切换工具 * 修复提到的问题 * Update main.js 修复提到的问题 * Update main.js * Update main.js * Update main.js --- repo/js/AccountSwitchStateMachine/README.md | 225 ++++ .../accounts/示例图片.png | Bin 0 -> 3241 bytes .../assets/RecognitionObjects/Agree.png | Bin 0 -> 1283 bytes .../assets/RecognitionObjects/Confirm.png | Bin 0 -> 2581 bytes .../RecognitionObjects/EnterAccount.png | Bin 0 -> 2931 bytes .../EnterAccountAndPassword.png | Bin 0 -> 11442 bytes .../assets/RecognitionObjects/EnterGame.png | Bin 0 -> 2978 bytes .../assets/RecognitionObjects/EnterGame2.png | Bin 0 -> 2781 bytes .../RecognitionObjects/EnterPassword.png | Bin 0 -> 1838 bytes .../assets/RecognitionObjects/Exit.png | Bin 0 -> 1421 bytes .../assets/RecognitionObjects/Login.png | Bin 0 -> 877 bytes .../assets/RecognitionObjects/LoginOther.png | Bin 0 -> 3310 bytes .../assets/RecognitionObjects/Menu.png | Bin 0 -> 1408 bytes .../assets/RecognitionObjects/Paimon.png | Bin 0 -> 2968 bytes .../RecognitionObjects/PrepareToLogOut.png | Bin 0 -> 6851 bytes .../RecognitionObjects/SelectAccount.png | Bin 0 -> 2671 bytes .../RecognitionObjects/SwitchAccount.png | Bin 0 -> 706 bytes .../assets/states.json | 412 ++++++ .../assets/uid图片/0.png | Bin 0 -> 789 bytes .../assets/uid图片/1.png | Bin 0 -> 477 bytes .../assets/uid图片/2.png | Bin 0 -> 777 bytes .../assets/uid图片/3.png | Bin 0 -> 722 bytes .../assets/uid图片/4.png | Bin 0 -> 713 bytes .../assets/uid图片/5.png | Bin 0 -> 807 bytes .../assets/uid图片/6.png | Bin 0 -> 814 bytes .../assets/uid图片/7.png | Bin 0 -> 605 bytes .../assets/uid图片/8.png | Bin 0 -> 854 bytes .../assets/uid图片/9.png | Bin 0 -> 623 bytes repo/js/AccountSwitchStateMachine/main.js | 1164 +++++++++++++++++ .../AccountSwitchStateMachine/manifest.json | 13 + .../AccountSwitchStateMachine/settings.json | 32 + 31 files changed, 1846 insertions(+) create mode 100644 repo/js/AccountSwitchStateMachine/README.md create mode 100644 repo/js/AccountSwitchStateMachine/accounts/示例图片.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Agree.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Confirm.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterAccount.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterAccountAndPassword.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterGame.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterGame2.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterPassword.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Exit.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Login.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/LoginOther.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Menu.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Paimon.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/PrepareToLogOut.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/SelectAccount.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/SwitchAccount.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/states.json create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/0.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/1.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/2.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/3.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/4.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/5.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/6.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/7.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/8.png create mode 100644 repo/js/AccountSwitchStateMachine/assets/uid图片/9.png create mode 100644 repo/js/AccountSwitchStateMachine/main.js create mode 100644 repo/js/AccountSwitchStateMachine/manifest.json create mode 100644 repo/js/AccountSwitchStateMachine/settings.json diff --git a/repo/js/AccountSwitchStateMachine/README.md b/repo/js/AccountSwitchStateMachine/README.md new file mode 100644 index 000000000..c12b605f9 --- /dev/null +++ b/repo/js/AccountSwitchStateMachine/README.md @@ -0,0 +1,225 @@ +# 账号切换工具使用说明 + +## 快速开始 + +### 方式一:账号图片识别(推荐) + +1. **准备账号图片** + - **推荐方式**:使用脚本的"截图模式"自动获取并保存截图(详见截图模式部分) + - **手动方式**:在 `accounts/` 目录下放置账号截图 + - 图片命名为 `{UID}.png`,如 `284073800.png` + - 截图应包含账号选择界面中该账号的明显标识 + +2. **配置脚本** + - 打开BetterGI配置面板 + - 填写目标账号UID + - 确保"跳过搜索直接输入账号密码"未勾选 + +3. **运行脚本** + - 脚本会自动定位到账号选择界面 + - 识别并点击对应账号图片 + - 自动进入游戏并返回主界面 + +### 方式二:账号密码输入 + +1. **配置脚本** + - 打开BetterGI配置面板 + - 填写目标账号UID(用于通知显示) + - 填写账号和密码 + - 勾选"跳过搜索直接输入账号密码" + +2. **运行脚本** + - 脚本会自动进入账号密码输入界面 + - 自动点击同意按钮、输入账号密码 + - 自动进入游戏并返回主界面 + +### 说明 +- **两种方式可同时使用**:方式二作为方式一的补充,当图片识别失败时会自动回退到账号密码方式 +- **推荐配置**:同时填写账号密码作为备用,提高切换成功率 + +## 截图模式 + +截图模式是一个辅助功能,用于自动获取并保存账号图片,方便后续使用账号图片识别方式进行切换。 + +**使用步骤**: +1. **配置脚本** + - 打开BetterGI配置面板 + - 填写目标账号UID(优先使用,用于命名图片) + - 勾选"截图模式" + +2. **运行脚本** + - 脚本会优先使用配置中的"要切换到的账号UID"命名图片 + - 如果未填写该项,会尝试自动识别当前账号的UID + - 导航到账号选择界面 + - 自动截图并保存账号图片 + - 返回主界面并发送成功通知 + +**保存位置**: +- 图片保存在 `accounts/` 目录下 +- 文件名格式为 `{UID}.png` + +## 配置说明 + +在BetterGI的配置面板中进行以下设置: + +### 1. 目标账号UID +- 输入要切换到的账号UID +- 用于识别账号图片文件名、通知显示和截图模式命名 + +### 2. 账号(可选) +- 当无法通过图片识别找到账号时备用 +- 建议填写,以防图片识别失败 + +### 3. 密码(可选) +- 当无法通过图片识别找到账号时备用 +- 建议填写,以防图片识别失败 + +### 4. 跳过搜索直接输入账号密码 +- **勾选**:不尝试查找账号图片,直接使用账号密码登录 +- **不勾选**:优先尝试通过账号图片识别切换,失败时回退到账号密码方式 + +### 5. UID校验 +- **勾选**:在切换前后检查当前账号UID,辅助判断切换是否成功 +- **不勾选**:不进行UID校验 + +### 6. 截图模式 +- **勾选**:进入截图模式,自动截图保存对应UID的账号图片 +- **不勾选**:正常执行账号切换流程 + +## 常见问题 + +### Q: 账号图片识别失败怎么办? +A: 确保: +1. 图片存放在 `accounts/` 目录下 +2. 图片命名为正确的UID格式 +3. 截图清晰,包含账号的明显标识 +4. 同时填写账号密码作为备用方案 + +### Q: 切换失败后会怎样? +A: 脚本会尝试返回主界面,并发送失败通知。如果配置了账号密码,建议手动检查账号状态。 + +### Q: 为什么需要填写UID? +A: UID用于: +1. 确定账号图片文件名 +2. 在通知中显示切换的账号信息 +3. 截图模式下命名保存的图片文件 + +## 注意事项 + +1. 首次使用前建议先手动测试一遍切换流程 +2. 确保游戏分辨率为1920x1080 +3. 账号图片建议截取账号选择界面中该账号的清晰截图 +4. 密码输入时不会显示,属于正常现象 +5. 截图模式下,默认使用设置的目标UID,未设置时会尝试自动识别 + +## 问题反馈 + +如果在使用过程中遇到预料之外的弹窗或其他问题,脚本可能无法处理,请按照以下方式反馈: + +1. **截图保存**:截取全屏原图,确保包含完整的游戏界面和弹窗 +2. **联系作者**:QQ号 718135749 +3. **提供信息**:简要描述遇到的问题和操作步骤 + +感谢您的支持! + +
+项目介绍(点击展开) + +## 项目介绍 + +基于状态机实现的自动账号切换脚本,支持通过账号图片识别或手动输入账号密码两种方式切换原神账号。 + +### 功能特性 + +- **双模式切换**:支持账号图片自动识别和账号密码手动输入两种方式 +- **状态机管理**:使用有限状态机管理切换流程,稳定可靠 +- **自动返回**:切换失败时自动尝试返回原账号 +- **通知提醒**:切换成功或失败时发送系统通知 +- **循环防护**:防止状态循环卡死 +- **UID校验**:支持切换前后检查当前账号UID,提高切换准确性 +- **截图模式**:自动截图保存账号图片,方便后续切换 + +### 目录结构 + +``` +AccountSwitchStateMachine/ +├── main.js # 主脚本文件 +├── manifest.json # 脚本清单 +├── settings.json # 配置定义 +├── assets/ +│ ├── states.json # 状态机配置 +│ └── RecognitionObjects/ # 状态识别图片 +│ ├── Paimon.png +│ ├── Menu.png +│ ├── PrepareToLogOut.png +│ ├── SwitchAccount.png +│ ├── Exit.png +│ ├── EnterGame.png +│ ├── LoginOther.png +│ ├── SelectAccount.png +│ ├── EnterAccountAndPassword.png +│ ├── Agree.png +│ ├── EnterAccount.png +│ ├── EnterPassword.png +│ ├── EnterGame2.png +│ ├── Login.png +│ └── Confirm.png +└── accounts/ # 账号图片存放目录 + └── {uid}.png # 以UID命名的账号截图 +``` + +### 工作流程 + +``` +开始 +│ +├─► 截图模式(勾选时) +│ ├─► 使用设置的UID或识别当前UID +│ ├─► 导航到账号选择界面 +│ ├─► 截图保存账号图片 +│ └─► 返回主界面 +│ ├─► 成功 → 发送成功通知 +│ └─► 失败 → 发送失败通知 +│ +├─► 尝试账号图片方式(未勾选跳过时) +│ ├─► 预加载账号图片 +│ ├─► 切换到选择账号界面 +│ ├─► 查找并点击账号图片 +│ └─► 切换到主界面 +│ ├─► 成功 → 发送成功通知 +│ └─► 失败 → 回退到账号密码方式 +│ +└─► 账号密码方式 + ├─► 检查账号密码 + ├─► 切换到输入账号密码界面 + ├─► 点击同意按钮 + ├─► 输入账号密码 + ├─► 点击进入游戏 + └─► 切换到主界面 + ├─► 成功 → 发送成功通知 + └─► 失败 → 尝试返回原账号 → 发送失败通知 +``` + +### 状态说明 + +| 状态名 | 说明 | +|--------|------| +| mainUI | 主界面(派蒙图标) | +| menuUI | 菜单界面(ESC菜单) | +| prepareToLogOut | 预备退出登录界面 | +| loginScreen | 登录界面(切换账号按钮) | +| exitAccount | 退出账号界面 | +| enterGame | 进入游戏或登录其他账号界面 | +| selectAccount | 选择账号界面 | +| enterAccountAndPassword | 输入账号密码界面 | +| noAccount | 无账号登录界面 | + +### 更新日志 + +#### v1.0 +- 初始版本 +- 实现状态机管理 +- 支持账号图片和账号密码两种切换方式 +- 添加自动返回和通知功能 + +
\ No newline at end of file diff --git a/repo/js/AccountSwitchStateMachine/accounts/示例图片.png b/repo/js/AccountSwitchStateMachine/accounts/示例图片.png new file mode 100644 index 0000000000000000000000000000000000000000..012e420ef0e08905e253037e9c690afcafc1b1e9 GIT binary patch literal 3241 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 literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Agree.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Agree.png new file mode 100644 index 0000000000000000000000000000000000000000..394c4a6cf489fb1719f5106cc91f83babe54525d GIT binary patch literal 1283 zcmV+e1^oJnP)KJ^*sy5%_>7Ey)UqEUqFTvP2e_0Is~GW{E5=0bF@W%@SE$0=V*$ znkBNh1aRdgHT!>+)ph;uPRWWA&nH-x=Qtr2OIIq5;c$$LWaqq^*3V?bK!7t0WBpB* zuGxnNl^2$v(P*kvs>*V^R+B|h;y8h2c}XhIX0!Ew>D5T7RN?vPj_u~=7ADknUDNuj zO_E9!%}_KWiuu*Jru7ZOu&%7!Y_l96Ph>Ui@deg=K9}V-7Z!wQ^6BaMMenOG&(ANR z$S!{q$uvwj8jW`u564U<6N}1rl%Fie=kw*oLf<5%Y5hnL<(*SLfMm(ALH@l#Z_#+oKB~E%s;ZauBTF2*pjJ?$Fon-jMwXf zk>it7ie?z+r(`m14zr?cz24-)!f3qHJxVH8xUevv&o?=)$4%L*tKg;!JYOiT%FgHW z{r!VCH7m*rLQ>30>mta=Xgp>){_b8u_9BC(Y`cA%74yi}>#3`2S;0E?GAUd*+Mqj=A>xGqO$$|fMxk! z?=7+^F`dpK5s|@l*$aOFE^mF`|D!OYpzIwho8|*Qj)y6O%zkWMxHd_FI7tdgd(uIZTzhvRbWHKq2t9FOW?fDI^v>FYW zq8Zxe>~s{%t-7E~r_+T`x4ORN>2?qbMTJ;$%NV4xDU0ld!Rd4g`O@hu06^FEgFlWb znhCHWS#H6u!Eguw7!F5;LeVgcOg1M;rD!Zc(aipr1B=Q602=agPKYNmn|5S^p$IP| z-n}Dp`r`~Kdwngq+@E2=QZysW%lfU=8g_?Eh{g@W(6m03^?dwt==FKMzC*9?>$gL@ z!`~wv$t}Fmxbr-Q%T<#*)>&2$N%fA4%(Wh?A_ zo9M_BtIHxk^nW5qWO02>M3%_n62O(0)GU$3C4eh0saYb6O8{41QnN%BmjJH3q-KdM tE&*J5NzD>jTmrcAlA0y5xCC(J{R>s@H)(e*+!z1=002ovPDHLkV1jZfhBW{H literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Confirm.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Confirm.png new file mode 100644 index 0000000000000000000000000000000000000000..96f28ee70b6fefe7cd531da9618c8176dcd8b4fe GIT binary patch literal 2581 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 literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterAccount.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterAccount.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c7bccf807aaade0d1b6de89f119cb58c6a5d8e GIT binary patch literal 2931 zcmV-(3yk!MP)i(A>t8@d7>3+OiDAiLI8MxNEmsUnpfnUM@izW=z53V_DuPr$ot$`| z)cW~+IUMcncE4I}OmjJ%Ek@&Mzd!19dTLXz)|67Y3gp4G%hftDPp?0OSBTy5WY#hI zk0q5Jz9c0mfA+|iq{PzkN4e1|m8+$4<$Srs+bh)u!xBZ+ecymxpG{6}OkENLjMN6Hc{)tUm6XZ#+HCW#pykx{Kh!gws3%{DQXQi{kZ3sq67 zKbEpkgQ;F1!?sV*hww@Cq#{FYwjar3Sb}E4=nhglM6+=zTVq%vh_%PvJ+@>;M*KJ; zDHqqZ?Gxc6KnP;Z^Sse$3U7zQq+oFE;PYeKCzj)HkP`6iv&w94YP!)-TD7`zD{8H- z0wNifs7<{;7)_^h%UW%B`_svZTNB^+uU8+L=s4%=)eplEk*Qqx?5fs^GXo-S-zF4uqzmg5J*@nASEm8%R(SdIssvEA-~ zfn_05LJUhj3S~vr6;C|n5g?#iQ+oX&6w_LM z6z|$%x7&mNptrj~il!UBpEUOIwJ#SJdNM5ep6y3j9uCL&%!xWGGhB{g$#k|rWWW(b zrSk6~LzHBoVZDyj(r`41la#1WrgK0B_fevh20;Mm0gH3FxUer-Zu-8DXL7sSuQywU zC4?7GC&%-y02)E6qozslC-pHaGJM|$wPD*QN@*B|fB=@`QO)AUO63YrLwISwKcp^9 zDP65LIApimKkN3QJtHzo<;q8)49AOM30Ek66w0%?`8{M5iAeaAg7~Xc8>uv(&v&Kr zQ7D6u02wUDo8}TYCROD5!UDD=kb%zJYPF%1_6OraQ5cPmxDd|!{oygnC=wA&b+gq5)e6=$m1}WR z677T|&iS0$6B_sXBla-JEM=(>Ss<)cuJvl7|& zqLL9A3-j*N!J=}b#ju1gRrpdA=^?XHkwGav9_{1Nj#7%B8G_l|gvQt3n2(~AHk4>l zh9&U~a&}};O85J_Dev4OUStd4n_&qki7*Udq112OEDnEG$z)A%Fdm?PVoiMChq#<8 zlrA^;qxy*(QCyT#+HAEk%8yqtpHZnc;_Xm}1X>UT5IqUvJrm`+-eO@@stp*9>4#^? z07|yEWHYdPe>ghl^Rt3M0dB$fefH)hFf4JLUq%!Cj35X=z>2D&nNGwelu}T%g`$9% zGe6x=AgtFPA~G6EYqQy6!Ldly>&iEgv081=o*j-hI1L$fhW;5NSj))RLkxTeK1Vj6 z?qQs6^e0l!!Lvq40|58iogNAvVv{fo!SIVx9oE7QsriiI@Kx?1PQ&9p1#VoCpE=s!GxDCm%ANBaIDvx<#PR4K=&{#8I7ioZ{h01F#6ifW})B#8JJx_L1Kqg zWQ;~r$RR%a#(YNi;c@_^aueHczSLKd0sR@4K$Zy37{*+hZirGHOq@c3qH;yCBT zGMZ+f*~3?;GxT;hH8xqTHgHV!b{D0NmCfd!>%Aq~Gu^>v@iQ>Hih>7ZU_!|>mp}t{ z0Ay@7yW`PD^8Q#sV&a}eJa>7M=x@2&?8xvu?{GMxLA+etg}G!{QWk2{+g&TelKI@E zl-j5J)l6BawzLiw$fUg)8DSXK>Z;KlEI!k7aX(|X+nZKIWs!*6-F`HhV&XO)wJsJG zz~V(xYrWor<`ATM+#WxZ3OZje5+^to1rNx8D>}X;Va^ykY&P2{6A_c>df3Fi3%cPzdsgYz3q_$;R~^TZDsqvC`ydb> z48||GoJySIoSUt-m-Y{_<#JuAHllPdNBEKi_BC~qcq1UAEL1y2k1t7&K~NZmgTYvC zwCb|j(mK26cY&1B%f%f}W{Rr8eG1`4QL1a2k;vpmx1i(cXfz#9W@xtJRK2bPGMcUS zi*A67LQz2b+wSxpi(&L9(>Z!wFD8UnUqc3^)Hz3gZusN8|3QcF_Q}4G>w30*0u%5* zvV}hqfBR?2ADrpl_V?_DpjpWF9GedvObU8A9j40o{ksKLs dX6TQZ?r)sUBckz)5AOf~002ovPDHLkV1hjFo9F-l literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterAccountAndPassword.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterAccountAndPassword.png new file mode 100644 index 0000000000000000000000000000000000000000..db64c11f1c40dc911d96350ca72da0a0ca5b8a99 GIT binary patch literal 11442 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 Ws;!?inmQEiO{E7%Xw}?DM*J5kIIPJ4 literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterGame2.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/EnterGame2.png new file mode 100644 index 0000000000000000000000000000000000000000..621ce701bc42c01f94448e6e703974502cf15453 GIT binary patch literal 2781 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 beI@2!K%_SF`iBy^00000NkvXXu0mjf0YsLf literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Login.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Login.png new file mode 100644 index 0000000000000000000000000000000000000000..25327a114f6afb8ac60d19da2ffb7ad33ae2299c GIT binary patch literal 877 zcmV-z1CsoSP)Hl(La-1O{0Hm>K{Ty}go6Vmh0RJX5O6Vt zMMO}uf(W9;9R#zO-8Y9!9G)-HJH2eMxM?yw?>pbmyqPy)8Z7IHyWHS)i;Cp*}&(F{6#1!hHNlYATW0*l*kXa{bb5AnR-DlM{JhvJxm{8-Jv}WoXJ=<(oMcAwCYo1!ZWxBD zs*0lE%4}?G93LOQyu1`f$H&K`qoY{B7;9>3nwXfly1J@l+qNB#$D`4xrfK{8`!Wa4 zd6^9W7Z(?@Ac-|&th>AW^z`)PjnVwZmBUl91fFWyuZH}w?CiH zlegmX^0K@^<^Q#R+4EQ|M%a-^$sS}vFC?Cca1{C+=qewe1&)6*lOLqkJ1H#daM zIR}7TE;lnX)7sjK!EJ4Ai;Ii5x3`YtRA-A(UDq?2Or@{LOxN{PDphGg969H@u5WH` zZf|c_=8DDTyv2|>kE(2u*;^s(D3yN{zhV;$w?BUa{Nxc#Ei0g@00000NkvXXu0mjf D^F6#f literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/LoginOther.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/LoginOther.png new file mode 100644 index 0000000000000000000000000000000000000000..a28f23e459d904681e8113991c35b618dbb71b58 GIT binary patch literal 3310 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 ILORF(57DnL2><{9 literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Menu.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/Menu.png new file mode 100644 index 0000000000000000000000000000000000000000..0d41a3cd9167276b88b531bc48680ff9db478965 GIT binary patch literal 1408 zcmV-`1%LX9P)3^~@PXqHtWXR1>L@`inM?!;5THQ>1Ov#F z=iTLHAa|GJF1bWI{R8T8x*Q=nGK@1jnO}Cl`}^!~ckj2mf;bWWFSeZ^;24I)@b6S2 z2u=7BM3BUSBTi>`&x1d@x&@!fKn#Oo*d`f%K$)qTT zu!TY)Yj3Zr(!fT;lAb*I69|$F9!v~J(vpJk^6&S%OBFiN?xe98p;*>@{f^z%Z@2Z2 z3=MeRz42`<0Ukdo7C}<` zx$Hd>yy~&Ecl@HL2J3$)hAYUD$P0=q^#;>~%gIM_j}O<>n#EGJD6>$w46dD(r%;ri zdERGbVf$iQ_DtXFxL>N&W#=n1oH4HJgSJQ_S5_Dtc3X6R$`UOmiyDmgME%% zBa+DV2D9hgB$I8woeu&s3=vx!Zzsp<8m@??>LYtk>L@vOvi)x7KGL_n2U93C;INl# zjYr!NNhG0IrZ<>}1|54beItF{>u7N8(WA%5OSLK2?0BhGQ&nd;cgb+BQCnSKrqbsW zs+rF&)jldy>055x*+&9@fU!6?Wvpw;k*bqi3=a(e4Lih0Xnn%vyw-BNuvjC?WwJ0R z1x1w=+Ot5PoFhO38eDC^+bJ#4aAAMFd;tX9$so2i+~dO)+Ou3(c7d{>xDx2olGqQz zZtMS9UJcUHqlb?{PcDf)-KQtVN|ibglPFi7m8Ssuv?So@{j6K9sr_O1;IOoJfSy1A zGJ+raU}^6Fr0fD^VTlIl`A9(Y&(ds7tvNeiX=-c%l}SbvLn5JdyR9FP@{20enpz;s zh(w|gvbJ#Ac(LmA1<*6eh+@dj_U6!_1CUN+viFj_kSKG*Le>|qHs4Na0)L|zf(BRn ztX7sJ%9R`InkHP%uY!Z;6X?k`c29bULZNN{3b&qXM==Ci^;~IgWl7Q!O;^{0y&RDt ziR+PTgT25`-Gs|oBC8ULWj_|EE?jJ0S(@dD58NX-ZHM2S0cj_^xiI&})Y!seYHH2n zqeGlr?oAM(jCMpi{4NQ`L+i6s`*; zHp{(*xi|Yph9NZWqw$X!#RO)F4Ps||)4RSnH~qK6Zfm-Ft6XcWs%f~{+BP=wI5dH)KCw4-9WKBThUeK1#h=kTZBFmdA@0UH(^Rw=;k=K1ztEIieeChht=G)Jn zKK*i?j%;nP0|HKH9APj-kl6MyFMv1^*nMuKUH>SC1fUP|Gw#7x_JPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D3oS`RK~z{rt(a|$ zRmXY9e=~E=xo>;-<+97Z5^u1vjcw8-1-ngS@&(p*>RTY60ITEU3jNBSp zJ1ugeG(rX$Y&TWi5+!nxsZ?SNb&L~icVX9Y))r1e+#oQ^!tUO?ckk{!_q@#X!<>8B zT^1ax{Y!tHbMKjX=9y>ad6^MWEjG%LXw7vIIbeXuz|o^8_{KLMrMbBooD`)}iRbt2 z<*qwdG}>zeO4vUCdH5Jd{`ouPayjn3_m5e>emzcWA!+GC7=qi>=nD`<;J6S524S=@ z7=9-uV-1xm?AWn`mws}B*49=6EeL{uaB7l`8#l6L^VcY}NoJ~sbXovVsnq!3)JxoW z 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 O0000 zB2r0KO4kc@-}qXGc;I47!-tO6&X5rmWUdla%J^O`H^`nIMBh(O3!-=aL}jgj{zOaa zJMABJOh!i3;0i_-m1fh$lFQ$_i+`GBdSO51HEYo3cdOKGe1_cvZMH`;#0xcEBON#n z99S>;ifGXYK$xiSQKXk=2r49-LI@-=|Na$hsjy4{o7H0*RnX&N6#pOtHVF+Fw6$i^V&&NDjQJD<2~@8krvw3J~aibzU=mX?iVNo*lykI%9nUoGsp zjCt_mTd%g$3`TFm{=O;--H{vpQJ67oN?B99yw9YTze_f(Vd9O_*HhVCrj?37=x~Nh zZd@2>u`~Bg8;|Dv$gIQUbHGLvz=W%EOxsy>5reP1q0{B^+AJ~9VMCf8)*o9DvbmX= zWtwg89K359*il7V-|c;J{ok8}35D7?GZA0DPrNBBiydV1`a(w(VbzO$EljRbb!=R_ zx`h<5d-8V!NlZTjw3dp2&fEpJt>IPg)z?;RY!<4zLML5*)8XNt8UFEja26>l+_=`l z*D{Be>-4t4ZAqFLjA$mIU?;|Je_NkLiayEx@7_WtXzYNM{mLg)Pm$+4?LFi1_73|N z@-B6Cb-b&0vC(*@@fJE(wn<2@tOboC2Va?Tql`NqTpf;0b)ZuMVm)$ciwjqk3EP zL<8@|Ob+9ViN#bZ!c6z3-@@^A9Et8erGXaTM-0iSZtuMJCcdsmWslB$+?JhMMHZfi zUAB5+h2)XhkEI)M;Ve2rbvVTI$My9WTo>-^%97(doNi-7=E&ynT*M>Gdz_1=oEkHhMi~(_S{#$!dRf-+(@Za#S)3DjDtm9@0eyt{_Swa?VYwu7HMNz{Bo@^$r7oQi?k8`~R_ zu+6M5pB~kgaSSCT>ZP=F4OTA%VO}o?O-8++2o4J4h_H$y0x5o#xVmjc1L3B>lq|&e z^1Y;FO~3)urU+6~gY^h8(P%zQ=ei@^B|rG�FV7iXGf=aDV*OVy+x;ra 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 VCWV~G1H>s5B{_B3a%r=W{{e}jJcR%N literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/SelectAccount.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/SelectAccount.png new file mode 100644 index 0000000000000000000000000000000000000000..5550489fc5608815d45ee4556f6230ef431b3d2a GIT binary patch literal 2671 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 literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/SwitchAccount.png b/repo/js/AccountSwitchStateMachine/assets/RecognitionObjects/SwitchAccount.png new file mode 100644 index 0000000000000000000000000000000000000000..b895ab39190563b31733f397ef616d43613f5604 GIT binary patch literal 706 zcmV;z0zLhSP)WBq{3SJccAm<<>t|5=8y-EQ79A+2_5n~91aWygmAG~ zFbs45H4lfw9LHfWlx3wIYxizE9_M);LTDBv8jT8qfZ7C87{y`{sT*A9xm*q{J313h zVT?v2m&?^`HnmG-Sgy3vvc~|1>HpdX^$f=MJ`h6HdFS{0DT>l9Y2?K2;k1lH=g$%U ziCz={P_NgI#NX%DiJls4&D^^IVhDmzsZ`Fm;{*bM({~)XUd$NmYS0XIr1@Vk*1y)4 z@7!F~o*I?3KQ;G*`*}HjV|o5+aQ*3nD2hf&BOOr`uRp%;y=0kw$I@cEEw+0xd_7+- zgw1BJ;l{^8NfI`j`H~w9FX?`V>38TCcZ^=E-)phmCl~#E@x|{VZq~P7 oW?zR_lc$3&!#<<80EX@S26S$n>Fl57w*UYD07*qoM6N<$f+?>@r~m)} literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/states.json b/repo/js/AccountSwitchStateMachine/assets/states.json new file mode 100644 index 000000000..158c1e18e --- /dev/null +++ b/repo/js/AccountSwitchStateMachine/assets/states.json @@ -0,0 +1,412 @@ +[ + { + "name": "mainUI", + "description": "处于主界面", + "transitions": [ + { + "targetState": "menuUI", + "action": "keyPress('VK_ESCAPE'); await sleep(1000);" + } + ], + "detection": { + "conditions": [ + { + "id": "paimon", + "template": "assets/RecognitionObjects/Paimon.png", + "region": { + "x": 0, + "y": 0, + "width": 150, + "height": 150 + } + } + ], + "logic": "paimon" + } + }, + { + "name": "menuUI", + "description": "处于菜单界面", + "transitions": [ + { + "targetState": "mainUI", + "action": "await genshin.returnMainUi();" + }, + { + "targetState": "prepareToLogOut", + "action": "click(50, 1025); await findAndClick('assets/RecognitionObjects/PrepareToLogOut.png', false);" + } + ], + "detection": { + "conditions": [ + { + "id": "menu", + "template": "assets/RecognitionObjects/Menu.png", + "region": { + "x": 0, + "y": 0, + "width": 150, + "height": 150 + } + }, + { + "id": "prepareToLogOut", + "template": "assets/RecognitionObjects/PrepareToLogOut.png", + "region": { + "x": 646, + "y": 488, + "width": 639, + "height": 97 + } + } + ], + "logic": "menu && !prepareToLogOut" + } + }, + { + "name": "prepareToLogOut", + "description": "预备退出登录", + "transitions": [ + { + "targetState": "menuUI", + "action": "await findAndClick('assets/RecognitionObjects/PrepareToLogOut.png');" + }, + { + "targetState": "loginScreen", + "action": "await findAndClick('assets/RecognitionObjects/PrepareToLogOut.png'); await sleep(500); await findAndClick('assets/RecognitionObjects/SwitchAccount.png', false, 30000);" + } + ], + "detection": { + "conditions": [ + { + "id": "prepareToLogOut", + "template": "assets/RecognitionObjects/PrepareToLogOut.png", + "region": { + "x": 646, + "y": 488, + "width": 639, + "height": 97 + } + } + ], + "logic": "prepareToLogOut" + } + }, + { + "name": "loginScreen", + "description": "登录界面", + "transitions": [ + { + "targetState": "exitAccount", + "action": "await findAndClick('assets/RecognitionObjects/SwitchAccount.png');" + }, + { + "targetState": "mainUI", + "action": "click(10, 10); let attempts = 0; const maxAttempts = 30; while (!await findAndClick('assets/RecognitionObjects/Paimon.png', false, 200) && attempts < maxAttempts) { attempts++; await sleep(5000); click(10, 10); }" + } + ], + "detection": { + "conditions": [ + { + "id": "switchAccount", + "template": "assets/RecognitionObjects/SwitchAccount.png", + "region": { + "x": 1783, + "y": 940, + "width": 100, + "height": 100 + } + }, + { + "id": "exit", + "template": "assets/RecognitionObjects/Exit.png", + "region": { + "x": 911, + "y": 600, + "width": 341, + "height": 173 + } + }, + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "switchAccount && !exit && !confirm" + } + }, + { + "name": "exitAccount", + "description": "退出账号界面", + "transitions": [ + { + "targetState": "enterGame", + "action": "await findAndClick('assets/RecognitionObjects/Exit.png');" + } + ], + "detection": { + "conditions": [ + { + "id": "exit", + "template": "assets/RecognitionObjects/Exit.png", + "region": { + "x": 911, + "y": 600, + "width": 341, + "height": 173 + } + }, + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "exit && !confirm" + } + }, + { + "name": "enterGame", + "description": "进入游戏或登录其他账号", + "transitions": [ + { + "targetState": "selectAccount", + "action": "click(974,496);" + }, + { + "targetState": "enterAccountAndPassword", + "action": "await findAndClick('assets/RecognitionObjects/LoginOther.png');" + }, + { + "targetState": "loginScreen", + "action": "await findAndClick('assets/RecognitionObjects/EnterGame.png');" + } + ], + "detection": { + "conditions": [ + { + "id": "enterGame", + "template": "assets/RecognitionObjects/EnterGame.png", + "region": { + "x": 643, + "y": 565, + "width": 652, + "height": 186 + } + }, + { + "id": "loginOther", + "template": "assets/RecognitionObjects/LoginOther.png", + "region": { + "x": 643, + "y": 565, + "width": 652, + "height": 186 + } + }, + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "enterGame && loginOther && !confirm" + } + }, + { + "name": "selectAccount", + "description": "选择账号界面", + "transitions": [ + { + "targetState": "enterGame", + "action": "click(957, 404);" + } + ], + "detection": { + "conditions": [ + { + "id": "enterGame", + "template": "assets/RecognitionObjects/EnterGame.png", + "region": { + "x": 643, + "y": 565, + "width": 652, + "height": 186 + } + }, + { + "id": "loginOther", + "template": "assets/RecognitionObjects/LoginOther.png", + "region": { + "x": 643, + "y": 565, + "width": 652, + "height": 186 + } + }, + { + "id": "selectAccount", + "template": "assets/RecognitionObjects/SelectAccount.png", + "region": { + "x": 642, + "y": 441, + "width": 644, + "height": 580 + } + }, + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "!enterGame && !loginOther && selectAccount && !confirm" + } + }, + { + "name": "enterAccountAndPassword", + "description": "输入账号密码界面", + "transitions": [ + { + "targetState": "noAccount", + "action": "click(1260, 259);" + } + ], + "detection": { + "conditions": [ + { + "id": "enterAccountAndPassword", + "template": "assets/RecognitionObjects/EnterAccountAndPassword.png", + "region": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + } + }, + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "enterAccountAndPassword && !confirm" + } + }, + { + "name": "ageConfirmation", + "description": "适龄提示界面", + "transitions": [ + { + "targetState": "noAccount", + "action": "await findAndClick('assets/RecognitionObjects/Confirm.png');" + } + ], + "detection": { + "conditions": [ + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "confirm" + } + }, + { + "name": "noAccount", + "description": "无账号登录界面", + "transitions": [ + { + "targetState": "enterGame", + "action": "await findAndClick('assets/RecognitionObjects/Login.png');" + } + ], + "detection": { + "conditions": [ + { + "id": "login", + "template": "assets/RecognitionObjects/Login.png", + "region": { + "x": 1776, + "y": 934, + "width": 100, + "height": 100 + } + }, + { + "id": "enterGame", + "template": "assets/RecognitionObjects/EnterGame.png", + "region": { + "x": 643, + "y": 565, + "width": 652, + "height": 186 + } + }, + { + "id": "selectAccount", + "template": "assets/RecognitionObjects/SelectAccount.png", + "region": { + "x": 642, + "y": 441, + "width": 644, + "height": 580 + } + }, + { + "id": "enterAccountAndPassword", + "template": "assets/RecognitionObjects/EnterAccountAndPassword.png", + "region": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + } + }, + { + "id": "confirm", + "template": "assets/RecognitionObjects/Confirm.png", + "region": { + "x": 734, + "y": 826, + "width": 439, + "height": 116 + } + } + ], + "logic": "login && !enterGame && !selectAccount && !enterAccountAndPassword && !confirm" + } + } +] diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/0.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/0.png new file mode 100644 index 0000000000000000000000000000000000000000..aefdf65e530ab710f6f7f8638b0c2ff6424a4918 GIT binary patch literal 789 zcmV+w1M2*VP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0;)+wK~y+Tb(2qT zlvNbQfA_w3rZSzjblRCt3o1c6)1d<)F~$m|Xrf)T4JNE~p;1HJ7)eZA`yC8RK7jHk z6%CCh@(FAW|HQ&86Tt!!gS68XGxOg2xEOrnEIgZg&+~iE=HxlX4eV27!CFHU86snd zoWTMZgZF_Dzz30d5d$hU_dRrlclz(vY zQx(Q*vZZ2yrG(lY6nn(UG3P&8;RO zbpIg;Vc2}K)#E7^N3prwj#LOC;1dr9Bq0MxwA$^S!Twwh+y0Met;HCFdDa_a0FlXL zQV&3@)y6iP+dX1F-;V(MY{+29=W{)tt!5J&$BiCwV!VtsU<_E(&G2}++~bMk2KJ}5 zD?Q?YdJStW8E0|Mf`NLymh#*29iD7%V}HFBbM5-g6f7;y;~YHK7t!Y|!#f9=o1IDd z+4JAxB^Vy7YU#v#X{J>DAc<4V9|O6((hN3{T2M-6K9QUE|azA8~B%Fry;{y!YI_ zyTPR&F7xH-Gi+{cPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0dh%1K~yMHb&$_W zlyMY>pL2fS%z%WJkS2)~Ihb|@UP8;jYZ$Oi5Ctw=C_;-aA!ySkgovU=H{nRG(y66? zAV>^!{T6;3TVjlyZvoJKvd z^5`KO>rY69!`Ng~<2M&{eR@IDvk%l-eX6hf9ZAwo`-^Vex-Z5io0^zfRO|6`9dy2H zFgxu%P2FA;^@(|Hzx$-~^NS%slG?j_nweXY*HWUj`i$n%GMig38I2@dKm?SRnOCph zaM1ZSf*GE3aG8TLf-1zx$&X>g4DZtlKsE#bC;~S#Bmf~a${@fc^~S$j0L*(j!~P2< z1DB#Jx@sIqZ^z9XNthYEPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0-i}kK~y+Tg^+J( zl~o+aKj%Epb2sPRu6Mh;-p$;Y-aXs3B#lr(0uA+rTdb6bph*+Oe-IMB?{yRrM(+|s zWQ-Lni>*-KD8qjd*`F6$Vc|lq5v1NJy62wr?Zxs51nCQhADqwm9Dcu_3A4);1ctyG zg1}%ORsdsg-s2p2FPsxjU>X+nDhlQwHa29~zHN}_)~{i4v7K72#>~tOzL-44#c#d? zLE=0#we~7k?9=)UFX*@5uj_G{`uem=-A^mm9*gEeta4?os?|RwNzOUF{qA9H-14G! zjO^F-nLkqT_~<8U>#T@;sbBBB_hAag#wXO#wL1f7F+8*CNhZdRro!d#f8yYwx9}dU7f2$oV66zP<&6V7nL05> zM@I=s9Qk;ZfuYy9eWwP-Lqo2tg>A8N#XglQ>vVSde7fRae!Zr_mxh%u^{crMD_e-w z+!o6;<$D!HmL;8w`EueI%X+#2m_B!r;gJK}x;;z1F1)}wPZU~Ab0OBk#tbLF8fWEG z6#%N$n{3=P#NT&n)awrCK^_t(7Bm=a7#Q~K*^vqWAh zdG(cTn6^^CE=*5RUfz>Rwb@zj-+ur~uZS`ci*hX-IXVi3&K2_Br^EkWcI|yrCRgm! zsmU?&`P_fsJ|+?jy*z?xX^#aE29_{180Ncrv^po8^LW8O`Amn%TK@QQ00000NkvXX Hu0mjfJKS!? literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/3.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/3.png new file mode 100644 index 0000000000000000000000000000000000000000..f93f4f2b85a0feef57fd344da0cc22b9f9df5aa4 GIT binary patch literal 722 zcmV;@0xkWCP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0%u7?K~y+Tb&yL) zlwlNxpYQ)?yf#xhGdAN?TI$U2lqHGVHZDp8=|V;XZMq;qMRX&CYS+F{15qf5Hr)u* zlw4F4g3>|*!9vT7vLXJ0wdsR10CltiR-%ZKknQckh16o z@r7c-?_nyHlFsg4IexN3dU~%*I-Slf^z_}3lCbkfK5+P$gjXjd6s?uWnuMG=cOfS) zEoEh0yiqKGOlFDu4@O8YX|k^1YGGjT5x?h%nJ_8sKi{0BU)5fDWp5u>HK>91&j(a|x!r&1^ss-UDs@;wUQav$a4 zGJL)w8tUsfajczicqITcvv1kj(uVSdV&btC3JYTl%2MQc74;hvJnX-fOSs(GgLU&- z7iUi$#fT>B8q#T(tSd}ROtG-=DJM5J)MK?YH?gC28_QUpPd0FUrwP!L;z-H-_UinkNNi>sC8;M(tdzYFaSU* zm10ZNc6j{cdG7iDBANUqd-u1?^0K(t{X?S{7Z(=^l$PN4`|x_bhzN6Y^W47M&!Hoy z8GkW}HiGg6V`A9~@2|s(iVDeOGH7jZwV<`Z7$K{LAKS-cBm8K^a{vGU07*qoM6N<$ Ef-;drfB*mh literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/4.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/4.png new file mode 100644 index 0000000000000000000000000000000000000000..00a9a5f0f2d74285904d6a7f7022a213455f1db9 GIT binary patch literal 713 zcmV;)0yh1LP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0$xc(K~y+Tjg!l7 zO;HqwzqR(cw_Z)PY17UG2?-OE5^7KcA%Y;{5fMZGK~o_pnvzBt8A&}86DBm?LON2? zikP5bL_1N^=RNoAy$sHAd)twf>}0>bwSVieC8}I%W*{P%8O|w@gUBf=fPk410)zl@ zF<=;44ylt25*iw6xp(^-j~?9O;r&~zXYSh=oIl$_eSID0+PfGU8YW4S z86a8iOH!jq+1T8~;RAc)me zGD@HdIVVJsBcF4~_3k@%?C2qehK70Z@(q?nH>aY4eS3G6z_)$x`TS)N?~Q>EpO6rO zl?VPm_Tp79GBWbp^7;JiGXUnzn_HU4aZHkU0D7LhM5^nT+rL_-g-V;euC|8$cW+AI ziIZ(~-@Hem=<(jrVggeI&*W4#3Z6I#Bq5X_fL|kjC=@+$94Hh6S|}z=7CaLL&-i4* z#6LKm2FJ%Ic-`Aa90#U~fns842-E)&05iNd5}&TW4u0q0k>mXMIgB^U?iu11EVV4O vl7ou8BNr)47BAxK;J0)?7;zHtA#IMon5YT-9|*(n00000NkvXXu0mjf!4EI% literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/5.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/5.png new file mode 100644 index 0000000000000000000000000000000000000000..31506f2efaf2e5a12085d50860ad624216db88d1 GIT binary patch literal 807 zcmV+?1K9kDP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=!8?K~y+Tg^Ol{)A=)xKhWBeo%jfR*Q7e=s(F+qYR#*K;pg^4?t zHeFbV5);K(LYf9tXbhE3#{t?x$$$vV%=^C2b6gCWP&c04&AIoS+@I;q6qQU`43G#c zK>)@WK!5?jIZy0CUN{e?FJDqJVR-MrTRi^gBN%Hs`}fF8Nbu!XUo&&+G^B?{lq;0A zSgq;*^w{`I>dTJECaj@xWQ|$?4?-TK{}o0>e@BV&Huso zKb)Z1im=Xhg7RA`T3%lLFQ|1|S-qm?_Pn9Yrm_Zx$}+=S$8@=RhGw(LdTchxT&Pg_dl#}r@!k0X*8~D z>$Wl3#cB-J~CX`bAIddO=$r z9Mh9ey{HpE&vZTSz4pBNriSmUi0tOFzL`4OH6_V8w{8x@NE07?tjxx;a)pY5p7ikN z)5nfezj&F!fqrt?45?HK=N$F=B_^kivj6?V%$%G`=kb0UsC&O_`x{{kigv!y>Ty^H_=002ovPDHLkV1iGnfuH~Y literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/6.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/6.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f9e86949994e7ec676fe1168bf4bf05b1f195 GIT binary patch literal 814 zcmV+}1JV46P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0>eo}K~y+TRgqh0 zTvZf?zkN9~iJ4|HlgUi9jY2b*CfL#jF>0|s)CcL4FGYk}gW{$5RFo>E zU==|HDOdk!R@4R1QXA1R;c>y4ONPfc4Ju+L0-q*!LI!^?HMuH;)oHOCoUi0D6+1 zz=QXeFa$GFoY3;ZPh_(h08X6zk{`~@^5`QE)0awdbL|%M^S`rjVVNiroiKuzUVS~D zLz1f1)w}k0V(zy^m3Qq`I$sf|zWU}a`0tggnw~kTXAi%k_dohX&1NfROUr+1@V;^J z>)b*t*6R(8R`zIkWK2U_$8_)g<9cKI?U+l_p{XOXnM``~ui?to6&klUXtu>hGosaw z`1ZTgF$Z{j?u&&9%UA-ODRxpqAs^yl+) zDvm#-{I)Sw9-P$t;)R%1S8r%!bV3XiD%$tt)2dc)#BhDRrlmjrQms~xwVP|VwEw_C z_2(-xsa#pk8FJYSFTZ$*15fRzQZ5n%o_4##<-h-7_Ut*1e}0P9|JDd2VG;vH13lY&MA^2&0HFgwAIDAKOl(rxstf_W%F@07*qoM6N<$f^~w4bN~PV literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/7.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/7.png new file mode 100644 index 0000000000000000000000000000000000000000..51e92ee50cfcb7c2c99218c43bb484e239f4f414 GIT binary patch literal 605 zcmV-j0;2tiP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0rE*iK~y+Tb(70W zRZ$eie{1a{fsuk2wa7s)XwslEn*`Ob@%=n961BQZ?swa$H#hFTjy?tU}V7nfq>N~W*`w|#Ad`EJsDL9 z0b_khLJ3Tg)_$%hUmI9YkDrcWNBajgJNFA;&C`PsFwEh?xgl;`ze2sfz@a0j@rlV9 z78V=K*9+>41xw2f7K;YG2ljLA>M#Iz?%ro%X$i|V?UcKrB(ltD$&9UAx_I~IIkj2` zd0w#h;Bl7o90wytVeYsaS#WOOx=F3p0l>WnBjkC(zou|Wm>Iz#3r6q0J)9jpRTcJd zaN1%!C_c!5O}E5wo)E(_d!zIXg#;iI{|&8D>V7Io;hg22S=@t}kAWk&=kX zkSNn|GlCmu22Kz{sNh#)uStTaLu rv>|gsW}xyigh+zF%|wbIgkkJqimOH9@}00000NkvXXu0mjf7kU8x literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/8.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/8.png new file mode 100644 index 0000000000000000000000000000000000000000..fd404f0e28dea98ab13d6f5c50e8623dbf69eae0 GIT binary patch literal 854 zcmV-c1F8IpP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0_#acK~y+Tb(BkJ zlvfyr-*e9VNwt~GA2a?l8D^r!WNuiAh%`bbq473gg+N{Cq99r*gi^sS+JYOYg118G zri&upNDFq+cvsv67lI0{l_WNjL@AmJ$rw|TIP=f{pSO$P5e47n_q^x(-tSu+VO2L$ zN+2L5qym%_P*Ok$KnOq+pa#mmhSq?uH9VgXm3AYofP|6)wjD(0r+IAH(2S_#Km`;E zg%^16Z~(oRZs6JTarm0>JOCfoHZkO+xO4XxoG!fJ`{cT19y!|0kdtCnI7x(@6xX+G z;j3cNn0hZ=<;SgCxn^CChYla7*4nVHufJurlO$>)DgM~~vjMZS|8ZqgGoy(tBk?R_ z^;urLe9iEC_y1&VU7At|AQ7*_Yt7BiqgZmGH5U&9pir1Gyr#y66ao?un3$Y0AXXPe z=Z-w26i^bdtN=b~Z#8^ia0q|^LQazHn>*?IzKP@eKF^%{jXOGb^UBp5rd}+SxGDb` zBk?svaCws9Sccyn{DGe58PmHO9vS7%U3(abXBbO-M5)ALq@G_d;MD2!CepjI?GWk{ zagczgePCH#irMBI`}-f6?h6YGyzqN3M@OH&HI*cfo&1STEKAhJ((Jo^$AGEn0^2&a zGn&XUn#l6Yt^<7ea@KIq^VpKhQzjc%p*^?JL^^h&8$*wu!1XjrWsN^BUdR4#zBgPc zh0PspkPR#AO%VVd{5=Stgj@Dea(#H3xOuA&(}jZZglm^Uj*U;e^{}B~1(X0P0tywD zgrx+Qd{Bv{OO_hX^RaOt97}WX(U93;cx03tb6XjWXXwN;T(P>Dr_c5nJ~ubd^x9U! zj-=Vrww>b>lLoqOnf(L*@b3M;I5YF+Ft3V5e!llBhMW{dRX9m01?=h|_U+k){I(8c zQcVa3s{jC{l8fPyC+NF<2fv)^!P9@AL2E!%*p0N5gb;ugkdP7(LZH%85K_S{xzHrE g20X2y{l(wle_Rs*IlO%HRsaA107*qoM6N<$f=`c%LI3~& literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/assets/uid图片/9.png b/repo/js/AccountSwitchStateMachine/assets/uid图片/9.png new file mode 100644 index 0000000000000000000000000000000000000000..48483735d3e48d09a4f99389d781bd31058494b4 GIT binary patch literal 623 zcmV-#0+9WQP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0t87!K~yMHbJhl+zz5JW5v z;@}`+HCj_>z(q)_Ml2RLO-&rU_a^sy9nz$f;=7%5p5YvhFk)#Ywr;`clY{8*%ODgA z!m=z>Dhqh=>J8o(OK_C}#_vv0DaHS#@V3ZA&mN-Puq=yLM#tE@{UA>ajj&vv@8H?e zhZQ(}=N@C5dl-#p7~PcN;UjsvuG_&9%Xv9fK(k5wwOu$3Vy^NPpFUUGV|3upUrQhf z$0d|f0D*4{-`B#(`Y?n9Jeq_M0zwFAnzk0^2D0$z5SMYfKn~sxk#6Jd~X0@a{AIAUy002ov JPDHLkV1oPD348zm literal 0 HcmV?d00001 diff --git a/repo/js/AccountSwitchStateMachine/main.js b/repo/js/AccountSwitchStateMachine/main.js new file mode 100644 index 000000000..8af4ba5a5 --- /dev/null +++ b/repo/js/AccountSwitchStateMachine/main.js @@ -0,0 +1,1164 @@ +// 全局状态机配置对象 +let stateMachineConfig = null; + +// 全局游戏画面区域 +let gameRegion = null; + +// 主逻辑 +(async function () { + // 只有当未开启截图模式时,才检查 verifyUid 和 targetUid + if (!settings.screenshotMode && settings.verifyUid && !settings.targetUid) { + const msg = '已启用UID校验,但未填写目标UID,请检查配置'; + log.error(msg); + notification.error(msg); + return; + } + // 读取状态配置文件 + let statesData; + try { + const statesJsonText = file.readTextSync('assets/states.json'); + statesData = JSON.parse(statesJsonText); + } catch (e) { + log.error(`读取 states.json 失败:${e.message},状态机无法正常工作`); + throw e; + } + + if (!statesData || !Array.isArray(statesData)) { + const errorMsg = 'states.json 格式错误:应为状态对象数组'; + log.error(errorMsg); + throw new Error(errorMsg); + } + + // 将状态数组转换为对象,以状态名称为key + stateMachineConfig = {}; + for (const state of statesData) { + stateMachineConfig[state.name] = state; + } + log.info(`成功加载 ${statesData.length} 个状态配置`); + + // 检查是否进入截图模式 + if (settings.screenshotMode) { + await handleScreenshotMode(); + return; + } + + // 检查是否需要UID校验 + if (settings.verifyUid) { + log.info('开始:验证当前账号UID'); + const currentUid = await verifyCurrentUid(); + if (isUidMatch(currentUid, settings.targetUid)) { + log.info(`当前账号UID ${currentUid} 与目标UID ${settings.targetUid} 匹配,无需切换`); + notification.Send(`当前账号UID ${currentUid} 与目标UID ${settings.targetUid} 匹配,无需切换`); + return; + } + } + + // 根据是否开启UID校验决定最大尝试次数 + let attempts = 0; + const maxAttempts = settings.verifyUid ? 2 : 1; + let switchSuccess = false; + let notificationMessage = null; + + while (attempts < maxAttempts) { + attempts++; + log.info(`开始第 ${attempts} 次账号切换尝试`); + + // 用于保存通知消息 + let currentNotificationMessage = null; + let currentSwitchSuccess = false; + + // 判断是否跳过搜索,直接使用账号密码 + let useAccountPassword = settings.skipSearch; + + if (!useAccountPassword) { + // 尝试预加载账号图片模板 + let accountImageMat = null; + const accountImagePath = `accounts/${settings.targetUid}.png`; + + try { + accountImageMat = file.ReadImageMatSync(accountImagePath); + log.info(`成功预加载账号图片:${accountImagePath}`); + } catch (e) { + log.warn(`预加载账号图片失败:${accountImagePath},错误:${e.message}`); + log.warn('将使用账号密码方式登录'); + useAccountPassword = true; + } + + // 如果图片预加载成功,尝试使用图片查找方式 + if (!useAccountPassword) { + // 尝试查找账号图片分支 + log.info('开始:尝试使用账号图片查找方式'); + + // 首先定位到"进入游戏或登录其他账号"状态 + log.info('开始:尝试切换到 enterGame 状态'); + const enterGameResult = await goToState('enterGame'); + if (!enterGameResult) { + log.warn('失败:未能到达 enterGame 状态,将尝试使用账号密码方式'); + useAccountPassword = true; + } else { + log.info('成功到达 enterGame 状态'); + + // 检查当前界面是否存在目标账号图片(适用于只有一个账号的情况) + log.info(`尝试在 enterGame 状态查找账号图片:${accountImagePath}`); + const accountRo = RecognitionObject.TemplateMatch(accountImageMat, 0, 0, 1920, 1080); + accountRo.Threshold = parseFloat(settings.accountImageThreshold) || 0.9; + accountRo.InitTemplate(); + const uidFoundInEnterGame = await findAndClick(accountRo, false, 1000); + + if (uidFoundInEnterGame) { + log.info(`在 enterGame 状态找到账号图片:${settings.targetUid}.png`); + // 点击进入游戏按钮 + await findAndClick('assets/RecognitionObjects/EnterGame.png', true, 1000); + + // 定位到主界面 + log.info('开始:尝试切换到 mainUI 状态'); + const mainUIResult = await goToState('mainUI'); + if (mainUIResult) { + log.info('成功到达 mainUI 状态,账号切换完成'); + currentSwitchSuccess = true; + currentNotificationMessage = `使用账号图片方式成功切换到uid为${settings.targetUid}的账号`; + } else { + log.warn('失败:未能到达 mainUI 状态,将尝试使用账号密码方式'); + useAccountPassword = true; + } + } else { + log.info('在 enterGame 状态未找到账号图片,尝试展开账号列表'); + + // 定位到选择账号界面 + log.info('开始:尝试切换到 selectAccount 状态'); + const selectAccountResult = await goToState('selectAccount'); + if (!selectAccountResult) { + log.warn('失败:未能到达 selectAccount 状态,将尝试使用账号密码方式'); + useAccountPassword = true; + } else { + log.info('成功到达 selectAccount 状态'); + + // 使用预加载的图片模板进行查找 + log.info(`尝试查找并点击账号图片:${accountImagePath}`); + const accountRo = RecognitionObject.TemplateMatch(accountImageMat, 0, 0, 1920, 1080); + accountRo.Threshold = parseFloat(settings.accountImageThreshold) || 0.9; + accountRo.InitTemplate(); + const uidFound = await findAndClick(accountRo, true, 5000); + + if (uidFound) { + log.info(`成功点击账号图片:${settings.targetUid}.png`); + + // 定位到主界面 + log.info('开始:尝试切换到 mainUI 状态'); + const mainUIResult = await goToState('mainUI'); + if (mainUIResult) { + log.info('成功到达 mainUI 状态,账号切换完成'); + currentSwitchSuccess = true; + currentNotificationMessage = `使用账号图片方式成功切换到uid为${settings.targetUid}的账号`; + } else { + log.warn('失败:未能到达 mainUI 状态,将尝试使用账号密码方式'); + useAccountPassword = true; + } + } else { + log.warn(`未找到账号图片:${settings.targetUid}.png,将尝试使用账号密码方式`); + useAccountPassword = true; + } + } + } + } + } + } else { + log.info('已勾选跳过搜索,直接使用账号密码登录'); + } + + // 账号密码分支 + if (useAccountPassword) { + log.info('开始:使用账号密码登录方式'); + + // 检查账号密码是否为空 + if (!settings.account || !settings.password) { + log.error('账号或密码为空,无法使用账号密码方式登录'); + log.error(`账号:${settings.account ? '已设置' : '未设置'},密码:${settings.password ? '已设置' : '未设置'}`); + currentNotificationMessage = '切换失败,账号或密码为空'; + } else { + // 切换到"输入账号密码"状态 + log.info('开始:尝试切换到 enterAccountAndPassword 状态'); + const result = await goToState('enterAccountAndPassword'); + if (!result) { + log.warn('失败:未能到达 enterAccountAndPassword 状态'); + currentNotificationMessage = '切换失败,未能到达输入账号密码界面'; + } else { + log.info('成功到达 enterAccountAndPassword 状态'); + + // 循环点击同意按钮,直到找不到为止 + log.info('开始循环点击同意按钮'); + let agreeFound = true; + while (agreeFound) { + // 创建带阈值的识别对象 + const agreeRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync('assets/RecognitionObjects/Agree.png'), + 0, 0, 1920, 1080 + ); + agreeRo.Threshold = 0.9; + agreeFound = await findAndClick(agreeRo, true, 1000); + if (agreeFound) { + log.info('点击了同意按钮,继续查找'); + await sleep(500); + } else { + log.info('未找到同意按钮,循环结束'); + } + } + + // 输入账号 + log.info('开始输入账号'); + let accountInputFound = true; + while (accountInputFound) { + accountInputFound = await findAndClick('assets/RecognitionObjects/EnterAccount.png', true, 1000); + if (accountInputFound) { + log.info('点击了账号输入框'); + await sleep(100); + // 输入账号 + inputText(settings.account); + log.info('已输入账号'); + await sleep(500); + // 检查输入框是否还存在(验证输入是否成功) + const checkRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync('assets/RecognitionObjects/EnterAccount.png'), + 0, 0, 1920, 1080 + ); + let tempRegion = null; + try { + tempRegion = captureGameRegion(); + accountInputFound = tempRegion.find(checkRo).isExist(); + } catch (e) { + log.error(`检查账号输入框时出错:${e.message}`); + accountInputFound = false; + } finally { + if (tempRegion) { + tempRegion.dispose(); + } + } + if (accountInputFound) { + log.warn('账号输入框仍然存在,可能输入失败,重试'); + } else { + log.info('账号输入完成'); + } + } else { + log.warn('未找到账号输入框'); + } + } + + // 输入密码 + log.info('开始输入密码'); + let passwordInputFound = true; + while (passwordInputFound) { + passwordInputFound = await findAndClick('assets/RecognitionObjects/EnterPassword.png', true, 1000); + if (passwordInputFound) { + log.info('点击了密码输入框'); + await sleep(100); + // 输入密码 + inputText(settings.password); + log.info('已输入密码'); + await sleep(500); + // 检查输入框是否还存在(验证输入是否成功) + const checkRo = RecognitionObject.TemplateMatch( + file.ReadImageMatSync('assets/RecognitionObjects/EnterPassword.png'), + 0, 0, 1920, 1080 + ); + let tempRegion = null; + try { + tempRegion = captureGameRegion(); + passwordInputFound = tempRegion.find(checkRo).isExist(); + } catch (e) { + log.error(`检查密码输入框时出错:${e.message}`); + passwordInputFound = false; + } finally { + if (tempRegion) { + tempRegion.dispose(); + } + } + if (passwordInputFound) { + log.warn('密码输入框仍然存在,可能输入失败,重试'); + } else { + log.info('密码输入完成'); + } + } else { + log.warn('未找到密码输入框'); + } + } + + // 点击进入游戏按钮,直到消失 + log.info('开始点击进入游戏按钮'); + let enterGame2Found = true; + while (enterGame2Found) { + enterGame2Found = await findAndClick('assets/RecognitionObjects/EnterGame2.png', true, 1000); + if (enterGame2Found) { + log.info('点击了进入游戏按钮,继续查找'); + await sleep(500); + } else { + log.info('未找到进入游戏按钮,循环结束'); + } + } + + // 切换到主界面 + log.info('开始:尝试切换到 mainUI 状态'); + const mainUIResult = await goToState('mainUI'); + if (mainUIResult) { + log.info('成功到达 mainUI 状态,账号切换完成'); + currentSwitchSuccess = true; + currentNotificationMessage = `使用账号密码方式成功切换到uid为${settings.targetUid}的账号`; + } else { + log.warn('失败:未能到达 mainUI 状态'); + currentNotificationMessage = '切换失败,未能到达主界面'; + } + } + } + } + + // 统一处理:回到主界面 + if (!currentSwitchSuccess) { + log.info('尝试返回主界面'); + const backToMainResult = await goToState('mainUI'); + if (backToMainResult) { + log.info('已返回主界面'); + } else { + log.warn('未能返回主界面'); + } + } + + // 检查是否需要UID校验 + if (settings.verifyUid) { + log.info('开始:验证切换后的账号UID'); + const currentUid = await verifyCurrentUid(); + if (isUidMatch(currentUid, settings.targetUid)) { + log.info(`切换后的账号UID ${currentUid} 与目标UID ${settings.targetUid} 匹配,切换成功`); + currentSwitchSuccess = true; + if (!currentNotificationMessage) { + currentNotificationMessage = `成功切换到uid为${settings.targetUid}的账号`; + } + } else { + log.warn(`切换后的账号UID ${currentUid} 与目标UID ${settings.targetUid} 不匹配,需要重新尝试`); + currentSwitchSuccess = false; + currentNotificationMessage = `切换失败,当前账号UID ${currentUid} 与目标UID ${settings.targetUid} 不匹配`; + } + } + + // 如果切换成功,跳出循环 + if (currentSwitchSuccess) { + switchSuccess = true; + notificationMessage = currentNotificationMessage; + break; + } + + // 如果是最后一次尝试,保存失败消息 + if (attempts === maxAttempts) { + notificationMessage = currentNotificationMessage; + } + } + + // 发送通知 + if (notificationMessage) { + if (switchSuccess) { + notification.Send(notificationMessage); + } else { + notification.error(notificationMessage); + } + } +})(); + +/** + * 判断当前所属状态 + * 使用广度优先遍历(BFS)从上一个状态开始查找,提高查找效率 + * + * @param {string|null} previousState - 上一个状态,null表示首次运行或未知状态 + * @returns {string|null} 当前状态标识,如果无法确定则返回null + */ +async function determineCurrentState(previousState = null) { + if (!stateMachineConfig) { + log.error('状态机配置未加载'); + return null; + } + + // 构建BFS遍历序列 + const stateSequence = buildBFSSequence(previousState); + + // 最多尝试3次 + const maxAttempts = 30; + let currentMousePos = null; // 保存当前鼠标位置,初始为空表示未知 + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + // 遍历所有状态进行匹配 + for (const stateName of stateSequence) { + const state = stateMachineConfig[stateName]; + if (!state || !state.detection) continue; + + // 评估该状态的检测条件,传入当前鼠标位置 + const result = await evaluateDetectionConditions(state.detection, currentMousePos); + if (result.isMatch) { + return stateName; + } + // 更新鼠标位置 + currentMousePos = result.mousePos; + } + } catch (e) { + log.error(`识别状态时出错:${e.message}`); + } + + if (attempt < maxAttempts) { + log.warn(`第 ${attempt} 次尝试未识别到当前状态,1秒后重试...`); + await sleep(1000); + } + } + + log.error(`经过 ${maxAttempts} 次尝试仍无法识别当前状态`); + return null; +} + +/** + * 构建BFS遍历序列 + * 从previousState开始广度优先遍历,然后处理剩余未遍历节点 + * + * @param {string|null} previousState - 起始状态 + * @returns {string[]} 状态遍历序列 + */ +function buildBFSSequence(previousState = null) { + if (!stateMachineConfig) { + log.error('状态机配置未加载'); + return []; + } + + const allStates = Object.keys(stateMachineConfig); + if (allStates.length === 0) return []; + + const visited = new Set(); + const sequence = []; + const queue = []; + + // 如果有指定起始状态,从它开始BFS + if (previousState && stateMachineConfig[previousState]) { + queue.push(previousState); + visited.add(previousState); + } + + // BFS遍历 + while (queue.length > 0) { + const currentState = queue.shift(); + sequence.push(currentState); + + const state = stateMachineConfig[currentState]; + if (state && state.transitions) { + for (const transition of state.transitions) { + const targetState = transition.targetState; + if (targetState && !visited.has(targetState) && stateMachineConfig[targetState]) { + visited.add(targetState); + queue.push(targetState); + } + } + } + } + + // 处理未遍历到的节点(从第一个未遍历节点开始继续BFS) + for (const stateName of allStates) { + if (!visited.has(stateName)) { + // 从这个未遍历节点开始新的BFS + const subQueue = [stateName]; + visited.add(stateName); + + while (subQueue.length > 0) { + const currentState = subQueue.shift(); + sequence.push(currentState); + + const state = stateMachineConfig[currentState]; + if (state && state.transitions) { + for (const transition of state.transitions) { + const targetState = transition.targetState; + if (targetState && !visited.has(targetState) && stateMachineConfig[targetState]) { + visited.add(targetState); + subQueue.push(targetState); + } + } + } + } + } + } + + return sequence; +} + +/** + * 评估状态的检测条件 + * 根据配置中的conditions和logic表达式判断当前是否处于该状态 + * + * @param {Object} detection - 检测配置对象,包含conditions数组和logic表达式 + * @returns {boolean} 是否匹配该状态 + */ +async function evaluateDetectionConditions(detection, currentMousePos) { + if (!detection || !detection.conditions || detection.conditions.length === 0) { + return { isMatch: false, mousePos: currentMousePos }; + } + + // 检查鼠标位置是否需要移动 + let newMousePos = currentMousePos; + + // 检查当前鼠标位置是否离所有识别区域均超过50x50 + const isMouseSafe = checkMousePosition(currentMousePos, detection.conditions); + + if (!isMouseSafe) { + // 寻找合适的鼠标位置 + newMousePos = findSafeMousePosition(detection.conditions); + if (newMousePos) { + moveMouseTo(newMousePos.x, newMousePos.y); + await sleep(50); + } else { + // 没有找到合适的位置,移动到默认位置 + moveMouseTo(10, 10); + await sleep(50); + newMousePos = { x: 10, y: 10 }; + } + } + + // 先检查并释放gameRegion + if (gameRegion) { + gameRegion.dispose(); + } + + // 捕获游戏画面 + gameRegion = captureGameRegion(); + + try { + // 计算每个条件的值 + const conditionValues = {}; + + for (const condition of detection.conditions) { + const { id, template, region } = condition; + + // 创建识别对象 + const templatePath = template; + const ro = RecognitionObject.TemplateMatch( + file.ReadImageMatSync(templatePath), + region.x, region.y, region.width, region.height + ); + + // 执行识别 + const result = gameRegion.find(ro); + conditionValues[id] = result.isExist(); + } + + // 使用logic表达式计算最终结果 + // 将logic表达式中的条件ID替换为实际值 + let logicExpression = detection.logic; + for (const [key, value] of Object.entries(conditionValues)) { + // 使用正则替换完整的条件名,避免部分匹配 + const regex = new RegExp(`\\b${key}\\b`, 'g'); + logicExpression = logicExpression.replace(regex, value); + } + + // 计算逻辑表达式 + try { + const isMatch = eval(logicExpression); + return { isMatch, mousePos: newMousePos }; + } catch (e) { + log.error(`逻辑表达式计算失败: ${detection.logic} -> ${logicExpression}, 错误: ${e.message}`); + return { isMatch: false, mousePos: newMousePos }; + } + } catch (e) { + log.error(`评估检测条件时出错:${e.message}`); + return { isMatch: false, mousePos: newMousePos }; + } finally { + if (gameRegion) { + gameRegion.dispose(); + } + } +} + +/** + * 检查鼠标位置是否离所有识别区域均超过50x50 + * @param {Object|null} mousePos - 当前鼠标位置 {x, y} + * @param {Array} conditions - 检测条件数组 + * @returns {boolean} 是否安全 + */ +function checkMousePosition(mousePos, conditions) { + if (!mousePos) { + return false; // 未知位置,认为不安全 + } + + for (const condition of conditions) { + const { region } = condition; + const { x, y, width, height } = region; + + // 检查鼠标是否在识别区域附近50像素内 + if (mousePos.x >= x - 50 && mousePos.x <= x + width + 50 && + mousePos.y >= y - 50 && mousePos.y <= y + height + 50) { + return false; + } + } + + return true; +} + +/** + * 在10x10到1910x1070间隔10x10的点阵中寻找安全的鼠标位置 + * @param {Array} conditions - 检测条件数组 + * @returns {Object|null} 安全的鼠标位置 {x, y},如果没有找到返回null + */ +function findSafeMousePosition(conditions) { + for (let x = 10; x <= 1910; x += 10) { + for (let y = 10; y <= 1070; y += 10) { + let isSafe = true; + + for (const condition of conditions) { + const { region } = condition; + const { x: rx, y: ry, width, height } = region; + + // 检查该点是否在识别区域附近50像素内 + if (x >= rx - 50 && x <= rx + width + 50 && + y >= ry - 50 && y <= ry + height + 50) { + isSafe = false; + break; + } + } + + if (isSafe) { + return { x, y }; + } + } + } + + return null; // 没有找到安全位置 +} + +/** + * 前往指定状态 + * 判断当前状态后,执行相应操作前往目标状态,每步执行后重新判断当前状态 + * + * @param {string} targetState - 目标状态名称 + * @param {string|null} previousState - 上一个状态(可选),用于判断状态起点 + * @returns {boolean} 是否成功到达目标状态 + */ +async function goToState(targetState, previousState = null) { + if (!stateMachineConfig) { + log.error('状态机配置未加载'); + return false; + } + + if (!stateMachineConfig[targetState]) { + log.error(`目标状态 ${targetState} 不存在于配置中`); + return false; + } + + const maxSteps = 20; // 最大步骤数,防止无限循环 + let currentState = previousState; + let steps = 0; + + // 防护机制:记录每个状态的尝试次数 + const stateAttemptCount = new Map(); + const maxStateAttempts = 5; // 同一状态最多尝试5次 + + while (steps < maxSteps) { + // 判断当前状态 + const detectedState = await determineCurrentState(currentState); + + if (!detectedState) { + log.error('无法识别当前状态,停止状态切换'); + return false; + } + + // 检查是否已到达目标状态 + if (detectedState === targetState) { + log.info(`已到达目标状态: ${targetState}`); + return true; + } + + // 防护机制:检查当前状态的尝试次数 + const attemptCount = stateAttemptCount.get(detectedState) || 0; + if (attemptCount >= maxStateAttempts) { + log.error(`状态 ${detectedState} 已连续尝试 ${attemptCount} 次,超过最大限制,停止状态切换`); + log.error('可能陷入状态循环,请检查状态配置或界面状态'); + return false; + } + stateAttemptCount.set(detectedState, attemptCount + 1); + + // 查找从当前状态到目标状态的路径 + const path = findPath(detectedState, targetState); + if (!path || path.length === 0) { + log.error(`无法找到从 ${detectedState} 到 ${targetState} 的路径`); + return false; + } + + // 执行路径中的第一步 + const nextState = path[0]; + const transition = stateMachineConfig[detectedState].transitions.find( + t => t.targetState === nextState + ); + + if (!transition) { + log.error(`从 ${detectedState} 到 ${nextState} 的转移未定义`); + return false; + } + + // 只在第一次尝试或重试时显示转移信息 + if (attemptCount === 0) { + log.info(`${detectedState} -> ${nextState} (目标: ${targetState})`); + } else { + log.warn(`重试: ${detectedState} -> ${nextState} (第 ${attemptCount + 1} 次)`); + } + try { + // 使用 new Function 创建异步函数并执行 + const actionFunc = new Function('return (async () => { ' + transition.action + ' })()'); + await actionFunc(); + } catch (e) { + log.error(`执行转移操作失败: ${e.message}`); + return false; + } + + // 等待一小段时间让界面响应 + await sleep(500); + + currentState = detectedState; + steps++; + } + + log.error(`超过最大步骤数 ${maxSteps},停止状态切换`); + return false; +} + +/** + * 查找从起始状态到目标状态的路径 + * 使用BFS算法查找最短路径 + * + * @param {string} startState - 起始状态 + * @param {string} targetState - 目标状态 + * @returns {string[]|null} 路径数组(不包含起始状态,包含目标状态),如果无法到达则返回null + */ +function findPath(startState, targetState) { + if (!stateMachineConfig[startState] || !stateMachineConfig[targetState]) { + return null; + } + + if (startState === targetState) { + return []; + } + + // BFS查找最短路径 + const queue = [[startState]]; + const visited = new Set([startState]); + + while (queue.length > 0) { + const path = queue.shift(); + const currentState = path[path.length - 1]; + + const state = stateMachineConfig[currentState]; + if (!state || !state.transitions) continue; + + for (const transition of state.transitions) { + const nextState = transition.targetState; + + if (nextState === targetState) { + // 找到路径 + return [...path.slice(1), nextState]; + } + + if (!visited.has(nextState) && stateMachineConfig[nextState]) { + visited.add(nextState); + queue.push([...path, nextState]); + } + } + } + + return null; // 无法到达目标状态 +} + +/** + * 通用找图/找RO并可选点击(支持单图片文件路径、单RO、图片文件路径数组、RO数组) + * @param {string|string[]|RecognitionObject|RecognitionObject[]} target + * @param {boolean} [doClick=true] 是否点击 + * @param {number} [timeout=3000] 识别时间上限(ms) + * @param {number} [interval=50] 识别间隔(ms) + * @param {number} [retType=0] 0-返回布尔;1-返回 Region 结果 + * @param {number} [preClickDelay=50] 点击前等待 + * @param {number} [postClickDelay=50] 点击后等待 + * @returns {boolean|Region} 根据 retType 返回是否成功或最终 Region + */ +async function findAndClick(target, + doClick = true, + timeout = 3000, + interval = 50, + retType = 0, + preClickDelay = 50, + postClickDelay = 50) { + // 建立识别目标的对象,将 mat 和 ro 分别挂载到对象上 + let targetObjs = []; + try { + // 1. 统一处理目标,保存 mat 和 ro 的对应关系 + if (Array.isArray(target)) { + targetObjs = new Array(target.length); + for (let i = 0; i < target.length; i++) { + const t = target[i]; + targetObjs[i] = {}; + if (typeof t === 'string') { + targetObjs[i].mat = file.ReadImageMatSync(t); + targetObjs[i].ro = RecognitionObject.TemplateMatch(targetObjs[i].mat); + } else { + targetObjs[i].ro = t; + } + } + } else { + targetObjs = new Array(1); + targetObjs[0] = {}; + if (typeof target === 'string') { + targetObjs[0].mat = file.ReadImageMatSync(target); + targetObjs[0].ro = RecognitionObject.TemplateMatch(targetObjs[0].mat); + } else { + targetObjs[0].ro = target; + } + } + + const start = Date.now(); + let found = null; + + while (Date.now() - start <= timeout) { + const gameRegion = captureGameRegion(); + try { + // 依次尝试每一个 ro + for (let i = 0; i < targetObjs.length; i++) { + const res = gameRegion.find(targetObjs[i].ro); + if (!res.isEmpty()) { // 找到 + found = res; + if (doClick) { + await sleep(preClickDelay); + res.click(); + await sleep(postClickDelay); + } + break; // 成功即跳出 for + } + } + if (found) break; // 成功即跳出 while + } finally { + gameRegion.dispose(); + } + await sleep(interval); // 没找到时等待 + } + + // 3. 按需返回 + return retType === 0 ? !!found : (found || null); + + } catch (error) { + log.error(`执行通用识图时出现错误:${error.message}`); + return retType === 0 ? false : null; + } finally { + // 遍历对象释放 mat + for (let i = 0; i < targetObjs.length; i++) { + if (targetObjs[i] && targetObjs[i].mat) { + try { + targetObjs[i].mat.dispose(); + } catch (e) { + log.error(`释放 Mat 对象时出错:${e.message}`); + } + } + } + } +} + +// 将函数挂载到 globalThis,供 new Function 创建的作用域访问 +//globalThis.findAndClick = findAndClick; + +/** + * 数字模板匹配 + * + * @param {string} numberPngFilePath - 存放 0.png ~ 9.png 的文件夹路径(不含文件名) + * @param {number} x - 待识别区域的左上角 x 坐标,默认 0 + * @param {number} y - 待识别区域的左上角 y 坐标,默认 0 + * @param {number} w - 待识别区域的宽度,默认 1920 + * @param {number} h - 待识别区域的高度,默认 1080 + * @param {number} maxThreshold - 模板匹配起始阈值,默认 0.95(最高可信度) + * @param {number} minThreshold - 模板匹配最低阈值,默认 0.8(最低可信度) + * @param {number} splitCount - 在 maxThreshold 与 minThreshold 之间做几次等间隔阈值递减,默认 5 + * @param {number} maxOverlap - 非极大抑制时允许的最大重叠像素,默认 2;只要 x 或 y 方向重叠大于该值即视为重复框 + * + * @returns {number} 识别出的整数;若没有任何有效数字框则返回 -1 + * + * @example + * const mora = await numberTemplateMatch('摩拉数字', 860, 70, 200, 40); + * if (mora >= 0) console.log(`当前摩拉:${mora}`); + */ +async function numberTemplateMatch( + numberPngFilePath, + x = 0, y = 0, w = 1920, h = 1080, + maxThreshold = 0.95, + minThreshold = 0.8, + splitCount = 5, + maxOverlap = 2 +) { + let targetObjs = new Array(10); // 0-9 共10个数字模板 + for (let i = 0; i <= 9; i++) { + targetObjs[i] = {}; + targetObjs[i].mat = file.ReadImageMatSync(`${numberPngFilePath}/${i}.png`); + targetObjs[i].ro = RecognitionObject.TemplateMatch(targetObjs[i].mat, x, y, w, h); + } + + function setThreshold(objs, newThreshold) { + for (let i = 0; i < objs.length; i++) { + if (objs[i] && objs[i].ro) { + objs[i].ro.Threshold = newThreshold; + objs[i].ro.InitTemplate(); + } + } + } + + let gameRegion; + const allCandidates = []; + + try { + gameRegion = captureGameRegion(); + + /* 1. splitCount 次等间隔阈值递减 */ + for (let k = 0; k < splitCount; k++) { + const curThr = maxThreshold - (maxThreshold - minThreshold) * k / Math.max(splitCount - 1, 1); + setThreshold(targetObjs, curThr); + + /* 2. 9-0 每个模板跑一遍,所有框都收 */ + for (let digit = 9; digit >= 0; digit--) { + try { + const res = gameRegion.findMulti(targetObjs[digit].ro); + if (res.count === 0) continue; + + for (let i = 0; i < res.count; i++) { + const box = res[i]; + allCandidates.push({ + digit: digit, + x: box.x, + y: box.y, + w: box.width, + h: box.height, + thr: curThr + }); + } + } catch (e) { + log.error(`识别数字 ${digit} 时出错:${e.message}`); + } + } + } + } catch (error) { + log.error(`识别数字过程中出现错误:${error.message}`); + } finally { + if (gameRegion) gameRegion.dispose(); + // 释放数字模板的 mat 对象 + for (let i = 0; i < targetObjs.length; i++) { + if (targetObjs[i] && targetObjs[i].mat) { + try { + targetObjs[i].mat.dispose(); + } catch (e) { + log.error(`释放数字模板 Mat 对象时出错:${e.message}`); + } + } + } + } + + /* 3. 无结果提前返回 -1 */ + if (allCandidates.length === 0) { + return -1; + } + + /* 4. 非极大抑制(必须 x、y 两个方向重叠都 > maxOverlap 才视为重复) */ + const adopted = []; + for (const c of allCandidates) { + let overlap = false; + for (const a of adopted) { + const xOverlap = Math.max(0, Math.min(c.x + c.w, a.x + a.w) - Math.max(c.x, a.x)); + const yOverlap = Math.max(0, Math.min(c.y + c.h, a.y + a.h) - Math.max(c.y, a.y)); + if (xOverlap > maxOverlap && yOverlap > maxOverlap) { + overlap = true; + break; + } + } + if (!overlap) { + adopted.push(c); + //log.info(`在 [${c.x},${c.y},${c.w},${c.h}] 找到数字 ${c.digit},匹配阈值=${c.thr}`); + } + } + + /* 5. 按 x 排序,拼整数;仍无有效框时返回 -1 */ + if (adopted.length === 0) return -1; + adopted.sort((a, b) => a.x - b.x); + + return adopted.reduce((num, item) => num * 10 + item.digit, 0); +} + +/** + * 计算两个字符串的相似度 + * 使用Levenshtein距离算法 + * + * @param {string} str1 - 第一个字符串 + * @param {string} str2 - 第二个字符串 + * @returns {number} 相似度,范围0-1 + */ +function calculateSimilarity(str1, str2) { + const len1 = str1.length; + const len2 = str2.length; + + // 创建距离矩阵 + const matrix = Array(len1 + 1).fill().map(() => Array(len2 + 1).fill(0)); + + // 初始化第一行和第一列 + for (let i = 0; i <= len1; i++) { + matrix[i][0] = i; + } + for (let j = 0; j <= len2; j++) { + matrix[0][j] = j; + } + + // 计算距离 + for (let i = 1; i <= len1; i++) { + for (let j = 1; j <= len2; j++) { + const cost = str1[i - 1] === str2[j - 1] ? 0 : 1; + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, // 删除 + matrix[i][j - 1] + 1, // 插入 + matrix[i - 1][j - 1] + cost // 替换 + ); + } + } + + // 计算相似度 + const maxLen = Math.max(len1, len2); + const distance = matrix[len1][len2]; + const similarity = 1 - (distance / maxLen); + + return similarity; +} + +/** + * 检查UID是否匹配 + * 考虑到识别误差,当相似度大于等于8/9时认为匹配成功 + * + * @param {number} currentUid - 当前识别的UID + * @param {string} targetUid - 目标UID + * @returns {boolean} 是否匹配成功 + */ +function isUidMatch(currentUid, targetUid) { + if (currentUid < 0) { + return false; + } + + const currentUidStr = currentUid.toString(); + const targetUidStr = targetUid.toString(); + + // 计算相似度 + const similarity = calculateSimilarity(currentUidStr, targetUidStr); + + // 输出相似度信息 + log.info(`UID相似度:${(similarity * 100).toFixed(2)}% (${currentUidStr} vs ${targetUidStr})`); + + // 相似度大于等于8/9时认为匹配成功 + return similarity >= 8 / 9; +} + +/** + * 校验当前账号UID + * + * @returns {number} 当前账号UID,若识别失败返回-1 + */ +async function verifyCurrentUid() { + try { + // 尝试返回主界面 + await genshin.returnMainUi(); + + // 尝试使用状态机进入主界面 + await goToState('mainUI'); + + // 无论尝试是否成功,按一次G键 + keyPress('VK_G'); + await sleep(1000); + + // 识别UID,识别区域是1727 1050 160 30 + const uid = await numberTemplateMatch('assets/uid图片', 1727, 1050, 160, 30); + + if (uid >= 0) { + log.info(`成功识别当前账号UID:${uid}`); + } else { + log.warn('未能识别当前账号UID'); + } + await genshin.returnMainUi(); + return uid; + } catch (e) { + log.error(`校验UID时出错:${e.message}`); + return -1; + } +} + +/** + * 处理截图模式 + * 前往主界面识别UID,然后前往"进入游戏或登录其他账号"界面,截图保存对应UID图片 + */ +async function handleScreenshotMode() { + try { + log.info('进入截图模式'); + + // 1. 确定要使用的UID + let uidStr = settings.targetUid; + + if (!uidStr) { + log.info('未设置目标UID,尝试识别当前账号UID'); + const currentUid = await verifyCurrentUid(); + + if (currentUid < 0) { + log.error('未能识别当前账号UID,截图模式失败'); + notification.error('截图模式失败:未能识别当前账号UID'); + return; + } + + uidStr = currentUid.toString(); + log.info(`识别到当前账号UID:${uidStr}`); + } else { + log.info(`使用设置的目标UID:${uidStr}`); + } + + // 2. 前往"进入游戏或登录其他账号"界面 + log.info('开始:前往进入游戏或登录其他账号界面'); + + // 直接尝试进入enterGame状态 + const enterGameResult = await goToState('enterGame'); + + if (!enterGameResult) { + log.error('未能到达进入游戏或登录其他账号界面,截图模式失败'); + notification.error('截图模式失败:未能到达进入游戏或登录其他账号界面'); + return; + } + + log.info('成功到达进入游戏或登录其他账号界面'); + + // 3. 截图保存对应UID图片 + log.info('开始:截图保存账号图片'); + + // 截图区域:780 481 150 27 + const CAP_X = 780; + const CAP_Y = 481; + const CAP_W = 150; + const CAP_H = 27; + + // 保存路径 + const TARGET_DIR = 'accounts'; + const fullPath = TARGET_DIR + '/' + uidStr + '.png'; + + // 捕获游戏画面 + gameRegion = captureGameRegion(); + try { + const mat = gameRegion.DeriveCrop(CAP_X, CAP_Y, CAP_W, CAP_H).SrcMat; + + // 保存图片 + file.WriteImageSync(fullPath, mat); + mat.dispose(); + } finally { + gameRegion.dispose(); + gameRegion = null; + } + + log.info(`成功保存账号图片:${fullPath}`); + notification.Send(`截图模式成功:已保存账号图片 ${uidStr}.png`); + + // 4. 返回主界面 + log.info('开始:返回主界面'); + await goToState('mainUI'); + log.info('已返回主界面'); + + } catch (e) { + log.error(`截图模式出错:${e.message}`); + notification.error(`截图模式失败:${e.message}`); + } +} diff --git a/repo/js/AccountSwitchStateMachine/manifest.json b/repo/js/AccountSwitchStateMachine/manifest.json new file mode 100644 index 000000000..4db86314d --- /dev/null +++ b/repo/js/AccountSwitchStateMachine/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest_version": 1, + "name": "带状态机的账号切换", + "version": "1.0", + "description": "", + "authors": [ + { + "name": "mno" + } + ], + "settings_ui": "settings.json", + "main": "main.js" +} \ No newline at end of file diff --git a/repo/js/AccountSwitchStateMachine/settings.json b/repo/js/AccountSwitchStateMachine/settings.json new file mode 100644 index 000000000..6b88f356d --- /dev/null +++ b/repo/js/AccountSwitchStateMachine/settings.json @@ -0,0 +1,32 @@ +[ + { + "name": "targetUid", + "type": "input-text", + "label": "要切换到的账号UID" + }, + { + "name": "account", + "type": "input-text", + "label": "账号(非必要,查找不到账号图片时备用)" + }, + { + "name": "password", + "type": "input-text", + "label": "密码(非必要,查找不到账号图片时备用)" + }, + { + "name": "skipSearch", + "type": "checkbox", + "label": "勾选后不尝试查找账号图片,直接使用账号密码登录" + }, + { + "name": "verifyUid", + "type": "checkbox", + "label": "勾选后在切换前后检查当前账号UID,辅助判断切换是否成功" + }, + { + "name": "screenshotMode", + "type": "checkbox", + "label": "勾选后进入截图模式,自动截图保存对应UID的账号图片" + } +]