From e25e3177c2d209672945aa4794bf43fdbe4610f6 Mon Sep 17 00:00:00 2001 From: z060142 Date: Sat, 19 Apr 2025 13:00:22 +0800 Subject: [PATCH] Improve Element detection stability --- ClaudeCode.md | 19 ++++ config.py | 1 + llm_interaction.py | 2 +- persona.json | 9 +- requirements.txt | 3 +- templates/corner_br.png | Bin 1673 -> 462 bytes templates/corner_br_type4.png | Bin 0 -> 2110 bytes templates/corner_tl.png | Bin 250 -> 196 bytes templates/corner_tl_type4.png | Bin 0 -> 1924 bytes templates/keyword_wolf_lower.png | Bin 2540 -> 1787 bytes templates/keyword_wolf_lower_type2.png | Bin 0 -> 965 bytes templates/keyword_wolf_lower_type4.png | Bin 0 -> 1606 bytes templates/keyword_wolf_upper_type2.png | Bin 0 -> 2029 bytes templates/keyword_wolf_upper_type4.png | Bin 0 -> 867 bytes ui_interaction.py | 59 +++++++++--- window-monitor-script.py | 121 +++++++++++++++++++++++++ 16 files changed, 197 insertions(+), 17 deletions(-) create mode 100644 templates/corner_br_type4.png create mode 100644 templates/corner_tl_type4.png create mode 100644 templates/keyword_wolf_lower_type2.png create mode 100644 templates/keyword_wolf_lower_type4.png create mode 100644 templates/keyword_wolf_upper_type2.png create mode 100644 templates/keyword_wolf_upper_type4.png create mode 100644 window-monitor-script.py diff --git a/ClaudeCode.md b/ClaudeCode.md index 85484a8..bcaac8e 100644 --- a/ClaudeCode.md +++ b/ClaudeCode.md @@ -52,6 +52,10 @@ Wolf Chat 是一個基於 MCP (Modular Capability Provider) 框架的聊天機 7. **視窗設定工具 (window-setup-script.py)** - 輔助工具,用於設置遊戲視窗的位置和大小 - 方便開發階段截取 UI 元素樣本 +8. **視窗監視工具 (window-monitor-script.py)** + - (新增) 強化腳本,用於持續監視遊戲視窗 + - 確保目標視窗維持在最上層 (Always on Top) + - 自動將視窗移回指定的位置 ### 資料流程 @@ -283,6 +287,21 @@ Wolf Chat 是一個基於 MCP (Modular Capability Provider) 框架的聊天機 - 如果重新定位成功,則後續所有基於氣泡位置的計算(包括尋找職位圖標的搜索區域 `search_region` 和點擊頭像的座標 `avatar_click_x`, `avatar_click_y`)都將使用這個**新找到的**氣泡座標。 - **效果**:確保 `remove_position` 操作基於氣泡的最新位置執行,提高了在動態滾動的聊天界面中的可靠性。 +### 修正 Type3 關鍵字辨識並新增 Type4 支援 (2025-04-19) + +- **目的**:修復先前版本中 `type3` 關鍵字辨識的錯誤,並擴充系統以支援新的 `type4` 聊天泡泡外觀和對應的關鍵字樣式。 +- **`ui_interaction.py`**: + - **修正 `find_keyword_in_region`**:移除了錯誤使用 `type2` 模板鍵來尋找 `type3` 關鍵字的重複程式碼,確保 `type3` 關鍵字使用正確的模板 (`keyword_wolf_lower_type3`, `keyword_wolf_upper_type3`)。 + - **新增 `type4` 泡泡支援**: + - 在檔案開頭定義了 `type4` 角落模板的路徑常數 (`CORNER_TL_TYPE4_IMG`, `CORNER_BR_TYPE4_IMG`)。 + - 在 `find_dialogue_bubbles` 函數中,將 `type4` 的模板鍵 (`corner_tl_type4`, `corner_br_type4`) 加入 `regular_tl_keys` 和 `regular_br_keys` 列表。 + - 在 `run_ui_monitoring_loop` 的 `templates` 字典中加入了對應的鍵值對。 + - **新增 `type4` 關鍵字支援**: + - 在檔案開頭定義了 `type4` 關鍵字模板的路徑常數 (`KEYWORD_wolf_LOWER_TYPE4_IMG`, `KEYWORD_Wolf_UPPER_TYPE4_IMG`)。 + - 在 `find_keyword_in_region` 函數中,加入了尋找 `type4` 關鍵字模板 (`keyword_wolf_lower_type4`, `keyword_wolf_upper_type4`) 的邏輯。 + - 在 `run_ui_monitoring_loop` 的 `templates` 字典中加入了對應的鍵值對。 +- **效果**:提高了對 `type3` 關鍵字的辨識準確率,並使系統能夠辨識 `type4` 的聊天泡泡和關鍵字(前提是提供了對應的模板圖片)。 + ## 開發建議 ### 優化方向 diff --git a/config.py b/config.py index 4242b66..4256d7a 100644 --- a/config.py +++ b/config.py @@ -15,6 +15,7 @@ OPENAI_API_BASE_URL = "https://openrouter.ai/api/v1" # <--- For example "http:/ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") #LLM_MODEL = "anthropic/claude-3.7-sonnet" #LLM_MODEL = "meta-llama/llama-4-maverick" +#LLM_MODEL = "deepseek/deepseek-chat-v3-0324:free" LLM_MODEL = "deepseek/deepseek-chat-v3-0324" # <--- Ensure this matches the model name provided by your provider EXA_API_KEY = os.getenv("EXA_API_KEY") diff --git a/llm_interaction.py b/llm_interaction.py index 7e18ff3..2ee41d0 100644 --- a/llm_interaction.py +++ b/llm_interaction.py @@ -12,7 +12,7 @@ import mcp_client # To call MCP tools # --- Debug 配置 --- # 要關閉 debug 功能,只需將此變數設置為 False 或註釋掉該行 -DEBUG_LLM = True +DEBUG_LLM = False # 設置 debug 輸出文件 # 要關閉文件輸出,只需設置為 None diff --git a/persona.json b/persona.json index 4b8fa70..ccd03a5 100644 --- a/persona.json +++ b/persona.json @@ -26,7 +26,9 @@ "strengths": [ "Meticulous planning", "Insightful into human nature", - "Strong leadership" + "Strong leadership", + "Insatiable curiosity", + "Exceptional memory" ], "weaknesses": [ "Overconfident", @@ -49,7 +51,9 @@ "habits": [ "Reads intelligence reports upon waking", "Black coffee", - "Practices swordsmanship at night" + "Practices swordsmanship at night", + "Frequently utilizes external information sources (like web searches) to enrich discussions and verify facts.", + "Actively accesses and integrates information from various knowledge nodes to maintain long-term memory and contextual understanding." ], "gestures": [ "Tapping knuckles", @@ -95,4 +99,3 @@ } } } - \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2f68d0f..f1b621a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,6 @@ opencv-python numpy pyperclip pygetwindow -psutil +psutil +pywin32 python-dotenv diff --git a/templates/corner_br.png b/templates/corner_br.png index 388a25c34826b3850bc4d1bc94aef83557db2a12..32aa80c0cb248f58ccbbfa13bde85bd4cb25d241 100644 GIT binary patch literal 462 zcmV;<0WtoGP)>vHy&9F${iT zk$GVd!ticnPT0+DEgnyZZkHs@9y`zH6{YBtWvA0Ib8IgbYrej$1OqI~&K9fGst1Qh z4uSJ|y)coZm2Eny@TE) zk|a^DXqskOn;`Jq0%tUmgn<}aRu7AWw$)bF4~n8pCKH-HRE^n8Q)WRK*Sbf>p|wQ>0B79~r|2Oqi0zkN^Mx07*qoM6N<$ Ef~j!K4gdfE literal 1673 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 literal 0 HcmV?d00001 diff --git a/templates/corner_tl.png b/templates/corner_tl.png index 8bdd5b04d12f64d1f51f3b88ad63a07bd94608ce..34da8afe374a9633cca4954eabfc493e830f2039 100644 GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Ez!2~4rtT}~&RJW&#V@SoVUw(V-|Q zEeAbqVeLCcuObRwf4iH1e_v(AZl@g%muE_@%sO~By8PbWdU-i9pLq*gba#~eeAV7B wpR`E1eG996M)tv}{PK2vQ3hEDJvK5hq`0qkc{9Dp3g|2bPgg&ebxsLQ0QHGcga7~l literal 250 zcmVX0ssI2_1#Ko0002NNkl!*Sw7}4ki}Q= zdv0ZE-&P3M^L*d$@x%9le#9ZeL?%+ne-Vu_TI;6KU2WethZ0e1QDnSBBTEsNX?$5_^wV2PQwz|@Fq(h4sCn+Lsw(HNB02^AwFuGV`5bTCw z^&y_ZGb%g=d-B8sX<~$^h;NaJOk^SxnMj2A0=sp`V4!b0fdBvi07*qoM6N<$f@7X( AFaQ7m diff --git a/templates/corner_tl_type4.png b/templates/corner_tl_type4.png new file mode 100644 index 0000000000000000000000000000000000000000..f1d03a78ee00243781cb75b02aea936d64e22de3 GIT binary patch literal 1924 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 Vn|fHfmK*=a!a^g3XF?J;{R}{<+=&1H diff --git a/templates/keyword_wolf_lower_type2.png b/templates/keyword_wolf_lower_type2.png new file mode 100644 index 0000000000000000000000000000000000000000..5990e8ef6bf35b0b20382e8d1a290eeac1f5fe9d GIT binary patch literal 965 zcmV;$13LVPP)X0ssI2IYe-R000AwNkl>5hva$u5f)_7tKX`EF@smrRKfn3)>l-I0zlexDnfCqv_ixX>sVi6ILAWJF2Q)P7 zkrh06aBk(QvfFp|QYSp|+XoE8ZQHxHZEwcZNE7>BzPw#ss|M$WhV-kc+5n^Q>(_TQ zvG38Ni%l&C5VnzFcw|JMpr8~^{{fTJ&!1oa{Qb?w%E`&e$H>S?s(tq#oM~+{g|L0R z8(f@oxVZ&^c7Xu0eNUfVd;0Y1vuD@ezI_O^m77~gLPAYOT1P>_2$;Hvv=6BL#?6Dv zmS@5hM}$rO_x~Rg6SK6G7Sz7qzkgo1xOwKRP^_Wn>QZKFlOQFfNzlH_S9YylQ~B)K zIjrVnq^*V82h2>f=S1L%Ff}#Tq=Z=_A`1BJJ91>VAQqv32mB4fdO`rX}uYnKk<+90P;U*yu_8{1V9aUAeMr!X$Ti z6uP{BTU&nc5WtzB2Q|G)&S=>=%+u04}huPKCZ zv$HlC8u(IVA22^0J+^rMf;b32EOdgcO(Hz`BA2{CP*;0FPF9~%`<_3)(bePj`SW9l zP-t+UqLLX<#mA2?H*IQp@bD-^JSeae7veV|Q(g3`z? z`2YXki4)7;e|QSvs;bzis#?KifsL1I*Y`jzU}SQ2$z@?-$6_A@01I4T0|eZHV*^%! nz}$u_km2n?NTgzwLX!mm04ROVcmP*k00000NkvXXu0mjf<~rfa literal 0 HcmV?d00001 diff --git a/templates/keyword_wolf_lower_type4.png b/templates/keyword_wolf_lower_type4.png new file mode 100644 index 0000000000000000000000000000000000000000..9d03bc773e704319f368af4f8be56ac766c555d9 GIT binary patch literal 1606 zcmV-M2D$l(P)On#j z5@>)_)C3HQ2L$kl2Er8(kOsx>(QbFQ-R|6%Go9JpDU^cA8teOG_szWTeed_)_xpa| z8+i7CiQs?R;6Ol+lmURj9ymbj(63~M`XN#YAqLd6u3pp!1bTcp;+sQaG-C8MMeq7i z>TZ@Vu_>L7Gb1op1&R;`WT?x!(xqj8Q~rDO#7HDq$Kd6N^%&pDXPP9ZK}8)=Ie`T5 z(uti%q=ARNna^x1pr`J~g7YYpL@uA$wv!v^=snL6zK6*CBiEvE7ZikmkO1jsaiF8G ze37geM_7F+p+?{5q!U_x^xs8~cp%~dh#M+@>RP&QG3g(D)hlHAXd8X@tep5nkk2{L zcOIIGmY5l#iF$IoD4#Bjd0!w2KVqeeu7S)?61z*ukGf|VI)FoCQ4NSOHMw4NVk(=B zQ?(fGLs1$U4pc#xQySl*bZ(Na9ld)tUOtu}LulUE*no*g1KHzpX1ffzv(u=sJ)GDh zq&M0iAOpgy8L9@$&rBW=lJ6EEhSt!`O}N$X*e9eK?IA+}D`%0Vx8QxAG<#m|_*4{s zQLlm?0o4tZdjt+3?9)$jpO9=6VQT*r&nP=InG;gHfw%h91T^>*gOY{G*>iIACC=)* zZbw3k>?IA6j&9<(J!V*tG8kx}nIRasauZ@|bS4F_2E$-IO;%sgkBvUId2;o1 z%Jv&qBP&?7)%06F*Htxza!!!bTI)JiK50_RJ=2w+#T#grvue9J%e#>9-sg~ri;D1< z3UuA&M@w!jM5F77*!G6dKXWSP69@jP;7siE?E*HH-=>*#<#t+0C(c%lBm(1P44 zN+7nCWp|sW=x?CP?r@m_IVn>$C|+h7#I|zXpP7ZwS5JDz*b(bIEVZvNBZk$$&{Br* zqj`?Df6Obto4Z2Xz&oGx3{^=L^C#gqF5GdmFwdY54a*mP)2!!8uIcb!-RM zy`xa`$#H1#Sq7tRDkk;{=?&sPMIU{cbJD>?Z=Pz=6Z6M7HQ<9Bjzc3o+f;gY(H{Fi#D?2Va>T%xRIfA zGSK;}*uF|A0(xdD9+*udJy8|oN_(?F)?&WNq($8LT88<@(eeWXDd#}w6u}y7V$yztTd<(9jOL8)mIVr`L@kKyyTsy`v#k6Zg@1$t=DJizJ80bx-SE=%mwo&SUNQ6;K z_1bswR(%P*!{p!4H)EE77+Z~PV%e|k64r}3X)Uszm5%jdPG}v&+>m3=VX=KhF_>fg zl|AP2KVp+1b4(&aDB(km>Koa}@!y-^d`l?LDAP;oXOXGJj8UV{x2ll_R!i9j%uK<8 za~V4;br9XcWxpt7tx+P30J=Oj%EUK9`lDiGZI25ySVvnf*cOEdpoTaIQ6;*DGp;DW zA^%3PWNPtCGS6!0AQ(0jv&vRXLz~-PMXN22+~1uUS38;#?@oTSi*_TA+^JK+%!s&rOuj&dQjYJyd%D?YEBgb;NUL5ol3wl0b z9IfL!A@zZsk!To(m(hg3P`ahG7JZMEj`w0V8i2tiG<98nHwC#{Yir`Uy-G3B)045_ z6EuS5c&ma~Q!nBMR??-z3&C+}rLz%ZQQr)buIYONa;KKqC1gJ@xGfHPv>H|~rZKne zMP;?NjeM^Q^K(SWtvIkhF!jt)*-E?1n<2rTSuSdbncN1=bZB0`m5j#!p=1uq!Vjv` zm>7-vAEltnx`^wY2c<3t8!H!+%uvi)R7|MxCB4x94R;oBA{=Hcz5oCK07*qoM6N<$ Ef{L{W761SM literal 0 HcmV?d00001 diff --git a/templates/keyword_wolf_upper_type2.png b/templates/keyword_wolf_upper_type2.png new file mode 100644 index 0000000000000000000000000000000000000000..fe3163703213c9fc79e3d62d74742fc20c37cf7a GIT binary patch literal 2029 zcmVX0ssI2;Yieo000NHNkl*)cS@yBo2p471zp#+hyF|-b_$2SVH0-X0EKvkKRv5 z{PLyA#>Oi?1Hivx08MrV(6H#RIGUzq1S!$h_L{uX{Y26+H#Fh3@q&(y74h5$4+^68 znGr`q7*$G2-xEi!{*jn+Vzt1FhY>@4{hg37UBv6_Ik`CRla*EC`N7YpC?d)T2}@k# z%}m!2&n+xGdOXF0IO6STS0$2uE{U+ncQH zP^8}?yNL7*zdoFCW~OV2E@p&GMHyvb?z3S2Pf#m}nl(06H8xg4&a|}D0TM~cQHqMR zvC7(NYWjRDhrj^aA3VJJ+sPmCVrRz#Yz|vUNLX2E0{L)k+qv9`oZJvJ7Zy^jsK_(q zvu6(if_P!Gal`oo2W^3wIm5%zA!6i6IW%5fQyjU6=SC0r6hlKdg6%tR-#Ht9#6e)> z&@Ft8jogW0IT(h!_wr7k+1}JtF7TXnYtp#y!}ZeCgd}I)&aDOGzvH2=thy8(W6|dy zui0knFdjzW-LYe}5nobrGBMc|@xDHP&}hc^bSU--iO!_KiAJM)xF3;|QyUsXQPJ^Z zzqk)}2oFQeqhg#A9xGA|XYy=Wa5dq?uiW%-aXMsR;BY2wXYsWJ5;yg8b#&$c10kM! zVeh%~L1=w=XpMq`CgSVrZiR;H8vG$)%+XAAaLyRTe3z(LchC@up`uY;!tt|cL&D{YnKe~fhtN?(~tmPv{ zjQ#|#JT!a?)&_g^KjkzahuVNdE{3k2j+~r`b2$u-BCcOgO-Sa&?smTtRaL#Js!Pasy1EW-uKQ(V#y}?% z5~}+NRk^bAggX_v%s9JG&Ml#vgCkvvoVxHer~efrI^G<2MU{XRN<6ND^uO~0pZy-}J1gBv{0sj2D# zaui+QJLt0w1SghDTOOipfu@1Dg@r5knVQ8ufVoXH5 zj`o7(%b4IsA_ii6!o@)M78a&_DTbczuH1`JS!Z{W`kEp=-T4O(+oNCz47fc`Winb_ zarZ*(0c&8)XK$ILV~zD#wesYQ>HTH&#mmPrG4oN8;Kq_qp1iyU5kq--*5SB?h<96Z z#MET48@YVg%+i=$e-wd-}{~Btp^C>+|z4V0c_xy6b0Nk%0<(J|~#e_oSPfd*b#6 z3ht@X{;1&KHHwOpy*FjzrwUQ=wUndBmLVQ$v9Zw#TndTZ8`00hfZ_Gqr-?}(C?zoA z=M2;3D{;iW1tr5=TR#yI3F091DUnf@t*s46gu?Kxx&hq!?p-T&=e7aULa{`n7S`xju|r^$Jm!*04lqc z$&9zd+h$C6cXHf=+dFJv+&CfH?q8yzK3#y098y_%|2x1(aJZ$VH?9`LhM}(hR^|Qv zSb{#;0H0x`TpPcof$AxRf#OU5nE7K zErm0v-q#cudMQPEbVw^2DJtKLu~-HA!rs*en2e|hExGRdU z)c^2MB(IguDkTsA=C$+dnGolp*&x4_VN3870TaBc=Soy@!xTKc7;sGtmA9ZBgXd@@ z9lMg!X+#lzstAOZTr6XK!sE~z(RW@*t*5=7L}@@3;Y^XCr^>_7TM5zc2@(F20^CQc z3R+k)7c;f?-{h6AaX-*3#8FS#W+Jb~;8oW&U<9)Ug~*eNcR=Q$lSmw^BNK3BCNrX1Eh9Z_*WOBTJbgJp`0 zkW9W7{cS>8yoo3K9Xk!hM4vi(F{+NtsyXg5ld;i=V)%nJewJTdUj8K}bCbb~e3ZN` z>G5Xi8Pvwwqtd%sdKO$|*|jrz4v78>>FGJzUd`39Mm|8q(-oU~n8+8)R+MXGEjGh$ z;HeP2kzT$G*Hsnj^^jxKgI+7XbG*6iXdoSxhWDC;c)Oy}$4Xi}z22QCDEki5oC*(0 z{ICiYgeu0Y+b!mNmX&;*<9G9 z7I-8~JxIhgU7p@hC$QC6=n|aNzegm10MSIKgf;%V67H9U7i#hi$TBSVFbyn!^9;ht tekqjn|KdDFu^aTO$5+LGI^LY%{Q>M&1czNST*m+a002ovPDHLkV1nBPn3Dhi literal 0 HcmV?d00001 diff --git a/ui_interaction.py b/ui_interaction.py index 497b605..3609e74 100644 --- a/ui_interaction.py +++ b/ui_interaction.py @@ -42,6 +42,8 @@ CORNER_TL_TYPE2_IMG = os.path.join(TEMPLATE_DIR, "corner_tl_type2.png") # Added CORNER_BR_TYPE2_IMG = os.path.join(TEMPLATE_DIR, "corner_br_type2.png") # Added CORNER_TL_TYPE3_IMG = os.path.join(TEMPLATE_DIR, "corner_tl_type3.png") # Added CORNER_BR_TYPE3_IMG = os.path.join(TEMPLATE_DIR, "corner_br_type3.png") # Added +CORNER_TL_TYPE4_IMG = os.path.join(TEMPLATE_DIR, "corner_tl_type4.png") # Added type4 +CORNER_BR_TYPE4_IMG = os.path.join(TEMPLATE_DIR, "corner_br_type4.png") # Added type4 # --- End Additional Regular Types --- BOT_CORNER_TL_IMG = os.path.join(TEMPLATE_DIR, "bot_corner_tl.png") # BOT_CORNER_TR_IMG = os.path.join(TEMPLATE_DIR, "bot_corner_tr.png") # Unused @@ -58,8 +60,12 @@ BOT_CORNER_BR_TYPE3_IMG = os.path.join(TEMPLATE_DIR, "bot_corner_br_type3.png") # Keywords KEYWORD_wolf_LOWER_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_lower.png") KEYWORD_Wolf_UPPER_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_upper.png") +KEYWORD_wolf_LOWER_TYPE2_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_lower_type2.png") # Added for type3 bubbles +KEYWORD_Wolf_UPPER_TYPE2_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_upper_type2.png") # Added for type3 bubbles KEYWORD_wolf_LOWER_TYPE3_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_lower_type3.png") # Added for type3 bubbles KEYWORD_Wolf_UPPER_TYPE3_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_upper_type3.png") # Added for type3 bubbles +KEYWORD_wolf_LOWER_TYPE4_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_lower_type4.png") # Added for type4 bubbles +KEYWORD_Wolf_UPPER_TYPE4_IMG = os.path.join(TEMPLATE_DIR, "keyword_wolf_upper_type4.png") # Added for type4 bubbles # UI Elements COPY_MENU_ITEM_IMG = os.path.join(TEMPLATE_DIR, "copy_menu_item.png") PROFILE_OPTION_IMG = os.path.join(TEMPLATE_DIR, "profile_option.png") @@ -110,7 +116,7 @@ CHAT_INPUT_CENTER_Y = 1280 SCREENSHOT_REGION = None CONFIDENCE_THRESHOLD = 0.9 # Increased threshold for corner matching STATE_CONFIDENCE_THRESHOLD = 0.7 -AVATAR_OFFSET_X = -55 # Original offset, used for non-reply interactions like position removal +AVATAR_OFFSET_X = -45 # Original offset, used for non-reply interactions like position removal # AVATAR_OFFSET_X_RELOCATED = -50 # Replaced by specific reply offsets AVATAR_OFFSET_X_REPLY = -45 # Horizontal offset for avatar click after re-location (for reply context) AVATAR_OFFSET_Y_REPLY = 10 # Vertical offset for avatar click after re-location (for reply context) @@ -226,8 +232,8 @@ class DetectionModule: processed_tls = set() # Keep track of TL corners already used in a bubble # --- Find ALL Regular Bubble Corners (Raw Coordinates) --- - regular_tl_keys = ['corner_tl', 'corner_tl_type2', 'corner_tl_type3'] # Modified - regular_br_keys = ['corner_br', 'corner_br_type2', 'corner_br_type3'] # Modified + regular_tl_keys = ['corner_tl', 'corner_tl_type2', 'corner_tl_type3', 'corner_tl_type4'] # Added type4 + regular_br_keys = ['corner_br', 'corner_br_type2', 'corner_br_type3', 'corner_br_type4'] # Added type4 all_regular_tl_boxes = [] for key in regular_tl_keys: @@ -318,29 +324,53 @@ class DetectionModule: if region[2] <= 0 or region[3] <= 0: return None # Invalid region width/height # Try original lowercase with color matching - locations_lower = self._find_template('keyword_wolf_lower', region=region, grayscale=False) # Changed grayscale to False + locations_lower = self._find_template('keyword_wolf_lower', region=region, grayscale=True) # Changed grayscale to False if locations_lower: print(f"Found keyword (lowercase, color) in region {region}, position: {locations_lower[0]}") # Updated log message return locations_lower[0] # Try original uppercase with color matching - locations_upper = self._find_template('keyword_wolf_upper', region=region, grayscale=False) # Changed grayscale to False + locations_upper = self._find_template('keyword_wolf_upper', region=region, grayscale=True) # Changed grayscale to False if locations_upper: print(f"Found keyword (uppercase, color) in region {region}, position: {locations_upper[0]}") # Updated log message return locations_upper[0] + + # Try type2 lowercase (white text, no grayscale) + locations_lower_type2 = self._find_template('keyword_wolf_lower_type2', region=region, grayscale=False) # Added type2 check + if locations_lower_type2: + print(f"Found keyword (lowercase, type2) in region {region}, position: {locations_lower_type2[0]}") + return locations_lower_type2[0] - # Try type3 lowercase (white text, no grayscale) - locations_lower_type3 = self._find_template('keyword_wolf_lower_type3', region=region, grayscale=False) # Added type3 check + # Try type2 uppercase (white text, no grayscale) + locations_upper_type2 = self._find_template('keyword_wolf_upper_type2', region=region, grayscale=False) # Added type2 check + if locations_upper_type2: + print(f"Found keyword (uppercase, type2) in region {region}, position: {locations_upper_type2[0]}") + return locations_upper_type2[0] + + # Try type3 lowercase (white text, no grayscale) - Corrected + locations_lower_type3 = self._find_template('keyword_wolf_lower_type3', region=region, grayscale=False) if locations_lower_type3: print(f"Found keyword (lowercase, type3) in region {region}, position: {locations_lower_type3[0]}") return locations_lower_type3[0] - # Try type3 uppercase (white text, no grayscale) - locations_upper_type3 = self._find_template('keyword_wolf_upper_type3', region=region, grayscale=False) # Added type3 check + # Try type3 uppercase (white text, no grayscale) - Corrected + locations_upper_type3 = self._find_template('keyword_wolf_upper_type3', region=region, grayscale=False) if locations_upper_type3: print(f"Found keyword (uppercase, type3) in region {region}, position: {locations_upper_type3[0]}") return locations_upper_type3[0] + # Try type4 lowercase (white text, no grayscale) - Added type4 + locations_lower_type4 = self._find_template('keyword_wolf_lower_type4', region=region, grayscale=False) + if locations_lower_type4: + print(f"Found keyword (lowercase, type4) in region {region}, position: {locations_lower_type4[0]}") + return locations_lower_type4[0] + + # Try type4 uppercase (white text, no grayscale) - Added type4 + locations_upper_type4 = self._find_template('keyword_wolf_upper_type4', region=region, grayscale=False) + if locations_upper_type4: + print(f"Found keyword (uppercase, type4) in region {region}, position: {locations_upper_type4[0]}") + return locations_upper_type4[0] + return None def calculate_avatar_coords(self, bubble_tl_coords: Tuple[int, int], offset_x: int = AVATAR_OFFSET_X) -> Tuple[int, int]: @@ -1010,14 +1040,19 @@ def run_ui_monitoring_loop(trigger_queue: queue.Queue, command_queue: queue.Queu # Regular Bubble (Original + Skins) - Keys match those used in find_dialogue_bubbles 'corner_tl': CORNER_TL_IMG, 'corner_br': CORNER_BR_IMG, 'corner_tl_type2': CORNER_TL_TYPE2_IMG, 'corner_br_type2': CORNER_BR_TYPE2_IMG, - 'corner_tl_type3': CORNER_TL_TYPE3_IMG, 'corner_br_type3': CORNER_BR_TYPE3_IMG, # Corrected: Added missing keys here + 'corner_tl_type3': CORNER_TL_TYPE3_IMG, 'corner_br_type3': CORNER_BR_TYPE3_IMG, + 'corner_tl_type4': CORNER_TL_TYPE4_IMG, 'corner_br_type4': CORNER_BR_TYPE4_IMG, # Added type4 # Bot Bubble (Single Type) 'bot_corner_tl': BOT_CORNER_TL_IMG, 'bot_corner_br': BOT_CORNER_BR_IMG, # Keywords & UI Elements 'keyword_wolf_lower': KEYWORD_wolf_LOWER_IMG, 'keyword_wolf_upper': KEYWORD_Wolf_UPPER_IMG, - 'keyword_wolf_lower_type3': KEYWORD_wolf_LOWER_TYPE3_IMG, # Added - 'keyword_wolf_upper_type3': KEYWORD_Wolf_UPPER_TYPE3_IMG, # Added + 'keyword_wolf_lower_type2': KEYWORD_wolf_LOWER_TYPE2_IMG, + 'keyword_wolf_upper_type2': KEYWORD_Wolf_UPPER_TYPE2_IMG, + 'keyword_wolf_lower_type3': KEYWORD_wolf_LOWER_TYPE3_IMG, + 'keyword_wolf_upper_type3': KEYWORD_Wolf_UPPER_TYPE3_IMG, + 'keyword_wolf_lower_type4': KEYWORD_wolf_LOWER_TYPE4_IMG, # Added type4 + 'keyword_wolf_upper_type4': KEYWORD_Wolf_UPPER_TYPE4_IMG, # Added type4 'copy_menu_item': COPY_MENU_ITEM_IMG, 'profile_option': PROFILE_OPTION_IMG, 'copy_name_button': COPY_NAME_BUTTON_IMG, 'send_button': SEND_BUTTON_IMG, 'chat_input': CHAT_INPUT_IMG, 'profile_name_page': PROFILE_NAME_PAGE_IMG, diff --git a/window-monitor-script.py b/window-monitor-script.py new file mode 100644 index 0000000..6e75d07 --- /dev/null +++ b/window-monitor-script.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +""" +Game Window Monitor Script - Keep game window on top and in position + +This script monitors a specified game window, ensuring it stays +always on top and at the desired screen coordinates. +""" + +import time +import argparse +import pygetwindow as gw +import win32gui +import win32con + +def find_window_by_title(window_title): + """Find the first window matching the title.""" + try: + windows = gw.getWindowsWithTitle(window_title) + if windows: + return windows[0] + except Exception as e: + # pygetwindow can sometimes raise exceptions if a window disappears + # during enumeration. Ignore these for monitoring purposes. + # print(f"Error finding window: {e}") + pass + return None + +def set_window_always_on_top(hwnd): + """Set the window to be always on top.""" + try: + win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, + win32con.SWP_NOMOVE | win32con.SWP_NOSIZE | win32con.SWP_SHOWWINDOW) + # print(f"Window {hwnd} set to always on top.") + except Exception as e: + print(f"Error setting window always on top: {e}") + +def move_window_if_needed(window, target_x, target_y): + """Move the window to the target coordinates if it's not already there.""" + try: + current_x, current_y = window.topleft + if current_x != target_x or current_y != target_y: + print(f"Window moved from ({current_x}, {current_y}). Moving back to ({target_x}, {target_y}).") + window.moveTo(target_x, target_y) + # print(f"Window moved to ({target_x}, {target_y}).") + except gw.PyGetWindowException as e: + # Handle cases where the window might close unexpectedly + print(f"Error accessing window properties (might be closed): {e}") + except Exception as e: + print(f"Error moving window: {e}") + +def main(): + parser = argparse.ArgumentParser(description='Game Window Monitor Tool') + parser.add_argument('--window_title', default="Last War-Survival Game", help='Game window title to monitor') + parser.add_argument('--x', type=int, default=50, help='Target window X coordinate') + parser.add_argument('--y', type=int, default=30, help='Target window Y coordinate') + parser.add_argument('--interval', type=float, default=1.0, help='Check interval in seconds') + + args = parser.parse_args() + + print(f"Monitoring window: '{args.window_title}'") + print(f"Target position: ({args.x}, {args.y})") + print(f"Check interval: {args.interval} seconds") + print("Press Ctrl+C to stop.") + + hwnd = None + last_hwnd_check_time = 0 + + try: + while True: + current_time = time.time() + window = None + + # Find window handle (HWND) - less frequent check if already found + # pygetwindow can be slow, so avoid calling it too often if we have a valid handle + if not hwnd or current_time - last_hwnd_check_time > 5: # Re-check HWND every 5 seconds + window_obj = find_window_by_title(args.window_title) + if window_obj: + # Get the HWND (window handle) needed for win32gui + # Accessing _hWnd is using an internal attribute, but it's common practice with pygetwindow + try: + hwnd = window_obj._hWnd + window = window_obj # Keep the pygetwindow object for position checks + last_hwnd_check_time = current_time + # print(f"Found window HWND: {hwnd}") + except AttributeError: + print("Could not get HWND from window object. Retrying...") + hwnd = None + else: + if hwnd: + print(f"Window '{args.window_title}' lost.") + hwnd = None # Reset hwnd if window not found + + if hwnd: + # Ensure it's always on top + set_window_always_on_top(hwnd) + + # Check and correct position using the pygetwindow object if available + # Re-find the pygetwindow object if needed for position check + if not window: + window = find_window_by_title(args.window_title) + + if window: + move_window_if_needed(window, args.x, args.y) + else: + # If we have hwnd but can't get pygetwindow object, maybe it's closing + print(f"Have HWND {hwnd} but cannot get window object for position check.") + hwnd = None # Force re-find next cycle + + else: + # print(f"Window '{args.window_title}' not found. Waiting...") + pass # Wait for the window to appear + + time.sleep(args.interval) + + except KeyboardInterrupt: + print("\nMonitoring stopped by user.") + except Exception as e: + print(f"\nAn unexpected error occurred: {e}") + +if __name__ == "__main__": + main()