From 5ee58f9962991ae758eff68c7dc9ff7f0c61bb25 Mon Sep 17 00:00:00 2001 From: Typpi <20943337+Nick2bad4u@users.noreply.github.com> Date: Fri, 12 Apr 2024 18:25:23 -0400 Subject: [PATCH] Massive-Reformat-Pep8 All code has been reformatted to the PEP 8 style guide. Read more about the style guide here: https://peps.python.org/pep-0008/ --- .gitignore | 53 +++ __pycache__/abstract_mode.cpython-311.pyc | Bin 1834 -> 1838 bytes __pycache__/bot.cpython-311.pyc | Bin 35665 -> 35669 bytes __pycache__/characters.cpython-311.pyc | Bin 9106 -> 9069 bytes __pycache__/client_config.cpython-311.pyc | Bin 664 -> 663 bytes __pycache__/config.cpython-311.pyc | Bin 20338 -> 20180 bytes __pycache__/direct_input.cpython-311.pyc | Bin 14687 -> 14682 bytes __pycache__/font_loader.cpython-311.pyc | Bin 1370 -> 1373 bytes __pycache__/levels.cpython-311.pyc | Bin 11630 -> 11823 bytes __pycache__/menu.cpython-311.pyc | Bin 9661 -> 9692 bytes __pycache__/utils.cpython-311.pyc | Bin 28625 -> 28716 bytes __pycache__/windows.cpython-311.pyc | Bin 14157 -> 14230 bytes abstract_mode.py | 4 +- bot.py | 375 ++++++++++-------- characters.py | 107 ++++- client_config.py | 8 +- config.py | 289 ++++++++++---- direct_input.py | 117 ++++-- font_loader.py | 16 +- gui.pyw | 208 ++++++---- languages/__pycache__/english.cpython-311.pyc | Bin 11066 -> 11062 bytes languages/__pycache__/russian.cpython-311.pyc | Bin 16564 -> 16564 bytes languages/english.py | 366 ++++++++--------- languages/russian.py | 365 ++++++++--------- levels.py | 169 +++++++- menu.py | 104 ++--- .../__pycache__/fastlvl.cpython-311.pyc | Bin 1818 -> 1811 bytes .../default/__pycache__/gold.cpython-311.pyc | Bin 1741 -> 1734 bytes .../__pycache__/powerlvl.cpython-311.pyc | Bin 1784 -> 1776 bytes .../__pycache__/totallvl.cpython-311.pyc | Bin 1734 -> 1733 bytes modes/default/fastlvl.py | 10 +- modes/default/gold.py | 10 +- modes/default/powerlvl.py | 13 +- modes/default/totallvl.py | 6 +- utils.py | 233 ++++++----- windows.py | 64 ++- 36 files changed, 1601 insertions(+), 916 deletions(-) diff --git a/.gitignore b/.gitignore index c58ac1b..447a756 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,56 @@ modes/default/__pycache__/fastlvl.cpython-311.pyc modes/default/__pycache__/gold.cpython-311.pyc modes/default/__pycache__/powerlvl.cpython-311.pyc modes/default/__pycache__/totallvl.cpython-311.pyc +.vs/BHBot/FileContentIndex/830ecabf-68dd-4f67-862b-84b0f1b16a92.vsidx +.vs/BHBot/FileContentIndex/70be95f4-aad0-4ddc-bbb7-b8d01b6f200f.vsidx +.vs/BHBot/FileContentIndex/38c310c6-7bd3-49d1-a0e5-eb50be03ed51.vsidx +.vs/BHBot/FileContentIndex/10bfabad-ccfe-4ef4-b9cd-8ada0f6d1a49.vsidx +.vs/BHBot/FileContentIndex/0c85c9e4-508d-4ee6-9efb-1fe400ab65da.vsidx +.vs/BHBot/v17/workspaceFileList.bin +.vs/BHBot/v17/DocumentLayout.json +.vs/BHBot/v17/Browse.VC.db +.vs/BHBot/v17/.wsuo +.vs/VSWorkspaceState.json +.vs/tasks.vs.json +.vs/slnx.sqlite +.vs/ProjectSettings.json +__pycache__/abstract_mode.cpython-311.pyc +__pycache__/bot.cpython-311.pyc +__pycache__/characters.cpython-311.pyc +__pycache__/client_config.cpython-311.pyc +__pycache__/config.cpython-311.pyc +__pycache__/direct_input.cpython-311.pyc +__pycache__/font_loader.cpython-311.pyc +__pycache__/levels.cpython-311.pyc +__pycache__/menu.cpython-311.pyc +__pycache__/utils.cpython-311.pyc +__pycache__/windows.cpython-311.pyc +__pycache__/abstract_mode.cpython-311.pyc +__pycache__/bot.cpython-311.pyc +__pycache__/characters.cpython-311.pyc +__pycache__/client_config.cpython-311.pyc +__pycache__/config.cpython-311.pyc +__pycache__/direct_input.cpython-311.pyc +__pycache__/font_loader.cpython-311.pyc +__pycache__/levels.cpython-311.pyc +__pycache__/menu.cpython-311.pyc +__pycache__/utils.cpython-311.pyc +__pycache__/windows.cpython-311.pyc +__pycache__/windows.cpython-311.pyc +__pycache__/abstract_mode.cpython-311.pyc +__pycache__/bot.cpython-311.pyc +__pycache__/characters.cpython-311.pyc +__pycache__/client_config.cpython-311.pyc +__pycache__/config.cpython-311.pyc +__pycache__/direct_input.cpython-311.pyc +__pycache__/font_loader.cpython-311.pyc +__pycache__/levels.cpython-311.pyc +__pycache__/menu.cpython-311.pyc +__pycache__/utils.cpython-311.pyc +__pycache__/windows.cpython-311.pyc +languages/__pycache__/english.cpython-311.pyc +languages/__pycache__/russian.cpython-311.pyc +modes/default/__pycache__/fastlvl.cpython-311.pyc +modes/default/__pycache__/gold.cpython-311.pyc +modes/default/__pycache__/powerlvl.cpython-311.pyc +modes/default/__pycache__/totallvl.cpython-311.pyc diff --git a/__pycache__/abstract_mode.cpython-311.pyc b/__pycache__/abstract_mode.cpython-311.pyc index a8d565da63effff524d64bef99fba025744d5b98..4025c2e58271015e2d364ac1461cfa5892e98a3d 100644 GIT binary patch delta 272 zcmZ3*w~mi*IWI340}!Zgl1wva-pE(RXyW2*6%$&VT2vh4mzkU$<5HAgkd$8$6X2|% zVWb)3lAm0fo0?Zr9OIr@;!&Cu@d^d!}rdV|N-{lpb z!90g!smeuO^((yU9~hXSQqyfF+1wS7zapT!qIgZk`kGZWJ3J2*AE~*hA96)M3l{kzK41_40Qovl_5c6? delta 268 zcmZ3-w~CK%IWI340}x#N_c-+@(?-5BMpIX3tC-N@)S}`Tzs%(97?+~_f~5S4827~7 z%)Im%r=rC2oQ%YroWvL>52ySR-Q2|DlGLKjI~cDrF=kBeV-X8r=1gO}AuKkF7m2h;Z^^@zzCC@ZZpYdDgPA(+a1MwD)!gxsyX3#Aw23zc=AR2lq>cr z7ll)=2&Z1)Nxi`@wD~7X2%`)utIP)mAn}ozflaU_`U4Y804O>+ldaQ6k`br?lb8?= Q7X1j4`hrEih!5y}0EgRF@Bjb+ diff --git a/__pycache__/bot.cpython-311.pyc b/__pycache__/bot.cpython-311.pyc index c20dacb643f60801e5694220bca210bd223e3ceb..e354daa292bcf060d39ebe2887975af431046fc3 100644 GIT binary patch delta 3019 zcmaJ?eQZyiWbe^~ zFC}9m!^aN{pGofQsH|=DCu740PrfpAY-}{S{peWd$pgtPom+;-gbMnjqUI1dnxJHg z4i>IL#vNGjlPw8Z1LBO$q5fZ8#>>?u*j$3q1>yY?J)(^We^`31Pj6_+n2V;)5_7#w z6>u#Xw{OuMPP@aBdp&WlpVnS8%XDs^OnuYMan)~D+51ZcrnSUUD_Ux0J>}^_!gtC( zX9_Mse53zD_yy$+JRscMwU8YQ-guuU1(LfA^cscr>&WCJsZj9H4Y z7*krAr9ifkm;w-!!2=3E=p(k^qAirRg))|Ev1U_NLl^T9Krz2;ExzUwE$%Es7keNs z&XbCzVh|2z1*?WtGq322{C~3tI?BM}i>`eyWLWFlJKowcePrf@#8wfuDknHJ(=M@2 z!a8$;!!zHO*hNx^cvA*hD_jbEq8T?KP||l6l>HGim7|EdydCA@K<)6*q`v9c8gi zU6>iWewMh_tt#yjBLfmYkS)+~2?TYt*i`@K8j-Kf8nm2W*60jI+-Io3-wPGu;?Oo! zjFX{BR4#lP>O&!y5@-zs`f!30`RK|!H2_F^y*7PeHxrHCDJFVjz6ocnzbtGsHwzv8ahdWvu zEi$dK7hcs*Yo`xD8l@HZV(VFb1;@qF!v{`*j@mS6k5Jm~(4YfCI5v#Xap7lM|E@)& z!eso627MRPy`601TtZ|US6*Rv?+8NQ7e4Mw(z+RCTL}kt?H^a08JfbYRt8GHdLV9z z56TSG3#!dop>GK5kqeaXd|}mtQ}qmr+?dnz$|mKTdIpRXL5&VV5ETB;kX48de1gyi zxNLA|foC#46;H*5;lY%)<+n0{(UK=X(!bHC;1J z!}AZ;I_;h8n(9iy15}kuF1TLJ1_;4r1fFmiMSX>=%g3xPJAiON0?Q6qE{7ws*9-4I zpGN3U!m0i98kE8H2X6#T=BX$#xn|vIlUFo(KP;Ntb*oOQYm@w&iGTA#t2mI5IudDr zLi8sD`Vh3pr^3mhKOpo^q5a4u8hs)B?r0~Cz7qJCH_^HR=+x0Myz|(GjTxXW=b|-` zwg%>6H+PU=j0AT`);O`oA$X358UyGkr~CR|;s~c=Q@ts8WP@<$*e*nyX@Ng}U(;Hw zcr-(KC#Z~`UDU5h>(}I4a`q7MZ6Lnrg7&T%*w3J3t8^6r&|SF$^J820W-OIUmZIsN z+1O9Jh=DH{h-ym_1@8b$A-)#%^Aa0-GehRbj2@>TIdbVmEu{+ofPDaEHQQ<83fw zkDIG*O$@kYpO$kvkn>N=%>^L0ELpdu1mUcB;kyZa3h|=l*Ix z1E=R_M!tgGM%?gw#G}9|1EZ^Iol(7<{G&q35**L=XFfiSdM1 zH=y`Pms(WXbH;{vhGGpwz8|+u5Fu1T;DI*g3v|hNqw)_KPc4? zrfq{VqviUQ8xY3SL@W)WrElK580kqzdZb7niS&u<`eYMjD3N)5i7E8z8@^2nz9uE4u@0Wr}0UzY&~XrdX!( z(2c*i@?ssUxXOdwpxEZ2q=KoCsXRpR>#6F18ZU2GL;9fmGQ__ z9()DLXC4X}W%E4ni0MXfYOAQzDhhdE?TlTvQVKo}`1Irix+*-JT!Y$r4G0#$8MCVV L%V(^rm+k)mQ5T!8 delta 2981 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 RTj2g1S2X&?|Jl{@{|`o&hy?%u delta 2555 zcmZ`*eQZ-z6o0p0?4w(u-A2n+x-F2=fh#DA!kBF9#y1Smjg4+?X}j%7+gI*;V;eJW z@gE~0ad1OIBxplOEHOk7N%jZg=N~g_d{2v+FEJX8iTr^MzoNz%&v|bjZPe}M_s-5e z=iJ{p_ndckul?-y(pO4Kia7XD{{A*rZ#z?Zk8HjE*QdmA*Tw>N7yHpLPFk2?tQg-O zPVmWrKno2I#|OjlcsS6qqeYM&NQ6ZxMgx(-FbzjQ5*t#Ze-<^yc~Oc8iBuD9h3L42 zxCLN>o8>r9hanf`gNH7j%+zF>@2uaWtlJ~k@5y@h<{F?}*kSk?{JFa&?6%v*YE7S$ zM)rrP+OiFlw3&I#E=voFt*puHYqx`_ZTP#kquY-=F5vWr^G7nvG8^vr+LX0zvbQbk zZqHrUW$I_w&2F-TePVWw@5F!vAVhH+wc_XyACCqX(herV66HteK3H6ews8R1{JZ6q z|BkF{?sPmUcXr+B4#>TON;jYF=6~h5IB7B5=ePr;)$oh{(u`N368M7-5M*Z?%7C(| zOn-nbwR}eQvi2pOx@P2Pq{6Uh1gW~ZT4!C@&nI=#^f2h1?DUc^wj4xTGvx;cCE5tG z*Fd{aWsC@M(E!rCcX1+|3QB^OH0{L&mM&OF{5L)>SWTLq1ceq=HOGbF7}YGi2Gz(X zQ>rl~h`l8=RK@dc*iFUFEYmKK910F-bTG9l7Xa<06kP%B- zYlx3cTGs-DTh>!#Cnj=t1OP&yi&_|uoruN7;7Ce~qZSVcaAvyFYotlA(G5+wuvjYg zLaCcP%&wMZh@W-Z?pGtPD}+*vZJ^uG4~Z>g#0Hi&K80$r4&ZzEo#5n(4tO%Li-qac zS?3!4&s?N_u%$@aYgUbk@Q7*@hZ1x*Y8TRm?KGPC+m$Ty?SCBXM1%?MOPl@NLwe|& zLsL)O>X|NAHto-D+7CLVq9Y6F=*T)ca@3G^_JiXw2#=HAT=xD7+`>z!sK2nc7tB57o4fa-q8MO; z%PhNkO!huD!7Uc|ZVcVbswo*8kyJ}RABV5hA_cYNkR+sbf-WraLNcGs>*YSOi+xl6 zo?D|ZSCvx`FyHRo3LiPZeyXU2RxDp$OV;#&iE0>1(IY6B;<0`SYKk*Qv8}xp{ZuC!TAy2WN$dvkI$4j&u=`lJKdgsRB^1$NY|oQ#-^mHex<>$IQ-M5Y3YMv z`EXF~3Mtz|iX$XP2jyde5*1|Wq%4jpkuk+FCR@j5mX=LQXNuEH&W|d#H5vc4jaPP0 zwNE{&tZh|nt#3=y(Raq=u3ovbPigOihLDf;%X~s{A5F;ggnWEh2@fl_VYy&fujXc8 z7%{N0y^LLQb@;S#^I|pz)f9>GacE*b32ADxw*(rRPYfj%XQXDuCK6!XD_~yPEvt)d ztJ=DHuB;az3LOWyGS}`eg1q2)wz7?c*jH7{S#MD|_IL@Dp zEsm}BwaO><@=6a$vbR?50_P%6(_98H^AgQB40&oAjw8WR0ByJ@H$FdldQ>iOYUi*I zK3zCv8ax4Wv6;Q*sUH_mHH3hps}`iFkcv@hRI4j}78kU})e3M9O{}5>Js(NLq(LD{ zwKLG_nMPYJ`^zY05MD*V+d*%I7L@pTXaK<=9%nt(588=2NM^Ylw6F`+g57K$0d)?R zGFQztAAS|IIQzV%szv`Y)2*m%LfD0%eJ%EYq*kzI_)moHKrIfvcH8Pxh1ZdGljyAS znf_VOm=K~v@mLeR0d{zei}irBMncF8S0>LtvnEsN!?WBxOcc#px$0V(tD14S&zHeX z4F9@}>o2)0fKP9q9E#u*O)4QF8xmlZl6`nZA13?pj>ebn0y^*q6(Gnf4xkJua}{5^ yDgkF4f4HxMsQk#`~K7m;YJS&hH;HnXc;q diff --git a/__pycache__/client_config.cpython-311.pyc b/__pycache__/client_config.cpython-311.pyc index e29e1b003d1e539667f1f8c893ef920d45ebc588..83f78c844c091ea259e68b9c641cb4e2dbd3c9db 100644 GIT binary patch delta 63 zcmbQiI-QkoIWI340}!}vl1yV}+{o9&$mlY8BBPjWfU|;zk!FlbesXDUYFgJqmjUmy5{Cc) delta 64 zcmbQvI)jyOIWI340}x#N_c-+r!$!U)Mn>1k6B)(i+!J#%^U`CSiW19nG7@ug5@Vb^ TobpR_a}$e8Qi~=ZVZ00gpg9(w diff --git a/__pycache__/config.cpython-311.pyc b/__pycache__/config.cpython-311.pyc index 342846894217034519e38afb98ebaabda4a3ae72..54d8f0c4b7876e5364fecb4c6123cfb0da33e761 100644 GIT binary patch delta 1924 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 diff --git a/__pycache__/direct_input.cpython-311.pyc b/__pycache__/direct_input.cpython-311.pyc index a594d8ea2474ab147a5595c4398d47ef5859fdd0..22ad1dcdc62b099c6f483f856ca45a7d403dd6b8 100644 GIT binary patch delta 1347 zcmZ{jO>7%Q7=~wdwRU#BYkSw;)coy%Xz?DBLyA%^5eKT2RV5A_inwqBTkK&K$qER1pOqZ72M)|QiPb=|vwB85 z-#6d9@667&x}`cd1c3widGmY!JMEDNPJud`PQReMHg{E>Q^uyJ-d8ee{@P^zBjs{B z+I=RjWb#uBGgoKl=alU9d~RV<8Ojai=k?3fcTCEM0LA)>X_I%~9$y|`&8!WtPpyqr z`GmnIDtw~Gi+4JgE|o47aYGEwZA{QM`5FCQ%Q*YSr&vh={K30RBL*MFWlAHZ@kY1* zY48!mGBQ_WkrN0a$&2hI{IAD}tz3~qJYWTFQ1VOZqO;nPGg@+${a2rs95CL80K;@0 z3Mb8Ak^9i@dNzUrGYk2m_z7 zR)~eQdiM=@UsvNAJ@|msbwl!#?%06nS+SSxruUfXZ!>+*)dF!N(7Q=h{LkZq5PkR{ zLAebI1VthuG5gNUJVnV=|f zMcYm%ki0AYvft#LCNG&{(Da3BV$=|0rkp?mkb)MlOV|m<6DM$Zmb;gFN_WlZg)BoySgYb8~aOxEZw{_$63T^wF*lg84O!}d$eV*(J2tuFf!S{d-OBmig zctIed{ha*5mXO&LI-ykni Lc>RcI<0AbFzrcSx delta 1328 zcmZ`(e`pg|9KZKsVw&Dv)AUEYEM>t)qth3Z>S3`gUu(M^MYWFHo`ojV(7rVGB#J)xNa;U|`Y)jVF{Nf)H06zS znoY4^O=Tmh)q$*z#rkh;YC}7YUv`SDYV;tZM|nNtm3Ze~`7?_ifc?^JRb4rjrqh=| z%2hphN2l)wgxUh-PpW^=0l(a|<1hpt%YyB52*UEZ-2_0ttkoTZ;F8?1vloCFX=c|X zn3n(8Wd|TBZ{NKPy1&31CpwCKD|GDD6vl3jq(iAtW^~?=OXNNzW941cIiL&%loLZ@ zNM(bF4Jr)(CP!-8d+OkIQmmm^Y`vl~J(HTrmuiNiI8dcyh>jI>&}`bEFyOYkhUdnZ*B0ZQI z%=oiYxsH_s$az*dH>7k1#X*(f5xI!-8kCMVwQxu zG9JWuo>_k8e&iJYfD#mbiAKX>mh*BNiXI5RAmpSLFMkXjn5^rGJ}O z!CiTE{~Q4K<%=zT0KSpGKX^xHctl!eCBBsdv^3RPZ`sBi$XD%9?88bGnR%{Gdfa-` zz|m!Gnoa6Cat};Oe;#qWb$a|lVJPS*gXKD}K|+{bvqhI?0{Sle6}^Rqa(%5vy#bqa zwXM6xthZ~_W{^TVtWfZ@lewNzae&>o@}dF1s}riu}g^c#D6B F{{ZIlom2n- diff --git a/__pycache__/font_loader.cpython-311.pyc b/__pycache__/font_loader.cpython-311.pyc index d65b8942b3df701c33d9e67ac3fff5878e266770..930eb712a0fca575af4b87e4d353712ca0df9176 100644 GIT binary patch delta 184 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 MIN`uFd6KF&09MZBng9R* delta 855 zcmccPz1N#>IWI340}x#L_c-;D=te#rCQ}<{tC-N@)S}`Tzs%(97?+~_f~5S4827~7 z%)Im%r=rC2oQ%YroWvL>52ySR-Q2|DlGLKjc}$B~8Tls*a%>1YA#z6cLS+1v$c&4g z85eT%ujH0o$SJ+(QF>9l^nyU?4H2m+{tHr;}YL|q9< zzUY#CAtUQbM(&05yo=6x7lrdK@Z{a)6W^@N8OO+|zPX7@no*aZ)qY0R2L>Ruq6SPI zU^x(Rgb&O_CzLjy;*MtGHvsywH~~ofXkd8C!8_TE?=_>~rS1+InagA{Cl zC}?1Szz<9utjaTjKQI8P6)qbj*7$*$Cyc?=4H?xB%s?412P+}6d8=SBBcuCdPT?d* zx5+ue%NTt&3yXL%G6rmp6_sXWjNM!*_Kt}$b#sO!H;YjN$O+m&qJiN91D7Zpg18|q z-^(#Uq?50Q?*lWE%;Xiae~o%kw26bXK?ziC2${{dOMp diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc index 1a86895fbc6671ae8557713614d5c111acb05456..3aec19eed536b415789b51a099faf63fd80571c2 100644 GIT binary patch delta 2460 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 diff --git a/__pycache__/windows.cpython-311.pyc b/__pycache__/windows.cpython-311.pyc index 14b4ef0474574b2cdf9e44bd1f93bb13e0a5cb33..b4099f7a8d08b41a140d50e54fb355c08a156dfe 100644 GIT binary patch delta 958 zcmZva&rj1}7{}kYrDZMc+jeYhLvb6UvJnUy4wrb)2t&w3Lt;1(cj8w>aT7KYOibip z%$5)`QzXWaC6z8wJx+P?X`PoKB__WM5X^FFnZ zM$dSj1^6OV&*;gcFVQNDp6)!A?9Yr$W|9}jhOQ;!lj$3S>FH!&r!UwRO2*SeQ`bjQ z*-Wx~EZZ|RnC$53NN3ewaEPn)6j4A!0rd)cN41>6sxx2N4BMlme)#`xbK7^y{H)kHgapk7Vn{erzINQ6y)va@NMb4{n-Zjo!;-o?# zpO{VL5(Wi?>e=xPB(Ed6h#EE2h=0Ji{)G}b;ZRc5jPl#H2q%<9w+XZAoJWLkT6t9a z6W&rE93jZc8EiO1LS^Y#{<0h3R_&PFhn* zbZeq)3N{{X0}P`K2AkYK_3Nme;gm6oUbkA473hi zx7Fm3b#E#o%^%x!PSSZtVL;=(k6p{Pnm@Y6;iTQlTeNnCZ6nJzVspff12B@BY9WY- zZi)6`%z%>Q(ImNSx_No{Mf_E-7LDUZvRjR0XLB_6%s7>Yt#6^E^oIjAH03b>hDici zx^vketc0k{h3l$tlaSOdlsDlE!*&WE*FJ((6fWLAY7un_8sGqZ1m(kRwg;P^vKwF@ z=O9JN1~?3LJTd{{XZ2HC7{afL5M6+q>eJ|1BGN@b{Qm!Gmh;;OSNkdycdY#nhRPDD delta 869 zcmZvaQAkr!7=Z7&*SlTq?&w`px1@CDv{stp%o3uSV(!6ZgfVTUbB?K-&TUo_Qa%Jt zkX{aogpi=sdKf6TU@24tf`P%kazq~-1R>N@8(8#GbgrU^^q>EH2mS-!Ifws@2R`?~ zml{n5MPlXeSI-^899#fZ7i(*r&0$X{?6h~gea^a2puZz9=)B_U?e6nBts&Pvf0xVe zcR8&M*1$k%uPZ#@35kDzn^)(_(1f8$^ulG^N>nE z2?A(Zp8_Fq%#a1ZUG(C}Z*X6HmPa${2jm9fFBFO%8)=F1E6^fAOM=&7-Z)h>b1E8~ zHO^j(r^N@~W-jTZCda4z)z%K&8j$&Z%=b%tf0Cgx53j+(6<8?4A`FXmCB(CrVl4k1 zP(4H|tE*14PrRK_}*IlWOj;hFMAyK(Wkc*zGdu4$A^ zcB$!x#ieys6F=uIw5d{au(=GThMMlehz1OpaK&%1DO{ZiUe=dvH$ zZ_7d(7TO}J1gAsa&difePM06gNn;I3iXg>MD&f)1B&`&9v{8Qa5a}ty?f|7#G(J?b zL6D^OQOmFaQrX80j=fGQ`>3PY0`ft2!Rc9TlG=lXVNHtQAVM1Qj8H3qo6Ma+PgK0P rR&f=81+k>E9Dt9=VHpSW;t$JZx}psLBKbd4+x4yeyLAk5RagB39abC* diff --git a/abstract_mode.py b/abstract_mode.py index dd9b26e..215ccad 100644 --- a/abstract_mode.py +++ b/abstract_mode.py @@ -26,5 +26,7 @@ def get_name(cls): if isinstance(cls.name, str): return cls.name # Shouldn't be used, here for backwards compatibility elif isinstance(cls.name, dict): - return cls.name.get(global_settings.language_name, cls.name.get('default', cls.__name__)) + return cls.name.get( + global_settings.language_name, cls.name.get("default", cls.__name__) + ) return cls.__name__ diff --git a/bot.py b/bot.py index ef6e70d..abd989b 100644 --- a/bot.py +++ b/bot.py @@ -76,11 +76,11 @@ def ensure_brawlhalla(self): try: steam = SteamClient() except SteamExeNotFound: - logger.error('no_steam_exe') + logger.error("no_steam_exe") return self.on_exit() count = 10000 while not self.find_brawlhalla(): - logger.debug('waiting_for_bh_window') + logger.debug("waiting_for_bh_window") self.process_queue() count += 1 if count >= 10000: @@ -90,16 +90,16 @@ def ensure_brawlhalla(self): self.virtual_input = VirtualInput(self.brawlhalla, self.hotkeys) self.level_definer = LevelDefiner(self.brawlhalla) - logger.info('found_bh') + logger.info("found_bh") self.virtual_input.esc() # idk why but it puts bh into windowed sleep(1) if self.brawlhalla.fullscreen: - logger.info('not_windowed_mode') + logger.info("not_windowed_mode") raise NotRespondingError self.brawlhalla.resize() self.get_states() if self.config.stealth: - logger.info('stealth_mode') + logger.info("stealth_mode") self.brawlhalla.hide() self.brawlhalla.set_low_priority() @@ -109,7 +109,7 @@ def initialize(self): self.go_to_menu(True) regenerate_layout() - self.current_menu_element = find_element('first_column').current_element + self.current_menu_element = find_element("first_column").current_element sleep(2) if self.config.mute: @@ -118,28 +118,44 @@ def initialize(self): if not self.characters: if self.mode.parse_character_levels: self.characters = self.get_characters() - self.unlocked_characters = [character for character in self.characters if character.unlocked] + self.unlocked_characters = [ + character for character in self.characters if character.unlocked + ] else: self.characters = [Character(name) for name in characters] self.unlocked_characters = self.characters self.character = self.unlocked_characters[0] - logger.debug('initialized') + logger.debug("initialized") def on_exit(self): if self.virtual_input: self.virtual_input.release_keys() if self.config.stealth and self.brawlhalla: self.brawlhalla.kill() - text = global_settings.messages.get('initial_on_exit', 'initial_on_exit') % (format_time(time() - self.time_started), self.games_completed, self.crashes, self.total_xp) + text = global_settings.messages.get("initial_on_exit", "initial_on_exit") % ( + format_time(time() - self.time_started), + self.games_completed, + self.crashes, + self.total_xp, + ) if self.mode.parse_character_levels: - text += global_settings.messages.get('on_exit_has_rewards', 'on_exit_has_rewards') % (self.total_gold,) + text += global_settings.messages.get( + "on_exit_has_rewards", "on_exit_has_rewards" + ) % (self.total_gold,) else: - text += global_settings.messages.get('on_exit_no_rewards', 'on_exit_no_rewards') % (self.total_gold,) + text += global_settings.messages.get( + "on_exit_no_rewards", "on_exit_no_rewards" + ) % (self.total_gold,) box(text, endmargin=False) global_settings.update_stats(time=time() - self._time_started) stats = global_settings.get_stats() - total = global_settings.messages.get('total_stats', 'total_stats') % (stats.get('games', 0), stats.get('xp', 0), stats.get('gold', 0), format_time(stats.get('time', 0))) + total = global_settings.messages.get("total_stats", "total_stats") % ( + stats.get("games", 0), + stats.get("xp", 0), + stats.get("gold", 0), + format_time(stats.get("time", 0)), + ) box(total, endmargin=False) sys.exit() @@ -151,9 +167,9 @@ def process_queue(self, stop_delayed=False): except queue.Empty: continue self.queue.task_done() - if msg == 'STOP': + if msg == "STOP": raise KeyboardInterrupt - elif msg == 'DELAYED_STOP': + elif msg == "DELAYED_STOP": if stop_delayed: raise KeyboardInterrupt put_back.append(msg) # cycle it in queue waiting for delayed_stop check @@ -172,107 +188,106 @@ def state_conditions(self): conn_x = 1806 - ceil(98.5 * 2) # 1609 low_conn_x = conn_x - 26 # 1583 - _new_menu = { - 'pixels': ((1890, 70),), - 'colors': ((255, 255, 255),) - } + _new_menu = {"pixels": ((1890, 70),), "colors": ((255, 255, 255),)} res = { - 'ingame': { - 'pixels': ((conn_x, 58),), - 'colors': (CONNECTION_LEVELS[0],), + "ingame": { + "pixels": ((conn_x, 58),), + "colors": (CONNECTION_LEVELS[0],), }, - 'low_connection': { - 'pixels': ((low_conn_x, 74),), - 'colors': CONNECTION_LEVELS, + "low_connection": { + "pixels": ((low_conn_x, 74),), + "colors": CONNECTION_LEVELS, }, - 'menu': self.state_detection_pixels.get('menu') or _new_menu, - 'loading': { - 'pixels': ((899, 85),), - 'colors': ((227, 248, 255),), + "menu": self.state_detection_pixels.get("menu") or _new_menu, + "loading": { + "pixels": ((899, 85),), + "colors": ((227, 248, 255),), }, - 'bonus': { - 'pixels': ((930, 320),), - 'colors': ((109, 198, 211),), + "bonus": { + "pixels": ((930, 320),), + "colors": ((109, 198, 211),), }, - 'offline': { - 'pixels': ((1674, 26),), - 'colors': ((55, 66, 100), (57, 67, 101)), + "offline": { + "pixels": ((1674, 26),), + "colors": ((55, 66, 100), (57, 67, 101)), }, - 'sorted_by_date': { - 'pixels': ((331, 766),), - 'colors': ((254, 254, 255),), + "sorted_by_date": { + "pixels": ((331, 766),), + "colors": ((254, 254, 255),), }, - 'lobby': { - 'pixels': ((1325, 22),), - 'colors': ((255, 255, 255),), + "lobby": { + "pixels": ((1325, 22),), + "colors": ((255, 255, 255),), }, - 'game_in_progress': { - 'pixels': ((960, 395),), - 'colors': ((111, 200, 211),), + "game_in_progress": { + "pixels": ((960, 395),), + "colors": ((111, 200, 211),), }, - 'settings_open': { - 'pixels': ((1221, 106),), - 'colors': ((220, 220, 222),), + "settings_open": { + "pixels": ((1221, 106),), + "colors": ((220, 220, 222),), }, - 'disconnected': { - 'pixels': ((934, 623),), - 'colors': ((247, 248, 249),), + "disconnected": { + "pixels": ((934, 623),), + "colors": ((247, 248, 249),), }, - 'system_settings_selected': { - 'pixels': ((1607, 195),), - 'colors': ((39, 85, 136),), + "system_settings_selected": { + "pixels": ((1607, 195),), + "colors": ((39, 85, 136),), }, - 'on_rewards_screen': { - 'pixels': ((1035, 121),), - 'colors': ((255, 255, 255),), + "on_rewards_screen": { + "pixels": ((1035, 121),), + "colors": ((255, 255, 255),), }, - 'level_up': { - 'pixels': ((1363, 320),), - 'colors': ((19, 133, 51),), + "level_up": { + "pixels": ((1363, 320),), + "colors": ((19, 133, 51),), }, - 'popup': { - 'pixels': ((940, 790),), - 'colors': ((247, 248, 249),), + "popup": { + "pixels": ((940, 790),), + "colors": ((247, 248, 249),), }, - 'in_mallhalla': { - 'pixels': ((338, 972),), - 'colors': ((100, 77, 255),), + "in_mallhalla": { + "pixels": ((338, 972),), + "colors": ((100, 77, 255),), }, - 'in_battle_pass': { - 'pixels': ((171, 660),), - 'colors': ((181, 201, 225),), + "in_battle_pass": { + "pixels": ((171, 660),), + "colors": ((181, 201, 225),), }, } for key in res.keys(): - if key != 'menu' and key in self.state_detection_pixels: + if key != "menu" and key in self.state_detection_pixels: res[key] = self.state_detection_pixels[key] return res @property def duration_setting(self): - return [self.open_settings, 1] + \ - [self.virtual_input.down] * 3 + \ - (self.mode.next_duration - self.duration) * [self.virtual_input.right] + \ - (self.duration - self.mode.next_duration) * [self.virtual_input.left] + \ - [self.virtual_input.quick] + return ( + [self.open_settings, 1] + + [self.virtual_input.down] * 3 + + (self.mode.next_duration - self.duration) * [self.virtual_input.right] + + (self.duration - self.mode.next_duration) * [self.virtual_input.left] + + [self.virtual_input.quick] + ) @property def danger_zone(self): - return {'in_mallhalla', 'in_battle_pass'} + return {"in_mallhalla", "in_battle_pass"} @property def safe_states(self): - return {'ingame', 'low_connection'} + return {"ingame", "low_connection"} @staticmethod def is_color(screenshot, pixels, colors): - tmp = [ screenshot.getpixel(pixel) for pixel in pixels ] + tmp = [screenshot.getpixel(pixel) for pixel in pixels] return any(screenshot.getpixel(pixel) in colors for pixel in pixels) - def execute_steps(self, *steps, delay=.2): + def execute_steps(self, *steps, delay=0.2): self.get_states() for step in steps: if isinstance(step, (int, float)): @@ -295,31 +310,34 @@ def main_sequence(self): while True: self.execute_steps(self.before_fight, self.go_to_fight) - logger.info('started_fighting') - last, ig = True, True # To avoid failing ingame detection on low connection bc of "Double kill" popup covering first connection column for 1 frame + logger.info("started_fighting") + last, ig = ( + True, + True, + ) # To avoid failing ingame detection on low connection bc of "Double kill" popup covering first connection column for 1 frame while last or ig: self.get_states() - last, ig = ig, self.has_state('ingame', 'low_connection') + last, ig = ig, self.has_state("ingame", "low_connection") self.virtual_input.fight() - self.execute_steps('ended_fighting', 5, self.after_fight) + self.execute_steps("ended_fighting", 5, self.after_fight) except NotRespondingError: self.crashes += 1 sleep(5) - logger.info('reinitializing') + logger.info("reinitializing") except QueuedRecalculation: sleep(5) - logger.info('queued_recalc') + logger.info("queued_recalc") except ResizedError: - logger.warning('resized_warning') + logger.warning("resized_warning") sleep(5) except DangerZoneError: - logger.warning('danger_zone_warning') + logger.warning("danger_zone_warning") sleep(5) except InvalidStateError: self.crashes += 1 - logger.warning('invalid_state_warning') + logger.warning("invalid_state_warning") sleep(5) def main_loop(self): @@ -353,32 +371,32 @@ def has_state(self, *states): def go_to_menu(self, initial=False): iters = 0 self.get_states() - while not self.has_state('menu'): + while not self.has_state("menu"): iters += 1 - logger.debug('not_in_menu') + logger.debug("not_in_menu") self.virtual_input.esc() sleep(1) - if self.has_state('bonus'): - logger.info('collecting_bonus') + if self.has_state("bonus"): + logger.info("collecting_bonus") self.virtual_input.quick() - if self.has_state('popup'): - logger.info('accepting_event_popup') + if self.has_state("popup"): + logger.info("accepting_event_popup") self.virtual_input.quick() - if not initial and self.has_state('offline'): - logger.info('offline') - self.select_menu_item('custom_game_room') + if not initial and self.has_state("offline"): + logger.info("offline") + self.select_menu_item("custom_game_room") self.go_to_lobby(100) - logger.info('reconnected') + logger.info("reconnected") self.go_to_menu() if iters > 100: raise NotRespondingError self.get_states() def select_item(self, item, *steps): - while not self.has_state(f'{item}_selected'): - logger.debug('item_not_selected', item) - self.execute_steps(*steps, delay=.05) - if self.has_state('game_in_progress'): + while not self.has_state(f"{item}_selected"): + logger.debug("item_not_selected", item) + self.execute_steps(*steps, delay=0.05) + if self.has_state("game_in_progress"): self.virtual_input.dodge() def select_menu_item(self, name): @@ -388,19 +406,25 @@ def select_menu_item(self, name): self.execute_steps(*keys) def mute(self): - logger.info('muting') - self.select_menu_item('system_settings') + logger.info("muting") + self.select_menu_item("system_settings") self.get_states() - if not self.has_state('system_settings_selected'): + if not self.has_state("system_settings_selected"): raise InvalidStateError - self.execute_steps(self.virtual_input.quick, *([self.virtual_input.left] * 10), self.virtual_input.down, *([self.virtual_input.left] * 10), self.virtual_input.dodge) + self.execute_steps( + self.virtual_input.quick, + *([self.virtual_input.left] * 10), + self.virtual_input.down, + *([self.virtual_input.left] * 10), + self.virtual_input.dodge, + ) def sort_by_date(self): counter = 0 - while not self.has_state('sorted_by_date'): - logger.debug('sorting_by_date') + while not self.has_state("sorted_by_date"): + logger.debug("sorting_by_date") self.virtual_input.enter() - sleep(.5) + sleep(0.5) self.get_states() if counter > 10: raise InvalidStateError @@ -409,9 +433,9 @@ def sort_by_date(self): def get_characters(self): _characters = [] rotation = get_rotation() - self.select_menu_item('meet_the_legends') - self.execute_steps(self.virtual_input.quick, .5, self.sort_by_date) - logger.info('collecting_character_data') + self.select_menu_item("meet_the_legends") + self.execute_steps(self.virtual_input.quick, 0.5, self.sort_by_date) + logger.info("collecting_character_data") for line in level_character_matrix: for character in line: self.get_states() @@ -421,19 +445,23 @@ def get_characters(self): _characters.append(Character(character, level, xp, unlocked)) logger.info(_characters[-1]) self.virtual_input.right() - sleep(.15) - #self.virtual_input.down() - sleep(.15) - unlocked_characters = [character.name for character in _characters if character.unlocked] - locked_characters = [character.name for character in _characters if not character.unlocked] - fixed_characters = unlocked_characters + ['random'] + locked_characters + sleep(0.15) + # self.virtual_input.down() + sleep(0.15) + unlocked_characters = [ + character.name for character in _characters if character.unlocked + ] + locked_characters = [ + character.name for character in _characters if not character.unlocked + ] + fixed_characters = unlocked_characters + ["random"] + locked_characters build_character_matrix(fixed_characters) self.go_to_menu() return _characters def go_to_lobby(self, max_iters=10): iters = 0 - while not self.has_state('lobby'): + while not self.has_state("lobby"): self.virtual_input.quick() sleep(2) if iters > max_iters: @@ -443,34 +471,34 @@ def go_to_lobby(self, max_iters=10): def validate_level(self): self.go_to_rewards_screen() - if self.duration < 3 or self.has_state('level_up'): - logger.debug('skip_lvl_valid') + if self.duration < 3 or self.has_state("level_up"): + logger.debug("skip_lvl_valid") return True xp = self.level_definer.get_xp(self.character.level, True) calculated_xp = get_duration_xp(self.duration) - logger.debug('calc_xp', calculated_xp) - logger.debug('pixel_xp', xp) - if (self.character.level < 40 and abs(xp - calculated_xp) > calculated_xp / 3): - logger.info('xp_discrep') + logger.debug("calc_xp", calculated_xp) + logger.debug("pixel_xp", xp) + if self.character.level < 40 and abs(xp - calculated_xp) > calculated_xp / 3: + logger.info("xp_discrep") return False return True def go_to_rewards_screen(self): - while not self.has_state('on_rewards_screen'): + while not self.has_state("on_rewards_screen"): self.virtual_input.quick() sleep(5) self.get_states() def open_settings(self): - while not self.has_state('settings_open'): + while not self.has_state("settings_open"): self.virtual_input.heavy() sleep(2) self.get_states() def wait_for_loading(self): iters = 0 - while not self.has_state('loading'): - logger.debug('waiting_for_loading') + while not self.has_state("loading"): + logger.debug("waiting_for_loading") iters += 1 self.virtual_input.quick() sleep(2) @@ -480,8 +508,8 @@ def wait_for_loading(self): def wait_for_loaded(self): iters = 0 - while self.has_state('loading'): - logger.debug('loading') + while self.has_state("loading"): + logger.debug("loading") iters += 1 sleep(1) if iters > 100: @@ -489,23 +517,30 @@ def wait_for_loaded(self): self.get_states() def pick_character(self): - logger.info('pick_char', self.mode.next_character) + logger.info("pick_char", self.mode.next_character) if self.character != self.mode.next_character: - self.execute_steps(*self.character.get_path_to(self.mode.next_character.name)) + self.execute_steps( + *self.character.get_path_to(self.mode.next_character.name) + ) self.character = self.mode.next_character def set_duration(self): - logger.info('setting_dur', self.mode.next_duration) + logger.info("setting_dur", self.mode.next_duration) if self.duration != self.mode.next_duration: self.execute_steps(*self.duration_setting) self.duration = self.mode.next_duration def reset_xp(self): - self.execute_steps(self.virtual_input.dodge, self.virtual_input.dodge, self.go_to_menu) + self.execute_steps( + self.virtual_input.dodge, self.virtual_input.dodge, self.go_to_menu + ) waiting_start = time() - logger.info('wait_for_xp_reset', self.config.auto_stop_duration) + logger.info("wait_for_xp_reset", self.config.auto_stop_duration) while time() - waiting_start < self.config.auto_stop_duration * 60: - logger.debug('wait_remaining', int(waiting_start + self.config.auto_stop_duration * 60 - time())) + logger.debug( + "wait_remaining", + int(waiting_start + self.config.auto_stop_duration * 60 - time()), + ) self.check_stuff() sleep(1) self.last_pause = time() @@ -515,58 +550,72 @@ def reset_xp(self): def setup_lobby(self): # noinspection PyTypeChecker - steps = [self.open_settings] + \ - [self.virtual_input.right] * 8 + \ - [self.virtual_input.down] * 3 + \ - [self.virtual_input.left] * (2 - self.duration) + \ - [self.virtual_input.right] * (self.duration - 2) + \ - [self.virtual_input.down] * 2 + \ - [self.virtual_input.left] * 6 + \ - [self.virtual_input.down] * 2 + \ - [self.virtual_input.right] + \ - [self.virtual_input.rbr] + \ - [self.virtual_input.down] * 3 + \ - [self.virtual_input.left, self.virtual_input.down] * 3 + \ - [self.virtual_input.left, self.virtual_input.quick] + steps = ( + [self.open_settings] + + [self.virtual_input.right] * 8 + + [self.virtual_input.down] * 3 + + [self.virtual_input.left] * (2 - self.duration) + + [self.virtual_input.right] * (self.duration - 2) + + [self.virtual_input.down] * 2 + + [self.virtual_input.left] * 6 + + [self.virtual_input.down] * 2 + + [self.virtual_input.right] + + [self.virtual_input.rbr] + + [self.virtual_input.down] * 3 + + [self.virtual_input.left, self.virtual_input.down] * 3 + + [self.virtual_input.left, self.virtual_input.quick] + ) self.execute_steps(*steps) def add_bots(self): - steps = [self.virtual_input.throw, 1] + \ - [self.virtual_input.down] * 3 + \ - [self.virtual_input.quick] * 4 + \ - [self.virtual_input.throw] + steps = ( + [self.virtual_input.throw, 1] + + [self.virtual_input.down] * 3 + + [self.virtual_input.quick] * 4 + + [self.virtual_input.throw] + ) self.execute_steps(*steps) def initial_setup(self): - self.execute_steps('creating_lobby', 1, 1) - self.select_menu_item('custom_game_room') - self.execute_steps(self.go_to_lobby, 'setting_lobby', self.setup_lobby, 4, self.add_bots) + self.execute_steps("creating_lobby", 1, 1) + self.select_menu_item("custom_game_room") + self.execute_steps( + self.go_to_lobby, "setting_lobby", self.setup_lobby, 4, self.add_bots + ) def before_fight(self): self.execute_steps(2, self.pick_character, 1, self.set_duration, 1) def go_to_fight(self): self.process_queue(True) - self.execute_steps('starting_game', self.wait_for_loading, self.wait_for_loaded, 'loaded', 5) + self.execute_steps( + "starting_game", self.wait_for_loading, self.wait_for_loaded, "loaded", 5 + ) def after_fight(self): self.get_states() - if self.has_state('disconnected', 'game_in_progress', 'offline'): - logger.info('disconnected') + if self.has_state("disconnected", "game_in_progress", "offline"): + logger.info("disconnected") raise NotRespondingError self.games_completed += 1 calc_xp = get_duration_xp(self.duration) time_to_sleep = self.config.auto_stop and ( - (not self.config.auto_detect_auto_stop and time() - self.last_pause > self.config.auto_stop_frequency * 3600) - or (self.config.auto_detect_auto_stop and not self.validate_level())) + ( + not self.config.auto_detect_auto_stop + and time() - self.last_pause > self.config.auto_stop_frequency * 3600 + ) + or (self.config.auto_detect_auto_stop and not self.validate_level()) + ) gold_for_level_up = self.character.add_xp(calc_xp) calc_gold = get_duration_gold(self.duration) + gold_for_level_up self.total_xp += calc_xp self.total_gold += calc_gold - logger.debug('update_total_stats') - global_settings.update_stats(games=1, time=time() - self._time_started, gold=calc_gold, xp=calc_xp) + logger.debug("update_total_stats") + global_settings.update_stats( + games=1, time=time() - self._time_started, gold=calc_gold, xp=calc_xp + ) self._time_started = time() - logger.info('return_to_lobby') + logger.info("return_to_lobby") self.go_to_lobby() sleep(2) self.process_queue(True) diff --git a/characters.py b/characters.py index 6e99054..5cc8b5b 100644 --- a/characters.py +++ b/characters.py @@ -1,13 +1,77 @@ from levels import * -characters = ['bödvar', 'cassidy', 'orion', 'lord vraxx', 'gnash', 'queen nai', 'hattori', 'sir roland', 'scarlet', 'thatch', 'ada', 'sentinel', 'lucien', - 'teros', 'brynn', 'asuri', 'barraza', 'ember', 'azoth', 'koji', 'ulgrim', 'diana', 'jhala', 'kor', 'wu shang', 'val', - 'ragnir', 'cross', 'mirage', 'nix', 'mordex', 'yumiko', 'artemis', 'caspian', 'sidra', 'xull', 'kaya', 'isaiah', 'jiro', - 'lin fei', 'zariel', 'rayman', 'dusk', 'fait', 'thor', 'petra', 'vector', 'volkov', 'onyx', 'jaeyun', 'mako', 'magyar', 'reno', 'munin', 'arcadia', 'ezio', 'tezca', - 'thea', 'red raptor', 'loki', 'seven', 'vivi'] +characters = [ + "bödvar", + "cassidy", + "orion", + "lord vraxx", + "gnash", + "queen nai", + "hattori", + "sir roland", + "scarlet", + "thatch", + "ada", + "sentinel", + "lucien", + "teros", + "brynn", + "asuri", + "barraza", + "ember", + "azoth", + "koji", + "ulgrim", + "diana", + "jhala", + "kor", + "wu shang", + "val", + "ragnir", + "cross", + "mirage", + "nix", + "mordex", + "yumiko", + "artemis", + "caspian", + "sidra", + "xull", + "kaya", + "isaiah", + "jiro", + "lin fei", + "zariel", + "rayman", + "dusk", + "fait", + "thor", + "petra", + "vector", + "volkov", + "onyx", + "jaeyun", + "mako", + "magyar", + "reno", + "munin", + "arcadia", + "ezio", + "tezca", + "thea", + "red raptor", + "loki", + "seven", + "vivi", +] level_character_matrix_width = 15 -level_character_matrix = list([characters[i:i + level_character_matrix_width] for i in range(0, len(characters), level_character_matrix_width)]) +level_character_matrix = list( + [ + characters[i : i + level_character_matrix_width] + for i in range(0, len(characters), level_character_matrix_width) + ] +) character_matrix_width = 13 character_matrix = [] @@ -15,10 +79,15 @@ def build_character_matrix(_characters): global character_matrix - character_matrix = list([_characters[i:i + character_matrix_width] for i in range(0, len(_characters), character_matrix_width)]) + character_matrix = list( + [ + _characters[i : i + character_matrix_width] + for i in range(0, len(_characters), character_matrix_width) + ] + ) -build_character_matrix(characters + ['random']) +build_character_matrix(characters + ["random"]) def find_char(name): @@ -30,7 +99,7 @@ def find_char(name): def map_to_char(row, col): - return ['down'] * (row - 1) + ['right'] * (col - 1) + return ["down"] * (row - 1) + ["right"] * (col - 1) def parse_pos(inp): @@ -61,7 +130,9 @@ def add_xp(self, xp): return gold def get_xp_to_level(self, level): - return max(sum((levels_xp[level]) for level in range(self.level, level)) - self.xp, 1) + return max( + sum((levels_xp[level]) for level in range(self.level, level)) - self.xp, 1 + ) @property def xp_to_next_level(self): @@ -86,8 +157,18 @@ def get_path_to(self, name): orow, opos = find_char(self.name) trow, tpos = find_char(name) if orow == 4: - return (trow - orow) * ['down'] + (orow - trow) * ['up'] + (tpos - opos) * ['right'] + (opos - tpos) * ['left'] - return (tpos - opos) * ['right'] + (opos - tpos) * ['left'] + (trow - orow) * ['down'] + (orow - trow) * ['up'] + return ( + (trow - orow) * ["down"] + + (orow - trow) * ["up"] + + (tpos - opos) * ["right"] + + (opos - tpos) * ["left"] + ) + return ( + (tpos - opos) * ["right"] + + (opos - tpos) * ["left"] + + (trow - orow) * ["down"] + + (orow - trow) * ["up"] + ) @staticmethod def get_duration_for_xp(xp, maximum=15): @@ -108,7 +189,7 @@ def duration_to_next_gold(self): return self.get_duration_to_next_gold() def __str__(self): - return f'<{self.name.capitalize()} (lvl: {self.level}, xp: {self.xp}, unlocked: {self.unlocked})>' + return f"<{self.name.capitalize()} (lvl: {self.level}, xp: {self.xp}, unlocked: {self.unlocked})>" def __repr__(self): return str(self) diff --git a/client_config.py b/client_config.py index 77149dc..83e89ad 100644 --- a/client_config.py +++ b/client_config.py @@ -1,7 +1,7 @@ class ClientConfig(object): - PUBLIC_KEY = 'fwL+l4+Xb4lAf5zdgwEKqr+zu+cO9kZ1/6uOb3/RNoA' - APP_NAME = 'BHBot' - COMPANY_NAME = 'Sovamorco' + PUBLIC_KEY = "fwL+l4+Xb4lAf5zdgwEKqr+zu+cO9kZ1/6uOb3/RNoA" + APP_NAME = "BHBot" + COMPANY_NAME = "Sovamorco" HTTP_TIMEOUT = 30 MAX_DOWNLOAD_RETRIES = 3 - UPDATE_URLS = ['https://sovamor.co/bhbot/versions'] + UPDATE_URLS = ["https://sovamor.co/bhbot/versions"] diff --git a/config.py b/config.py index 847dd45..184f667 100644 --- a/config.py +++ b/config.py @@ -7,38 +7,48 @@ def display_changelog(): - changelog_path = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'changelog' - if global_settings.compiled and (not changelog_path.exists() or changelog_path.read_text('utf-8') != global_settings.APP_VERSION): - Sg.popup(*global_settings.APP_CHANGELOG, font=(global_settings.font, 13), title=global_settings.language.LAYOUT_MAPPING.get('changelog_popup_title', 'Changelog'), icon=global_settings.icon) + changelog_path = Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "changelog" + if global_settings.compiled and ( + not changelog_path.exists() + or changelog_path.read_text("utf-8") != global_settings.APP_VERSION + ): + Sg.popup( + *global_settings.APP_CHANGELOG, + font=(global_settings.font, 13), + title=global_settings.language.LAYOUT_MAPPING.get( + "changelog_popup_title", "Changelog" + ), + icon=global_settings.icon, + ) changelog_path.parent.mkdir(parents=True, exist_ok=True) - changelog_path.write_text(global_settings.APP_VERSION, 'utf-8') + changelog_path.write_text(global_settings.APP_VERSION, "utf-8") class Config: def __init__(self, config): - self.character = config.get('character', 'Random') - self.duration = config.get('duration', 8) - self.auto_stop = config.get('auto_stop', True) - self.auto_detect_auto_stop = config.get('auto_detect_auto_stop', False) - self.auto_stop_frequency = config.get('auto_stop_frequency', 5) - self.auto_stop_duration = config.get('auto_stop_duration', 30) + self.character = config.get("character", "Random") + self.duration = config.get("duration", 8) + self.auto_stop = config.get("auto_stop", True) + self.auto_detect_auto_stop = config.get("auto_detect_auto_stop", False) + self.auto_stop_frequency = config.get("auto_stop_frequency", 5) + self.auto_stop_duration = config.get("auto_stop_duration", 30) # self.bots = config.get('bots', 2) self.bots = 2 - self.mute = config.get('mute', False) - self.stealth = config.get('stealth', False) + self.mute = config.get("mute", False) + self.stealth = config.get("stealth", False) self.modes = self.get_modes() if not self.modes: - logger.error('no_modes') + logger.error("no_modes") else: - self.mode_name = config.get('mode_name', self.modes[0].get_name()) + self.mode_name = config.get("mode_name", self.modes[0].get_name()) self.version = global_settings.APP_VERSION @classmethod def load(cls): try: - res = json.load(global_settings.config_location.open('r')) - if res.get('version') != global_settings.APP_VERSION: - logger.warning('old_config') + res = json.load(global_settings.config_location.open("r")) + if res.get("version") != global_settings.APP_VERSION: + logger.warning("old_config") return cls(res) except FileNotFoundError: return cls({}) @@ -46,10 +56,10 @@ def load(cls): # noinspection PyUnresolvedReferences @staticmethod def get_modes(): - for mode in global_settings.modes_folder.glob('**/*.py'): + for mode in global_settings.modes_folder.glob("**/*.py"): if mode in global_settings.loaded_modes: continue - spec = importlib.util.spec_from_file_location('module.name', mode) + spec = importlib.util.spec_from_file_location("module.name", mode) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) global_settings.loaded_modes[mode] = module @@ -67,7 +77,7 @@ def mode(self): @property def not_save(self): - return ['modes'] + return ["modes"] def get_save_vars(self): return {k: v for k, v in vars(self).items() if k not in self.not_save} @@ -75,12 +85,14 @@ def get_save_vars(self): def save(self): try: global_settings.config_location.parent.mkdir(parents=True, exist_ok=True) - json.dump(self.get_save_vars(), global_settings.config_location.open('w+')) + json.dump(self.get_save_vars(), global_settings.config_location.open("w+")) except Exception as e: - logger.error('cant_save_config', e) + logger.error("cant_save_config", e) def __str__(self): - return global_settings.messages.get('config', 'Missing "config" entry in language').format(self) + return global_settings.messages.get( + "config", 'Missing "config" entry in language' + ).format(self) # noinspection PyUnresolvedReferences @@ -92,8 +104,8 @@ def __init__(self): @property def characters(self): res = [character.capitalize() for character in sorted(characters)] - if hasattr(self, 'window') and not self.get_mode().parse_character_levels: - res.insert(0, 'Random') + if hasattr(self, "window") and not self.get_mode().parse_character_levels: + res.insert(0, "Random") return res @property @@ -105,41 +117,45 @@ def language_names(self): return [language.LANGUAGE for language in global_settings.languages] def get_mode(self): - return self.config.get_mode(self.window['mode_name'].get()) + return self.config.get_mode(self.window["mode_name"].get()) def update_layout(self): - if self.window['mode_name'].Values != self.mode_names: - self.window['mode_name'].Update(self.mode_names[0], values=self.mode_names) - if self.window['character'].Values != self.characters: - char = self.window['character'].get() if self.window['character'].get() in self.characters else self.characters[0] - self.window['character'].Update(char, values=self.characters) + if self.window["mode_name"].Values != self.mode_names: + self.window["mode_name"].Update(self.mode_names[0], values=self.mode_names) + if self.window["character"].Values != self.characters: + char = ( + self.window["character"].get() + if self.window["character"].get() in self.characters + else self.characters[0] + ) + self.window["character"].Update(char, values=self.characters) mode = self.get_mode() if mode.character_selection_enabled: - self.window['character'].update(disabled=False, readonly=True) + self.window["character"].update(disabled=False, readonly=True) else: - self.window['character'].update(disabled=True, readonly=True) + self.window["character"].update(disabled=True, readonly=True) if not mode.parse_character_levels: - self.window['auto_detect_auto_stop'].update(False, disabled=True) + self.window["auto_detect_auto_stop"].update(False, disabled=True) if mode.duration_selection_enabled: - self.window['duration'].update(disabled=False) + self.window["duration"].update(disabled=False) else: - self.window['duration'].update(disabled=True) - if self.window['auto_stop'].get(): - self.window['auto_stop_duration'].update(disabled=False) + self.window["duration"].update(disabled=True) + if self.window["auto_stop"].get(): + self.window["auto_stop_duration"].update(disabled=False) if mode.parse_character_levels: - self.window['auto_detect_auto_stop'].update(disabled=False) - if self.window['auto_detect_auto_stop'].get(): - self.window['auto_stop_frequency'].update(disabled=True) + self.window["auto_detect_auto_stop"].update(disabled=False) + if self.window["auto_detect_auto_stop"].get(): + self.window["auto_stop_frequency"].update(disabled=True) else: - self.window['auto_stop_frequency'].update(disabled=False) + self.window["auto_stop_frequency"].update(disabled=False) else: - self.window['auto_detect_auto_stop'].update(disabled=True) - self.window['auto_stop_frequency'].update(disabled=True) - self.window['auto_stop_duration'].update(disabled=True) - if self.window['stealth'].get(): - self.window['mute'].update(False, disabled=False) + self.window["auto_detect_auto_stop"].update(disabled=True) + self.window["auto_stop_frequency"].update(disabled=True) + self.window["auto_stop_duration"].update(disabled=True) + if self.window["stealth"].get(): + self.window["mute"].update(False, disabled=False) else: - self.window['mute'].update(disabled=False) + self.window["mute"].update(disabled=False) def save(self, values): for key in values: @@ -156,62 +172,173 @@ def save(self, values): @staticmethod def row(key, input_element): input_element.Key = key - text = Sg.Text(' ', size=(1, 1), key=f'{key}_text', font=(global_settings.font, 12)) + text = Sg.Text( + " ", size=(1, 1), key=f"{key}_text", font=(global_settings.font, 12) + ) if isinstance(input_element, Sg.Slider): text.Pad = ((5, 0), (20, 0)) - layout = [ - [text, input_element] - ] - return Sg.Column(layout, key=f'{key}_column', element_justification='left') + layout = [[text, input_element]] + return Sg.Column(layout, key=f"{key}_column", element_justification="left") def create_window(self): layout = [ - [Sg.Text(' ', size=(1, 1), key='settings_title', font=(global_settings.font, 20))], - [Sg.Text(' ', size=(1, 1), key='settings_help', font=(global_settings.font, 16))], - [self.row('language_name', Sg.Combo(self.language_names, enable_events=True, readonly=True, default_value=global_settings.language_name, font=(global_settings.font, 12)))], - [self.row('font', Sg.Combo(global_settings.fonts, enable_events=True, readonly=True, default_value=global_settings.font, font=(global_settings.font, 12)))], - [self.row('autostart', Sg.Checkbox('', default=global_settings.autostart))], - [self.row('branch', Sg.Combo(['stable', 'beta'], readonly=True, default_value=global_settings.branch, font=(global_settings.font, 12)))], - [self.row('debug', Sg.Checkbox('', default=global_settings.debug))], - [self.row('mode_name', Sg.Combo(self.mode_names, readonly=True, default_value=self.config.mode_name, font=(global_settings.font, 12)))], - [self.row('character', Sg.Combo(self.characters, readonly=True, default_value=self.config.character, font=(global_settings.font, 12)))], - [self.row('duration', Sg.Slider(range=(1, 15), orientation='horizontal', default_value=self.config.duration, font=(global_settings.font, 12)))], - [self.row('auto_stop', Sg.Checkbox('', default=self.config.auto_stop))], - [self.row('auto_detect_auto_stop', Sg.Checkbox('', default=self.config.auto_detect_auto_stop))], - [self.row('auto_stop_frequency', - Sg.Slider(range=(1, 20), resolution=.5, orientation='horizontal', default_value=self.config.auto_stop_frequency, font=(global_settings.font, 12)))], - [self.row('auto_stop_duration', - Sg.Slider(range=(5, 240), resolution=5, orientation='horizontal', default_value=self.config.auto_stop_duration, font=(global_settings.font, 12)))], + [ + Sg.Text( + " ", + size=(1, 1), + key="settings_title", + font=(global_settings.font, 20), + ) + ], + [ + Sg.Text( + " ", + size=(1, 1), + key="settings_help", + font=(global_settings.font, 16), + ) + ], + [ + self.row( + "language_name", + Sg.Combo( + self.language_names, + enable_events=True, + readonly=True, + default_value=global_settings.language_name, + font=(global_settings.font, 12), + ), + ) + ], + [ + self.row( + "font", + Sg.Combo( + global_settings.fonts, + enable_events=True, + readonly=True, + default_value=global_settings.font, + font=(global_settings.font, 12), + ), + ) + ], + [self.row("autostart", Sg.Checkbox("", default=global_settings.autostart))], + [ + self.row( + "branch", + Sg.Combo( + ["stable", "beta"], + readonly=True, + default_value=global_settings.branch, + font=(global_settings.font, 12), + ), + ) + ], + [self.row("debug", Sg.Checkbox("", default=global_settings.debug))], + [ + self.row( + "mode_name", + Sg.Combo( + self.mode_names, + readonly=True, + default_value=self.config.mode_name, + font=(global_settings.font, 12), + ), + ) + ], + [ + self.row( + "character", + Sg.Combo( + self.characters, + readonly=True, + default_value=self.config.character, + font=(global_settings.font, 12), + ), + ) + ], + [ + self.row( + "duration", + Sg.Slider( + range=(1, 15), + orientation="horizontal", + default_value=self.config.duration, + font=(global_settings.font, 12), + ), + ) + ], + [self.row("auto_stop", Sg.Checkbox("", default=self.config.auto_stop))], + [ + self.row( + "auto_detect_auto_stop", + Sg.Checkbox("", default=self.config.auto_detect_auto_stop), + ) + ], + [ + self.row( + "auto_stop_frequency", + Sg.Slider( + range=(1, 20), + resolution=0.5, + orientation="horizontal", + default_value=self.config.auto_stop_frequency, + font=(global_settings.font, 12), + ), + ) + ], + [ + self.row( + "auto_stop_duration", + Sg.Slider( + range=(5, 240), + resolution=5, + orientation="horizontal", + default_value=self.config.auto_stop_duration, + font=(global_settings.font, 12), + ), + ) + ], # [self.row('bots', Sg.Combo(list(range(1, 8)), readonly=True, default_value=self.config.bots, font=(global_settings.font, 12)))], - [self.row('stealth', Sg.Checkbox('', default=self.config.stealth))], - [self.row('mute', Sg.Checkbox('', default=self.config.mute))], - [Sg.Button('', font=(global_settings.font, 12), key='hotkey_settings')], - [Sg.Button('', font=(global_settings.font, 12), key='save'), Sg.Button('', font=(global_settings.font, 12), key='back')] + [self.row("stealth", Sg.Checkbox("", default=self.config.stealth))], + [self.row("mute", Sg.Checkbox("", default=self.config.mute))], + [Sg.Button("", font=(global_settings.font, 12), key="hotkey_settings")], + [ + Sg.Button("", font=(global_settings.font, 12), key="save"), + Sg.Button("", font=(global_settings.font, 12), key="back"), + ], ] - window = Sg.Window('', layout, size=(800, 800), keep_on_top=True, icon=global_settings.icon, metadata='settings_window_title').Finalize() + window = Sg.Window( + "", + layout, + size=(800, 800), + keep_on_top=True, + icon=global_settings.icon, + metadata="settings_window_title", + ).Finalize() global_settings.update_window(window) return window def start_loop(self): while True: event, values = self.window.read(timeout=50) - if event in (Sg.WINDOW_CLOSED, 'back'): + if event in (Sg.WINDOW_CLOSED, "back"): break - elif event == 'save': + elif event == "save": self.save(values) break - elif event == 'hotkey_settings': + elif event == "hotkey_settings": hotkeys = GUIHotkeys() self.window.disable() self.window.hide() hotkeys.start_loop() self.window.enable() self.window.un_hide() - elif event == 'language_name': - global_settings.language_name = values['language_name'] + elif event == "language_name": + global_settings.language_name = values["language_name"] global_settings.save() - elif event == 'font': - global_settings.font = values['font'] + elif event == "font": + global_settings.font = values["font"] global_settings.save() self.update_layout() global_settings.update_window(self.window) diff --git a/direct_input.py b/direct_input.py index 7c154e4..b58366c 100644 --- a/direct_input.py +++ b/direct_input.py @@ -28,7 +28,7 @@ def PressKey(self, hexCode): def ReleaseKey(self, hexCode): win32gui.SendMessage(self.brawlhalla.window, win32con.WM_KEYUP, hexCode) - def press_key(self, *key_codes, delay=.05): + def press_key(self, *key_codes, delay=0.05): for key_code in key_codes: self.PressKey(key_code) sleep(delay) @@ -38,7 +38,7 @@ def press_key(self, *key_codes, delay=.05): def release_keys(self): for key in self.keys: self.ReleaseKey(self.keys[key]) - sleep(.05) + sleep(0.05) def up(self, *args, **kwargs): self.press_key(self.hotkeys.up, *args, **kwargs) @@ -74,21 +74,31 @@ def enter(self, *args, **kwargs): self.press_key(self.hotkeys.enter, *args, **kwargs) def fight(self): - move_keys = [self.hotkeys.left, self.hotkeys.up, self.hotkeys.down, self.hotkeys.right] - fight_keys = [self.hotkeys.throw, self.hotkeys.quick, self.hotkeys.heavy, self.hotkeys.dodge] - self.press_key(choice(move_keys), choice(fight_keys), delay=.2) + move_keys = [ + self.hotkeys.left, + self.hotkeys.up, + self.hotkeys.down, + self.hotkeys.right, + ] + fight_keys = [ + self.hotkeys.throw, + self.hotkeys.quick, + self.hotkeys.heavy, + self.hotkeys.dodge, + ] + self.press_key(choice(move_keys), choice(fight_keys), delay=0.2) class Hotkeys: def __init__(self, hotkeys): - self.up = hotkeys.get('up', win32con.VK_UP) - self.down = hotkeys.get('down', win32con.VK_DOWN) - self.left = hotkeys.get('left', win32con.VK_LEFT) - self.right = hotkeys.get('right', win32con.VK_RIGHT) - self.throw = hotkeys.get('throw', 0x48) - self.quick = hotkeys.get('quick', 0x4A) - self.heavy = hotkeys.get('heavy', 0x4B) - self.dodge = hotkeys.get('dodge', 0x4C) + self.up = hotkeys.get("up", win32con.VK_UP) + self.down = hotkeys.get("down", win32con.VK_DOWN) + self.left = hotkeys.get("left", win32con.VK_LEFT) + self.right = hotkeys.get("right", win32con.VK_RIGHT) + self.throw = hotkeys.get("throw", 0x48) + self.quick = hotkeys.get("quick", 0x4A) + self.heavy = hotkeys.get("heavy", 0x4B) + self.dodge = hotkeys.get("dodge", 0x4C) self.rbr = 0xDD self.esc = win32con.VK_ESCAPE self.enter = win32con.VK_RETURN @@ -96,7 +106,7 @@ def __init__(self, hotkeys): @classmethod def load(cls): try: - res = json.load(global_settings.hotkeys_location.open('r')) + res = json.load(global_settings.hotkeys_location.open("r")) return cls(res) except FileNotFoundError: return cls({}) @@ -104,9 +114,9 @@ def load(cls): def save(self): try: global_settings.hotkeys_location.parent.mkdir(parents=True, exist_ok=True) - json.dump(vars(self), global_settings.hotkeys_location.open('w+')) + json.dump(vars(self), global_settings.hotkeys_location.open("w+")) except Exception as e: - logger.error('cant_save_hotkeys', e) + logger.error("cant_save_hotkeys", e) class GUIHotkeys: @@ -132,12 +142,33 @@ def unhook_keyboard(self): @staticmethod def text_column(*keys): return Sg.Column( - [[Sg.Text(' ', size=(1, 1), key=f'{key}_text', font=(global_settings.font, 12))] for key in keys] + [ + [ + Sg.Text( + " ", + size=(1, 1), + key=f"{key}_text", + font=(global_settings.font, 12), + ) + ] + for key in keys + ] ) def input_column(self, *keys): return Sg.Column( - [[Sg.Input(size=(3, 1), key=key, enable_events=True, font=(global_settings.font, '12'), default_text=self.vk_to_char(vars(self.hotkeys)[key]))] for key in keys] + [ + [ + Sg.Input( + size=(3, 1), + key=key, + enable_events=True, + font=(global_settings.font, "12"), + default_text=self.vk_to_char(vars(self.hotkeys)[key]), + ) + ] + for key in keys + ] ) def combined_column(self, *keys): @@ -145,14 +176,31 @@ def combined_column(self, *keys): def create_window(self): layout = [ - [Sg.Text(' ', size=(1, 1), key='hotkeys_title', font=(global_settings.font, 20))], [ - *self.combined_column('up', 'left', 'throw', 'quick'), - *self.combined_column('down', 'right', 'dodge', 'heavy') + Sg.Text( + " ", + size=(1, 1), + key="hotkeys_title", + font=(global_settings.font, 20), + ) + ], + [ + *self.combined_column("up", "left", "throw", "quick"), + *self.combined_column("down", "right", "dodge", "heavy"), + ], + [ + Sg.Button("", key="save", font=(global_settings.font, 12)), + Sg.Button("", key="back", font=(global_settings.font, 12)), ], - [Sg.Button('', key='save', font=(global_settings.font, 12)), Sg.Button('', key='back', font=(global_settings.font, 12))], ] - window = Sg.Window('', layout, size=(600, 350), keep_on_top=True, icon=global_settings.icon, metadata='hotkeys_window_title').Finalize() + window = Sg.Window( + "", + layout, + size=(600, 350), + keep_on_top=True, + icon=global_settings.icon, + metadata="hotkeys_window_title", + ).Finalize() global_settings.update_window(window) return window @@ -163,10 +211,10 @@ def vsc_to_vk(code): @staticmethod def vk_to_char(code): direct = { - win32con.VK_LEFT: '◁', - win32con.VK_UP: '△', - win32con.VK_RIGHT: '▷', - win32con.VK_DOWN: '▽', + win32con.VK_LEFT: "◁", + win32con.VK_UP: "△", + win32con.VK_RIGHT: "▷", + win32con.VK_DOWN: "▽", } if code in direct: return direct[code] @@ -182,13 +230,22 @@ def start_loop(self): while True: event, values = self.window.read(timeout=50) if self.last_keyboard_event: - if event in ['up', 'down', 'left', 'right', 'quick', 'heavy', 'dodge', 'throw']: + if event in [ + "up", + "down", + "left", + "right", + "quick", + "heavy", + "dodge", + "throw", + ]: vk = self.vsc_to_vk(self.last_keyboard_event.scan_code) self.window[event].Update(self.vk_to_char(vk)) self.converter[event] = vk - if event in (Sg.WINDOW_CLOSED, 'back'): + if event in (Sg.WINDOW_CLOSED, "back"): break - elif event == 'save': + elif event == "save": self.save() break global_settings.update_window(self.window) diff --git a/font_loader.py b/font_loader.py index b9c9bca..385739f 100644 --- a/font_loader.py +++ b/font_loader.py @@ -7,18 +7,20 @@ def get_font_name(file): font = TTFont(file) - name = '' - for record in font['name'].names: - if b'\x00' in record.string: - name_str = record.string.decode('utf-16-be') + name = "" + for record in font["name"].names: + if b"\x00" in record.string: + name_str = record.string.decode("utf-16-be") else: - name_str = record.string.decode('utf-8') + name_str = record.string.decode("utf-8") if record.nameID == FONT_SPECIFIER_NAME_ID and not name: return name_str - return '' + return "" def load_font(fontpath): flags = 0x10 | 0x20 - num_fonts_added = ctypes.windll.gdi32.AddFontResourceExW(ctypes.byref(ctypes.create_unicode_buffer(fontpath)), flags, 0) + num_fonts_added = ctypes.windll.gdi32.AddFontResourceExW( + ctypes.byref(ctypes.create_unicode_buffer(fontpath)), flags, 0 + ) return bool(num_fonts_added) diff --git a/gui.pyw b/gui.pyw index c854330..1147840 100644 --- a/gui.pyw +++ b/gui.pyw @@ -3,19 +3,17 @@ import uuid from bot import * + class Handler(logging.StreamHandler): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def emit(self, record): - colors = { - logging.DEBUG: 'grey10', - logging.INFO: 'white' - } + colors = {logging.DEBUG: "grey10", logging.INFO: "white"} if isinstance(record.msg, str) and record.msg in global_settings.messages: record.msg = global_settings.messages[record.msg] - Sg.cprint(self.format(record), text_color=colors.get(record.levelno, 'red')) + Sg.cprint(self.format(record), text_color=colors.get(record.levelno, "red")) class GUI: @@ -40,53 +38,104 @@ class GUI: @staticmethod def create_window(): - Sg.theme('DarkGrey10') + Sg.theme("DarkGrey10") layout = [ - [Sg.Text(' ', size=(1, 1), key='title', font=(global_settings.font, 20))], - [Sg.Text(' ', size=(1, 1), key='version', font=(global_settings.font, 13))], - [Sg.Text(' ', size=(1, 1), key='press_start', font=(global_settings.font, 14), metadata=0)], - [Sg.Multiline(size=(90, 10), key='log', disabled=True, autoscroll=True, - write_only=True, reroute_cprint=True, reroute_stderr=global_settings.compiled, font=(global_settings.font, 12), text_color='red')] + [Sg.Text(" ", size=(1, 1), key="title", font=(global_settings.font, 20))], + [Sg.Text(" ", size=(1, 1), key="version", font=(global_settings.font, 13))], + [ + Sg.Text( + " ", + size=(1, 1), + key="press_start", + font=(global_settings.font, 14), + metadata=0, + ) + ], + [ + Sg.Multiline( + size=(90, 10), + key="log", + disabled=True, + autoscroll=True, + write_only=True, + reroute_cprint=True, + reroute_stderr=global_settings.compiled, + font=(global_settings.font, 12), + text_color="red", + ) + ], ] buttons = [ [ - Sg.Button('', key='toggle', font=(global_settings.font, 12), metadata=0), - Sg.Button('', key='instructions', font=(global_settings.font, 12)), - Sg.Button('', key='settings', font=(global_settings.font, 12)), - Sg.Button('', key='exit', font=(global_settings.font, 12)), + Sg.Button( + "", key="toggle", font=(global_settings.font, 12), metadata=0 + ), + Sg.Button("", key="instructions", font=(global_settings.font, 12)), + Sg.Button("", key="settings", font=(global_settings.font, 12)), + Sg.Button("", key="exit", font=(global_settings.font, 12)), ], [ - Sg.Button('', key='delayed_stop', font=(global_settings.font, 12), metadata=0, visible=False), - Sg.Button('', key='take_screenshot', font=(global_settings.font, 12)), + Sg.Button( + "", + key="delayed_stop", + font=(global_settings.font, 12), + metadata=0, + visible=False, + ), + Sg.Button("", key="take_screenshot", font=(global_settings.font, 12)), ], ] if not global_settings.compiled: - buttons[0].append(Sg.Button('', key='test', font=(global_settings.font, 12))) + buttons[0].append( + Sg.Button("", key="test", font=(global_settings.font, 12)) + ) layout += buttons - layout.append([Sg.Text(' ', font='Any 50')]) + layout.append([Sg.Text(" ", font="Any 50")]) if global_settings.new_version: global_settings.new_version.cleanup() update_column_layout = [ - [Sg.Text(' ', size=(1, 1), key='update_available_title', font=(global_settings.font, 25))], - [Sg.Text(' ', size=(1, 1), key='update_available_version', font=(global_settings.font, 15))], - [Sg.Button('', key='update_available_button', font=(global_settings.font, 12))], - [Sg.Text(' ', font='Any 50')] + [ + Sg.Text( + " ", + size=(1, 1), + key="update_available_title", + font=(global_settings.font, 25), + ) + ], + [ + Sg.Text( + " ", + size=(1, 1), + key="update_available_version", + font=(global_settings.font, 15), + ) + ], + [ + Sg.Button( + "", + key="update_available_button", + font=(global_settings.font, 12), + ) + ], + [Sg.Text(" ", font="Any 50")], ] layout.append([Sg.Column(update_column_layout)]) - window = Sg.Window('', layout, icon=global_settings.icon, metadata='main_window_title').Finalize() + window = Sg.Window( + "", layout, icon=global_settings.icon, metadata="main_window_title" + ).Finalize() global_settings.update_window(window) return window def toggle_bot(self): if self.bot_thread and self.bot_thread.is_alive(): - logger.info('stop_bot') - self.queue.put_nowait('STOP') + logger.info("stop_bot") + self.queue.put_nowait("STOP") else: - logger.info('start_bot') + logger.info("start_bot") config = Config.load() logger.debug(global_settings) logger.debug(config) @@ -104,43 +153,55 @@ class GUI: def delayed_stop(self): if self.bot_thread and self.bot_thread.is_alive(): - if self.window['delayed_stop'].metadata: - logger.info('cancel_stop') + if self.window["delayed_stop"].metadata: + logger.info("cancel_stop") self.clear_queue() else: - logger.info('delayed_stop') - self.queue.put_nowait('DELAYED_STOP') - self.window['delayed_stop'].metadata = not self.window['delayed_stop'].metadata + logger.info("delayed_stop") + self.queue.put_nowait("DELAYED_STOP") + self.window["delayed_stop"].metadata = not self.window[ + "delayed_stop" + ].metadata def refresh_buttons(self): if self.downloading_new_version: - self.window['toggle'].update(disabled=True) - self.window['delayed_stop'].update(visible=False) - self.window['instructions'].update(disabled=True) - self.window['settings'].update(disabled=True) - self.window['update_available_button'].update(disabled=True) - self.window['exit'].update(disabled=True) + self.window["toggle"].update(disabled=True) + self.window["delayed_stop"].update(visible=False) + self.window["instructions"].update(disabled=True) + self.window["settings"].update(disabled=True) + self.window["update_available_button"].update(disabled=True) + self.window["exit"].update(disabled=True) elif self.bot_thread and self.bot_thread.is_alive(): - self.window['toggle'].metadata = 1 - self.window['press_start'].metadata = 1 - self.window['delayed_stop'].Update(visible=True) - self.window['instructions'].Update(disabled=True) - self.window['settings'].Update(disabled=True) + self.window["toggle"].metadata = 1 + self.window["press_start"].metadata = 1 + self.window["delayed_stop"].Update(visible=True) + self.window["instructions"].Update(disabled=True) + self.window["settings"].Update(disabled=True) else: - self.window['toggle'].metadata = 0 - self.window['press_start'].metadata = 0 - self.window['delayed_stop'].metadata = 0 - self.window['delayed_stop'].Update(visible=False) - self.window['instructions'].Update(disabled=False) - self.window['settings'].Update(disabled=False) + self.window["toggle"].metadata = 0 + self.window["press_start"].metadata = 0 + self.window["delayed_stop"].metadata = 0 + self.window["delayed_stop"].Update(visible=False) + self.window["instructions"].Update(disabled=False) + self.window["settings"].Update(disabled=False) self.clear_queue() @staticmethod def display_instructions(): - contents = global_settings.language.LAYOUT_MAPPING.get('instructions_contents', - global_settings.get_language('English').LAYOUT_MAPPING.get('instructions_contents', 'Should be stuff here')) - Sg.popup(*contents, font=(global_settings.font, 13), - title=global_settings.language.LAYOUT_MAPPING.get('instructions_window_title', 'Instructions'), icon=global_settings.icon) + contents = global_settings.language.LAYOUT_MAPPING.get( + "instructions_contents", + global_settings.get_language("English").LAYOUT_MAPPING.get( + "instructions_contents", "Should be stuff here" + ), + ) + Sg.popup( + *contents, + font=(global_settings.font, 13), + title=global_settings.language.LAYOUT_MAPPING.get( + "instructions_window_title", "Instructions" + ), + icon=global_settings.icon, + ) def configure(self): settings = GUIConfig() @@ -156,7 +217,7 @@ class GUI: if self.bot_thread and self.bot_thread.is_alive(): self.last_window_check = time() elif time() - self.last_window_check > 300: - logger.debug('autostart_check') + logger.debug("autostart_check") bh = BrawlhallaProcess.find() if not bh: self.toggle_bot() @@ -165,35 +226,42 @@ class GUI: def main_loop(self): while True: event, values = self.window.Read(timeout=50) - if event in (Sg.WINDOW_CLOSED, 'exit'): + if event in (Sg.WINDOW_CLOSED, "exit"): break - elif event == 'toggle': + elif event == "toggle": self.toggle_bot() - elif event == 'delayed_stop': + elif event == "delayed_stop": self.delayed_stop() - elif event == 'instructions': + elif event == "instructions": self.display_instructions() - elif event == 'settings': + elif event == "settings": self.configure() - elif event == 'test': - logger.info('test') - logger.debug('test') - elif event == 'take_screenshot': + elif event == "test": + logger.info("test") + logger.debug("test") + elif event == "take_screenshot": bh = BrawlhallaProcess.find() if bh is None: - logger.error('bh_not_running') + logger.error("bh_not_running") else: - screenshot_name = f'screenshot-{uuid.uuid4()}.png' - screenshot_path = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / screenshot_name + screenshot_name = f"screenshot-{uuid.uuid4()}.png" + screenshot_path = ( + Path(os.getenv("LOCALAPPDATA")) / "BHBot" / screenshot_name + ) bh.make_screenshot().save(screenshot_path) - logger.info('took_screenshot', str(screenshot_path)) + logger.info("took_screenshot", str(screenshot_path)) - elif event == 'update_available_button': + elif event == "update_available_button": global_settings.new_version.download(background=True) self.downloading_new_version = True elif not self.downloading_new_version: self.autostart() - if global_settings.compiled and global_settings.new_version and self.downloading_new_version and global_settings.new_version.is_downloaded(): + if ( + global_settings.compiled + and global_settings.new_version + and self.downloading_new_version + and global_settings.new_version.is_downloaded() + ): global_settings.new_version.extract_restart() self.refresh_buttons() global_settings.update_window(self.window) @@ -201,7 +269,7 @@ class GUI: self.window.close() -if __name__ == '__main__': +if __name__ == "__main__": Singleton() gui = GUI() gui.main_loop() diff --git a/languages/__pycache__/english.cpython-311.pyc b/languages/__pycache__/english.cpython-311.pyc index 2868c6c29910efef0dba00ae17818c691ae37aaa..dd4b9b3d64a30e6438593304e7c2f4b85bf32ac6 100644 GIT binary patch delta 1000 zcmXZaOHWfl6bJCRg#m=Vd)rd!yOg$oC~pu&D6fK8&=eFXARwqHVj@I$Wy#K)G$viL zb8Sdm_!(Tx%DChs{P%90In%p1xxYDc@5!7wzt(@QJHI;|txP^&uR}+FGC!QpQbwV= zmkLByXX4p`TzU5N;QZw9!`Y|u!rQaL`F=TH$e*57zol}OkIjss!7z-!ZRSM_8ViQk zcGDQF1;N!Tni#hWM*_{H7REb7cn-Hnt)ywRk?f>dI7m*$ox+uai{xh9E6gD=QWCy3 zcxcVbxZ|?&xz;3{G5Bc7WZY-%Tx|<;5Pn(-Fz&Ki5hS#$bP*xKZN}R!=g~n3YeWc9 z#sealMT`_@JR+j~NRX0@$3%P{DN-k^@M!G|(j-PP%Xql9i5#Jualdtp9zrkUrtnYW z4yljvP^}kt3HKOxTkYs449Jf}U>Jj>A$4?L8pEU!#y!H@g;CO&tnatpV4QSc6+6KM z;ejk>mGO`;Nq-!`Bhr+?ctRxem^R3HX?ds#%xKR_M@4K3bK3LLZYzcb&4Q#)nB!QK zuE;Ca=hksa?G>IR9_!7^($vf++C^#A_7%<5I-hEmBs*%0SkqjWG_4Ih*L)%AysThD zb5qh?u>#oAE-OoJYwk#TtPFNFU)H&&xlioFf#$1v_E7Vvo_(!(EE&Iw;+^Ju$uO8J z+60!RsKSX(7H#VOx3FTP-cnaCoG4Xqx)zK`HJi!rP8mof?KX5W9t5+6mJOtp<*=lr z)05~?lrxb4Gg3tZ{mOC>;?l`U3@eVwxsbcfl7Vq$b;mLt9KHUX$u%@3&~A4ns{gt} K#!}Vall~7S0SV*) delta 1024 zcmXZaOH&g;5CGtwzyLzt**qX2PeKR*G`s}y0TBWLMN~i@Arc^hm}nJHg|~3zxK;6x zBR9_$RUZ5W9;ZCzrd9rd{soq^J=sIoe$%ttH9h@(^T($3tHsg@^7-}eZ^<+G&H5_F zaY3<&$E$T+C;dGM_GnE5-AR(pl|GELlBo2f!A_Sffk3>cG}4 zbDI9Ndh1@~#w)e~Hw#My+esb3{UWe{cSxP2IqV{tN%LqSS%F)HEr~Xg9k^3y<6?4M zxHiy1Yfj*n>*{5_O;|(dq9qNuOE28)32hABwBiA7(>u{i@EPe<>?ZU8@4C*SpAb;! zB?N(cgm(Z#q%iPa5lmx*6a^j<;Vi~TeNg4W`e%%jfZ_n~Kz#?3ghAkL{S;G#A>f*D z&*3m>1bBCS2=5Z^0k`WuOcO@sN5V6KW2AB8=&m?UkS2jUgtH&-lcr>Sw|;`tqz6W^ z3eFH7%3^vR9}ynYA4l;CX_f(xh-e1q7&#*?4>f~NmFJ~{A~cH&%2{c<9>PV%B}tdi zrZFd7l~=6Jt>Ut=S2&`$qBcL1re;1@UX?c5{z7rB$(M@jlKy%QHxxG|HGLc3C~iqw zudBGNxFcz=>K@!x&Ks88Q!Ge2^aQ?D+;6g|ct9M%L&c+Jwxsy3nLSoKl?>km@q=Pn zGJs$S4k8RtG(trsmrcg~@8XJydP`lovf^6pcmEX+d{vQ)h6XO5? diff --git a/languages/__pycache__/russian.cpython-311.pyc b/languages/__pycache__/russian.cpython-311.pyc index c674d7cbda2935125a4aa18ba6fedec6ceecb187..c3e832953a78f86deed701699c2afd07ffa46faf 100644 GIT binary patch delta 1044 zcmYk)Nl#i)6bJBopI)RgBZ?>@gSHOUibb5TRU80E)M^v^IygW}t)iewn>0apE=fol zzd_fHF1zpcE?xE`oR84+&SlZJxOu;G4k7ov|NS+;zvlLzHd`~p=f|BdA4&YOf18M< z=cn|Ya;{X?*9(Wo`a-FAc2NAPXXk@`qy73q@$m8_cY0CQQ-zE4<$<0|CyN)g-xK|n zKTV851Lyp~(s^Zu#te6Be;d8GUJ%ABn3%9iTLhX&W+pnNKL?LU&7>o+kgRptNOmUd z(lHDUl9LJdgL&h@ybYqkV*(dd+nK1?Zoc2{NqZmosMNtki)@X__@?x1K_}JxOgN;o z2f9cBW5P24-J~EBmYY=w5yA$RAwuY3qD^||pqKQRi7pvff+wUtCc0&C3;Ht^omn+J0-LJ0g~r{cU|abW z+9It%*iqg!j9=xo;vN!HGYxN)bu`{325-6A|2r&TSVrKz8u);AR{gNActGrcL&YPc zt2zuh#eAI~6+ao2?m;Lhf3CNW6~CzV5S%D4o?#&6*9G)Iz(uM4Y{M&3kpUtFa(`Og)lkE9Lu5kN( zpuPB`QV5h8hVjeRbXkS+DZ^|2F8AO0A+4XN5Ynz3F;udugltlQ93HT$S$nXt>?Ju^ zP9dGj)eRTREu>fZwjji|acI()@5qFQT169HAsyGB?>3dQ4K+ON6S79t_NwHX@~xwm zYjr}pl)D9fR=qXh>p%mmQApeMBAOTh3-f4Z1cj_q{t1LwVIiAUa2gR-RLGDjhSw2e zwg?$ip#)l4g3mS~1FAWJc2%v1ukC5)0AQFs6 z{Kr*1X7yUTs7a#FQV~fZJ<1zLKQ{+x^yUZDKg!zfG671WViD4qBx@qvs9psu3}DG-(R2edbkG*y0=KCtZiP}w2n*0!E!4 players and long games', - 'stealth_column': 'Will hide the game window after Brawlhalla startsn\n' - 'Also automatically sets following option to True', - 'mute_column': 'If bot should disable sounds/music ingame after getting into menu', + "settings_help": "\n :) \n", + "language_name_column": "Choose a language (automatically updates)", + "font_column": "Choose a font (automatically updates)", + "autostart_column": "Automatically start the bot if the program is running and you are not playing Brawlhalla\n" + "Checks every 5 minutes", + "branch_column": "Beta may have experimental and unstable updates", + "debug_column": "Will print A LOT of useless debug info into the output", + "mode_name_column": "Choose a mode bot will follow while choosing characters/duration", + "character_column": "Choose a character (disabled for some modes)", + "duration_column": "Choose a duration\n" + "For some modes this is max duration that the bot will use", + "auto_stop_column": "Brawlhalla imposes xp/gold limit which is triggered if you play too much\n" + "This option enables automatically stopping the bot to reset said limit\n" + "Otherwise, bot will continue to work but will get close to no xp/gold", + "auto_detect_auto_stop_column": "Enables automatic detection of xp limit from xp earnings after the game\n" + "Disables following 2 options\n" + "!!Can be unstable for character levels higher than 60!!", + "auto_stop_frequency_column": "How often will bot stop to reset xp/gold limit\n" + "From experience, 4.5-5 hours is average time needed to get to the limit", + "auto_stop_duration_column": "How much time bot will wait to reset xp/gold limit\n" + "From experience, 30 minutes is enough (25 is not afaik)", + "bots_column": "How many bots (not including player) will there be in a game\n" + "Hits and goals do not actually affect xp/gold gain, so 2 is optimal value\n" + "Brawlhalla is known to work extremely unstable with >4 players and long games", + "stealth_column": "Will hide the game window after Brawlhalla startsn\n" + "Also automatically sets following option to True", + "mute_column": "If bot should disable sounds/music ingame after getting into menu", } MESSAGES = { - 'waiting_for_bh_window': 'Waiting for brawlhalla window', - 'found_bh': 'Found Brawlhalla', - 'not_windowed_mode': 'Not windowed mode, restarting', - 'stealth_mode': 'Entering stealth mode', - 'initialized': 'Initialized', - 'initial_on_exit': '\nSession Stats:\n\nTime running: %s\nGames completed: %s\nCrashes/restarts: %s\nXP earned: %s\n', - 'on_exit_has_rewards': 'Gold earned: %s\n', - 'on_exit_no_rewards': 'Gold earned: %s (+level-up rewards)\n', - 'started_fighting': 'Started fighting', - 'ended_fighting': 'Ended fighting', - 'reinitializing': 'Reinitializing', - 'queued_recalc': 'Restarting for queued character recalculation', - 'not_in_menu': 'Not in menu', - 'collecting_bonus': 'Collecting daily bonus', - 'accepting_event_popup': 'Accepting event bonus', - 'offline': 'Offline, trying to connect', - 'item_not_selected': '%s not selected', - 'sorting_by_date': 'Sorting by date', - 'collecting_character_data': 'Collecting character data', - 'skip_lvl_valid': 'Skipping level validation', - 'calc_xp': 'Calculated xp: %s', - 'pixel_xp': 'Pixelsearch xp: %s', - 'xp_discrep': 'Discrepancy in xp earnings detected\nScheduling xp reset and character recalculation', - 'waiting_for_loading': 'Waiting for loading', - 'loading': 'Loading', - 'pick_char': 'Picking %s', - 'setting_dur': 'Setting duration to %s', - 'wait_for_xp_reset': 'Waiting %s minutes for xp reset', - 'wait_remaining': 'Waiting for xp reset: %s seconds left', - 'creating_lobby': 'Creating lobby', - 'setting_lobby': 'Setting lobby up', - 'starting_game': 'Starting game', - 'loaded': 'Loaded', - 'disconnected': 'Disconnected from the game, restarting', - 'return_to_lobby': 'Going back to lobby', - 'no_modes': 'No modes found. Program will not function with empty modes directory.', - 'old_config': 'Config was made for older bot version. Please, make sure everything is up to date.', - 'level_error': 'Error recognizing level\nBot only works with levels 1-100', - 'cant_save_hotkeys': 'Could not save hotkeys. Exception: %s', - 'cant_save_config': 'Could not save config. Exception: %s', - 'start_bot': 'Starting the bot', - 'stop_bot': 'Stopping the bot', - 'test': 'Test', - 'downloading': 'Downloading: %s%%', - 'downloaded': 'Download completed in %s', - 'resize': 'Game window size: %sx%s\nGame client area size: %sx%s\nScreen size: %sx%s', - 'move_offscreen': 'Moving game window off-screen', - 'update_total_stats': 'Updating all-time stats', - 'total_stats': '\nAll-Time Stats:\n\nGames completed: %s\nXP earned: %s\nGold earned: %s\nTime running: %s\n', - 'autostart_check': 'Checking autostart conditions', - 'rotation_error': 'Error getting character rotation:\n%s', - 'muting': 'Muting sounds', - 'reconnected': 'Reconnected', - 'settings': 'Current settings:\n' - '------------------------\n' - 'App Name: {0.APP_NAME}\n' - 'App Version: {0.APP_VERSION}\n' - 'Compiled: {0.compiled}\n' - 'Branch: {0.branch}\n' - 'New version: {0.new_version}\n' - 'Language: {0.language_name}\n' - 'Font: {0.font}\n' - 'Autostart: {0.autostart}\n' - 'Debug: {0.debug}\n' - '------------------------\n', - 'config': 'Current config:\n' - '------------------------\n' - 'Mode: {0.mode_name}\n' - 'Character: {0.character}\n' - 'Duration: {0.duration}\n' - 'Auto-stop: {0.auto_stop}\n' - 'Auto detect auto-stop: {0.auto_detect_auto_stop}\n' - 'Auto-stop frequency: {0.auto_stop_frequency}\n' - 'Auto-stop duration: {0.auto_stop_duration}\n' - 'Bots: {0.bots}\n' - 'Stealth: {0.stealth}\n' - 'Mute: {0.mute}\n' - '------------------------\n', - 'resized_warning': 'Game window was resized. Please read the instructions. Bot will now restart', - 'danger_zone_warning': 'Bot is in danger zone (Mallhalla or Battle Pass). Restarting', - 'invalid_state_warning': 'Bot is in invalid state. Something went wrong. Restarting', - 'no_steam_exe': 'Steam client exe cannot be found. Please launch Steam and try to run bot again', - 'delayed_stop': 'Bot will stop after finishing current game or before beginning a new one', - 'cancel_stop': 'All queued stops were cancelled', - 'menu_pixels_error': 'Error getting pixels for state detection. Don\'t worry, this just means that the default ones will be used:\n%s', - 'took_screenshot': 'Screenshot saved to %s', - 'bh_not_running': 'Brawlhalla is not running', + "waiting_for_bh_window": "Waiting for brawlhalla window", + "found_bh": "Found Brawlhalla", + "not_windowed_mode": "Not windowed mode, restarting", + "stealth_mode": "Entering stealth mode", + "initialized": "Initialized", + "initial_on_exit": "\nSession Stats:\n\nTime running: %s\nGames completed: %s\nCrashes/restarts: %s\nXP earned: %s\n", + "on_exit_has_rewards": "Gold earned: %s\n", + "on_exit_no_rewards": "Gold earned: %s (+level-up rewards)\n", + "started_fighting": "Started fighting", + "ended_fighting": "Ended fighting", + "reinitializing": "Reinitializing", + "queued_recalc": "Restarting for queued character recalculation", + "not_in_menu": "Not in menu", + "collecting_bonus": "Collecting daily bonus", + "accepting_event_popup": "Accepting event bonus", + "offline": "Offline, trying to connect", + "item_not_selected": "%s not selected", + "sorting_by_date": "Sorting by date", + "collecting_character_data": "Collecting character data", + "skip_lvl_valid": "Skipping level validation", + "calc_xp": "Calculated xp: %s", + "pixel_xp": "Pixelsearch xp: %s", + "xp_discrep": "Discrepancy in xp earnings detected\nScheduling xp reset and character recalculation", + "waiting_for_loading": "Waiting for loading", + "loading": "Loading", + "pick_char": "Picking %s", + "setting_dur": "Setting duration to %s", + "wait_for_xp_reset": "Waiting %s minutes for xp reset", + "wait_remaining": "Waiting for xp reset: %s seconds left", + "creating_lobby": "Creating lobby", + "setting_lobby": "Setting lobby up", + "starting_game": "Starting game", + "loaded": "Loaded", + "disconnected": "Disconnected from the game, restarting", + "return_to_lobby": "Going back to lobby", + "no_modes": "No modes found. Program will not function with empty modes directory.", + "old_config": "Config was made for older bot version. Please, make sure everything is up to date.", + "level_error": "Error recognizing level\nBot only works with levels 1-100", + "cant_save_hotkeys": "Could not save hotkeys. Exception: %s", + "cant_save_config": "Could not save config. Exception: %s", + "start_bot": "Starting the bot", + "stop_bot": "Stopping the bot", + "test": "Test", + "downloading": "Downloading: %s%%", + "downloaded": "Download completed in %s", + "resize": "Game window size: %sx%s\nGame client area size: %sx%s\nScreen size: %sx%s", + "move_offscreen": "Moving game window off-screen", + "update_total_stats": "Updating all-time stats", + "total_stats": "\nAll-Time Stats:\n\nGames completed: %s\nXP earned: %s\nGold earned: %s\nTime running: %s\n", + "autostart_check": "Checking autostart conditions", + "rotation_error": "Error getting character rotation:\n%s", + "muting": "Muting sounds", + "reconnected": "Reconnected", + "settings": "Current settings:\n" + "------------------------\n" + "App Name: {0.APP_NAME}\n" + "App Version: {0.APP_VERSION}\n" + "Compiled: {0.compiled}\n" + "Branch: {0.branch}\n" + "New version: {0.new_version}\n" + "Language: {0.language_name}\n" + "Font: {0.font}\n" + "Autostart: {0.autostart}\n" + "Debug: {0.debug}\n" + "------------------------\n", + "config": "Current config:\n" + "------------------------\n" + "Mode: {0.mode_name}\n" + "Character: {0.character}\n" + "Duration: {0.duration}\n" + "Auto-stop: {0.auto_stop}\n" + "Auto detect auto-stop: {0.auto_detect_auto_stop}\n" + "Auto-stop frequency: {0.auto_stop_frequency}\n" + "Auto-stop duration: {0.auto_stop_duration}\n" + "Bots: {0.bots}\n" + "Stealth: {0.stealth}\n" + "Mute: {0.mute}\n" + "------------------------\n", + "resized_warning": "Game window was resized. Please read the instructions. Bot will now restart", + "danger_zone_warning": "Bot is in danger zone (Mallhalla or Battle Pass). Restarting", + "invalid_state_warning": "Bot is in invalid state. Something went wrong. Restarting", + "no_steam_exe": "Steam client exe cannot be found. Please launch Steam and try to run bot again", + "delayed_stop": "Bot will stop after finishing current game or before beginning a new one", + "cancel_stop": "All queued stops were cancelled", + "menu_pixels_error": "Error getting pixels for state detection. Don't worry, this just means that the default ones will be used:\n%s", + "took_screenshot": "Screenshot saved to %s", + "bh_not_running": "Brawlhalla is not running", } diff --git a/languages/russian.py b/languages/russian.py index fa4a4eb..b3a6d27 100644 --- a/languages/russian.py +++ b/languages/russian.py @@ -1,197 +1,202 @@ -LANGUAGE = 'Русский' +LANGUAGE = "Русский" LAYOUT_MAPPING = { # Основное окно - 'main_window_title': 'BHBot', - 'title': 'BHBot от Sovamorco', - 'bannability_warning1': '!!!ОСТОРОЖНО: Согласно недавним жалобам', - 'bannability_warning2': 'бот может вызвать блокировку аккаунта', - 'bannability_warning3': 'так что лучше пока его не использовать', - 'version': 'Версия: {0.APP_VERSION}', - 'press_start': ['Нажми "Старт" чтобы начать работу бота', 'Нажми "Стоп" чтобы завершить работу бота'], - 'toggle': ['Старт', 'Стоп'], - 'delayed_stop': ['Отложенный Стоп', 'Отменить Стоп'], - 'settings': 'Настройки', - 'instructions': 'Инструкции', - 'exit': 'Выход', - 'test': 'Тест', - 'take_screenshot': 'Сделать снимок экрана', - 'update_available_title': 'Доступно обновление!', - 'update_available_version': 'Новая версия: {0.new_version.version}', - 'update_available_button': 'Обновить', + "main_window_title": "BHBot", + "title": "BHBot от Sovamorco", + "bannability_warning1": "!!!ОСТОРОЖНО: Согласно недавним жалобам", + "bannability_warning2": "бот может вызвать блокировку аккаунта", + "bannability_warning3": "так что лучше пока его не использовать", + "version": "Версия: {0.APP_VERSION}", + "press_start": [ + 'Нажми "Старт" чтобы начать работу бота', + 'Нажми "Стоп" чтобы завершить работу бота', + ], + "toggle": ["Старт", "Стоп"], + "delayed_stop": ["Отложенный Стоп", "Отменить Стоп"], + "settings": "Настройки", + "instructions": "Инструкции", + "exit": "Выход", + "test": "Тест", + "take_screenshot": "Сделать снимок экрана", + "update_available_title": "Доступно обновление!", + "update_available_version": "Новая версия: {0.new_version.version}", + "update_available_button": "Обновить", # Окно настроек - 'settings_window_title': 'Настройки', - 'settings_title': 'Настройки', - 'settings_help': 'Для дополнительной информации наведите на пункт настроек', - 'language_name_text': 'Язык: ', - 'font_text': 'Шрифт: ', - 'autostart_text': 'Автоматически запускать бота: ', - 'branch_text': 'Ветка обновлений: ', - 'debug_text': 'Дебаг-выводы: ', - 'mode_name_text': 'Режим: ', - 'character_text': 'Персонаж: ', - 'duration_text': 'Длительность: ', - 'auto_stop_text': 'Сброс лимита опыта: ', - 'auto_detect_auto_stop_text': 'Автоматическое определение лимита: ', - 'auto_stop_frequency_text': 'Частота сброса в часах: ', - 'auto_stop_duration_text': 'Длительность ожидания для сброса в минутах: ', - 'bots_text': 'Количество ботов: ', - 'stealth_text': 'Стелс-режим (скрытое окно игры): ', - 'mute_text': 'Выключать звук: ', - 'hotkey_settings': 'Настройки горячих клавиш', - 'save': 'Сохранить', - 'back': 'Назад', + "settings_window_title": "Настройки", + "settings_title": "Настройки", + "settings_help": "Для дополнительной информации наведите на пункт настроек", + "language_name_text": "Язык: ", + "font_text": "Шрифт: ", + "autostart_text": "Автоматически запускать бота: ", + "branch_text": "Ветка обновлений: ", + "debug_text": "Дебаг-выводы: ", + "mode_name_text": "Режим: ", + "character_text": "Персонаж: ", + "duration_text": "Длительность: ", + "auto_stop_text": "Сброс лимита опыта: ", + "auto_detect_auto_stop_text": "Автоматическое определение лимита: ", + "auto_stop_frequency_text": "Частота сброса в часах: ", + "auto_stop_duration_text": "Длительность ожидания для сброса в минутах: ", + "bots_text": "Количество ботов: ", + "stealth_text": "Стелс-режим (скрытое окно игры): ", + "mute_text": "Выключать звук: ", + "hotkey_settings": "Настройки горячих клавиш", + "save": "Сохранить", + "back": "Назад", # Окно настройки горячих клавиш - 'hotkeys_window_title': 'Горячие клавиши', - 'hotkeys_title': 'Горячие клавиши', - 'up_text': 'Вверх (Не прыжок!)', - 'left_text': 'Влево', - 'down_text': 'Вниз', - 'right_text': 'Вправо', - 'throw_text': 'Бросок', - 'quick_text': 'Слабая атака', - 'dodge_text': 'Уворот', - 'heavy_text': 'Сильная атака', + "hotkeys_window_title": "Горячие клавиши", + "hotkeys_title": "Горячие клавиши", + "up_text": "Вверх (Не прыжок!)", + "left_text": "Влево", + "down_text": "Вниз", + "right_text": "Вправо", + "throw_text": "Бросок", + "quick_text": "Слабая атака", + "dodge_text": "Уворот", + "heavy_text": "Сильная атака", # Окно инструкций - 'instructions_window_title': 'Инструкции', - 'instructions_contents': ['Инструкции по использованию бота:', - '1. ОЧЕНЬ ВАЖНО\n' - 'Либо используйте стелс-режим в настройках бота, либо никак не мешайте его работе. ' - 'Движение мышью над окном игры, изменение размера или перемещение этого окна и использование клавиатуры при выбранном окне игры ' - 'во время работы бота может привести к неожиданным и нежелательным последствиям, ' - 'таким как выбор неверных персонажей или даже совершение покупок в внутриигровом магазине или боевом пропуске. ' - 'Если вы заметили что случайно что-то передвинули, перезапустите бота (а лучше используйте стелс-режим, серьезно)', - '2. Убедитесь что внутриигровой язык - Английский', - '3. Убедитесь, что настройка "Свернуть разноплановых" ("Collapse crossovers") - включена', - '4. Переведите игру в оконный режим если бот не делает это сам', - '5. Настройте все по своему усмотрению и нажмите "Старт"', - 'Наслаждайтесь c:'], + "instructions_window_title": "Инструкции", + "instructions_contents": [ + "Инструкции по использованию бота:", + "1. ОЧЕНЬ ВАЖНО\n" + "Либо используйте стелс-режим в настройках бота, либо никак не мешайте его работе. " + "Движение мышью над окном игры, изменение размера или перемещение этого окна и использование клавиатуры при выбранном окне игры " + "во время работы бота может привести к неожиданным и нежелательным последствиям, " + "таким как выбор неверных персонажей или даже совершение покупок в внутриигровом магазине или боевом пропуске. " + "Если вы заметили что случайно что-то передвинули, перезапустите бота (а лучше используйте стелс-режим, серьезно)", + "2. Убедитесь что внутриигровой язык - Английский", + '3. Убедитесь, что настройка "Свернуть разноплановых" ("Collapse crossovers") - включена', + "4. Переведите игру в оконный режим если бот не делает это сам", + '5. Настройте все по своему усмотрению и нажмите "Старт"', + "Наслаждайтесь c:", + ], # Высвечивающиеся окна - 'changelog_popup_title': 'Новая версия', + "changelog_popup_title": "Новая версия", } TOOLTIPS = { # Основное окно - 'delayed_stop': [ - 'Выключить бота после завершения текущей игры или перед началом следующей', - 'Отменить отложенное выключение', + "delayed_stop": [ + "Выключить бота после завершения текущей игры или перед началом следующей", + "Отменить отложенное выключение", ], # Окно настроек - 'settings_help': '\n :) \n', - 'language_name_column': 'Выбор языка (автоматически обновляется)', - 'font_column': 'Выбор шрифта (автоматически обновляется)', - 'autostart_column': 'Автоматически запускать бота, если запущена программа и вы не играете в Brawlhall\'у\n' - 'Условия проверяются каждые 5 минут', - 'branch_column': 'В бете могут быть экспериментальные/нестабильные изменения', - 'debug_column': 'Выводит МНОГО бесполезной дебаг-информации в окно вывода', - 'mode_name_column': 'Выбор режима, который бот будет использовать для выбора персонажей/длительности', - 'character_column': 'Выбор персонажа (выключен для некоторых режимов)', - 'duration_column': 'Выбор длительности\n' - 'Для некоторых режимов это максимальная длительность игры', - 'auto_stop_column': 'Brawlhalla накладывает лимит на количество опыта/золота, которое можно получить за одну сессию игры\n' - 'Эта настройка автоматически приостанавливает бота для сброса данного лимита\n' - 'Иначе, бот будет продолжать работать, но почти не получая опыта/золота', - 'auto_detect_auto_stop_column': 'Включает автоматическое определение момента достижения лимита по опыту, получаемому после игры\n' - 'Выключает следующие две настройки\n' - '!!Может быть нестабильным для персонажей уровня 60 и выше!!', - 'auto_stop_frequency_column': 'Как часто боту стоит останавливаться для сброса лимита\n' - 'Из опыта, 4.5-5 часов - среднее время достижения лимита', - 'auto_stop_duration_column': 'Как долго боту стоит ждать сброса лимита\n' - 'Из опыта, 30 минут - минимальное значение, полностью сбрасывающее лимит', - 'bots_column': 'Как много ботов (не включая игрока) будет в игре\n' - 'Урон и голы не влияют на получаемый опыт/золото, так что оптимальное значение - 2\n' - 'Известно, что Brawlhalla работает очень нестабильно с >4 игроками и долгими играми', - 'stealth_column': 'Прячет окно игры после запуска Brawlhall\'ы\n' - 'Так же автоматически включает следующую настройку', - 'mute_column': 'Стоит ли боту выключать звук/музыку после попадания в меню', + "settings_help": "\n :) \n", + "language_name_column": "Выбор языка (автоматически обновляется)", + "font_column": "Выбор шрифта (автоматически обновляется)", + "autostart_column": "Автоматически запускать бота, если запущена программа и вы не играете в Brawlhall'у\n" + "Условия проверяются каждые 5 минут", + "branch_column": "В бете могут быть экспериментальные/нестабильные изменения", + "debug_column": "Выводит МНОГО бесполезной дебаг-информации в окно вывода", + "mode_name_column": "Выбор режима, который бот будет использовать для выбора персонажей/длительности", + "character_column": "Выбор персонажа (выключен для некоторых режимов)", + "duration_column": "Выбор длительности\n" + "Для некоторых режимов это максимальная длительность игры", + "auto_stop_column": "Brawlhalla накладывает лимит на количество опыта/золота, которое можно получить за одну сессию игры\n" + "Эта настройка автоматически приостанавливает бота для сброса данного лимита\n" + "Иначе, бот будет продолжать работать, но почти не получая опыта/золота", + "auto_detect_auto_stop_column": "Включает автоматическое определение момента достижения лимита по опыту, получаемому после игры\n" + "Выключает следующие две настройки\n" + "!!Может быть нестабильным для персонажей уровня 60 и выше!!", + "auto_stop_frequency_column": "Как часто боту стоит останавливаться для сброса лимита\n" + "Из опыта, 4.5-5 часов - среднее время достижения лимита", + "auto_stop_duration_column": "Как долго боту стоит ждать сброса лимита\n" + "Из опыта, 30 минут - минимальное значение, полностью сбрасывающее лимит", + "bots_column": "Как много ботов (не включая игрока) будет в игре\n" + "Урон и голы не влияют на получаемый опыт/золото, так что оптимальное значение - 2\n" + "Известно, что Brawlhalla работает очень нестабильно с >4 игроками и долгими играми", + "stealth_column": "Прячет окно игры после запуска Brawlhall'ы\n" + "Так же автоматически включает следующую настройку", + "mute_column": "Стоит ли боту выключать звук/музыку после попадания в меню", } MESSAGES = { - 'waiting_for_bh_window': 'Жду окна игры', - 'found_bh': 'Окно игры найдено', - 'not_windowed_mode': 'Игра не в оконном режиме, перезапускаюсь', - 'stealth_mode': 'Вхожу в стелс-режим', - 'initialized': 'Бот инициализирован', - 'initial_on_exit': '\nИтоги сессии:\n\nВремя работы: %s\nИгр завершено: %s\nКрашей/Перезапусков: %s\nОпыта получено: %s\n', - 'on_exit_has_rewards': 'Золота получено: %s\n', - 'on_exit_no_rewards': 'Золота получено: %s (+награды за повышение уровня)\n', - 'started_fighting': 'Начинаю бой', - 'ended_fighting': 'Завершаю бой', - 'reinitializing': 'Перезапускаюсь', - 'queued_recalc': 'Перезапускаю игру для запланированного пересчета опыта персонажей', - 'not_in_menu': 'Не в меню', - 'collecting_bonus': 'Собираю награду за вход', - 'accepting_event_popup': 'Принимаю награду события', - 'offline': 'Не в сети, пытаюсь подключиться', - 'item_not_selected': '%s не выбран', - 'sorting_by_date': 'Сортирую по дате', - 'collecting_character_data': 'Собираю информацию о персонажах', - 'skip_lvl_valid': 'Пропускаю проверку уровня', - 'calc_xp': 'Подсчитанный опыт: %s', - 'pixel_xp': 'Опыт по пикселям: %s', - 'xp_discrep': 'Обнаружено несоответствие в полученном опыте\nПланирую сброс лимита опыта и пересчет опыта персонажей', - 'waiting_for_loading': 'Жду загрузки', - 'loading': 'Загрузка', - 'pick_char': 'Выбираю %s', - 'setting_dur': 'Устанавливаю длительность на %s', - 'wait_for_xp_reset': 'Жду сброса лимита %s минут', - 'wait_remaining': 'Жду сброса лимита: осталось %s секунд', - 'creating_lobby': 'Создаю лобби', - 'setting_lobby': 'Настраиваю лобби', - 'starting_game': 'Начинаю игру', - 'loaded': 'Загрузка завершена', - 'disconnected': 'Отключился от игры, перезапускаю', - 'return_to_lobby': 'Возвращаюсь в лобби', - 'no_modes': 'Не найдено ни одного режима. Программа не будет работать с пустой папкой modes.', - 'old_config': 'Настройки были сделаны для старой версии бота. Пожалуйста, убедитесь что они актуальны.', - 'level_error': 'Ошибка при распознавании уровня\nБот работает только с уровнями 1-100', - 'cant_save_hotkeys': 'Не получилось сохранить горячие клавиши. Ошибка: %s', - 'cant_save_config': 'Не получилось сохранить настройки. Ошибка: %s', - 'start_bot': 'Запускаю бота', - 'stop_bot': 'Выключаю бота', - 'test': 'Тест', - 'downloading': 'Загрузка: %s%%', - 'downloaded': 'Загрузка завершена за %s', - 'resize': 'Размер окна игры: %sx%s\nРазмер клиент-зоны игры: %sx%s\nРазмер экрана: %sx%s', - 'move_offscreen': 'Перемещаю окно игры в другое измерение', - 'update_total_stats': 'Обновляю статистику за все время', - 'total_stats': '\nСтатистика за все время:\n\nИгр завершено: %s\nОпыта получено: %s\nЗолота получено: %s\nВремя работы: %s\n', - 'autostart_check': 'Проверяю условия автозапуска', - 'rotation_error': 'Ошибка при получении ротации персонажей:\n%s', - 'muting': 'Выключаю звук', - 'reconnected': 'Переподключился', - 'settings': 'Настройки:\n' - '------------------------\n' - 'App Name: {0.APP_NAME}\n' - 'App Version: {0.APP_VERSION}\n' - 'Compiled: {0.compiled}\n' - 'Branch: {0.branch}\n' - 'New version: {0.new_version}\n' - 'Language: {0.language_name}\n' - 'Font: {0.font}\n' - 'Autostart: {0.autostart}\n' - 'Debug: {0.debug}\n' - '------------------------\n', - 'config': 'Конфиг:\n' - '------------------------\n' - 'Mode: {0.mode_name}\n' - 'Character: {0.character}\n' - 'Duration: {0.duration}\n' - 'Auto-stop: {0.auto_stop}\n' - 'Auto detect auto-stop: {0.auto_detect_auto_stop}\n' - 'Auto-stop frequency: {0.auto_stop_frequency}\n' - 'Auto-stop duration: {0.auto_stop_duration}\n' - 'Bots: {0.bots}\n' - 'Stealth: {0.stealth}\n' - 'Mute: {0.mute}\n' - '------------------------\n', - 'resized_warning': 'Размер окна игры был изменен. Пожалуйста, прочитайте инструкции. Бот сейчас перезапустится', - 'danger_zone_warning': 'Бот в опасном состоянии (в внутриигровом магазине или боевом пропуске). Перезапускаюсь', - 'invalid_state_warning': 'Бот в нестандартном состоянии. Что-то пошло не так. Перезапускаюсь', - 'no_steam_exe': 'Файл запуска клиента Steam не найден. Попробуйте запустить Steam и включить бота заново', - 'delayed_stop': 'Бот автоматически выключится после завершения текущей игры или перед началом следующей', - 'cancel_stop': 'Все отложенные выключения отменены', - 'menu_pixels_error': 'Ошибка при получении пикселей для определения состояния. Не волнуйтесь, это просто значит что будут использованы значения по умолчанию:\n%s', - 'took_screenshot': 'Снимок экрана сохранен в %s', - 'bh_not_running': 'Игра не запущена', + "waiting_for_bh_window": "Жду окна игры", + "found_bh": "Окно игры найдено", + "not_windowed_mode": "Игра не в оконном режиме, перезапускаюсь", + "stealth_mode": "Вхожу в стелс-режим", + "initialized": "Бот инициализирован", + "initial_on_exit": "\nИтоги сессии:\n\nВремя работы: %s\nИгр завершено: %s\nКрашей/Перезапусков: %s\nОпыта получено: %s\n", + "on_exit_has_rewards": "Золота получено: %s\n", + "on_exit_no_rewards": "Золота получено: %s (+награды за повышение уровня)\n", + "started_fighting": "Начинаю бой", + "ended_fighting": "Завершаю бой", + "reinitializing": "Перезапускаюсь", + "queued_recalc": "Перезапускаю игру для запланированного пересчета опыта персонажей", + "not_in_menu": "Не в меню", + "collecting_bonus": "Собираю награду за вход", + "accepting_event_popup": "Принимаю награду события", + "offline": "Не в сети, пытаюсь подключиться", + "item_not_selected": "%s не выбран", + "sorting_by_date": "Сортирую по дате", + "collecting_character_data": "Собираю информацию о персонажах", + "skip_lvl_valid": "Пропускаю проверку уровня", + "calc_xp": "Подсчитанный опыт: %s", + "pixel_xp": "Опыт по пикселям: %s", + "xp_discrep": "Обнаружено несоответствие в полученном опыте\nПланирую сброс лимита опыта и пересчет опыта персонажей", + "waiting_for_loading": "Жду загрузки", + "loading": "Загрузка", + "pick_char": "Выбираю %s", + "setting_dur": "Устанавливаю длительность на %s", + "wait_for_xp_reset": "Жду сброса лимита %s минут", + "wait_remaining": "Жду сброса лимита: осталось %s секунд", + "creating_lobby": "Создаю лобби", + "setting_lobby": "Настраиваю лобби", + "starting_game": "Начинаю игру", + "loaded": "Загрузка завершена", + "disconnected": "Отключился от игры, перезапускаю", + "return_to_lobby": "Возвращаюсь в лобби", + "no_modes": "Не найдено ни одного режима. Программа не будет работать с пустой папкой modes.", + "old_config": "Настройки были сделаны для старой версии бота. Пожалуйста, убедитесь что они актуальны.", + "level_error": "Ошибка при распознавании уровня\nБот работает только с уровнями 1-100", + "cant_save_hotkeys": "Не получилось сохранить горячие клавиши. Ошибка: %s", + "cant_save_config": "Не получилось сохранить настройки. Ошибка: %s", + "start_bot": "Запускаю бота", + "stop_bot": "Выключаю бота", + "test": "Тест", + "downloading": "Загрузка: %s%%", + "downloaded": "Загрузка завершена за %s", + "resize": "Размер окна игры: %sx%s\nРазмер клиент-зоны игры: %sx%s\nРазмер экрана: %sx%s", + "move_offscreen": "Перемещаю окно игры в другое измерение", + "update_total_stats": "Обновляю статистику за все время", + "total_stats": "\nСтатистика за все время:\n\nИгр завершено: %s\nОпыта получено: %s\nЗолота получено: %s\nВремя работы: %s\n", + "autostart_check": "Проверяю условия автозапуска", + "rotation_error": "Ошибка при получении ротации персонажей:\n%s", + "muting": "Выключаю звук", + "reconnected": "Переподключился", + "settings": "Настройки:\n" + "------------------------\n" + "App Name: {0.APP_NAME}\n" + "App Version: {0.APP_VERSION}\n" + "Compiled: {0.compiled}\n" + "Branch: {0.branch}\n" + "New version: {0.new_version}\n" + "Language: {0.language_name}\n" + "Font: {0.font}\n" + "Autostart: {0.autostart}\n" + "Debug: {0.debug}\n" + "------------------------\n", + "config": "Конфиг:\n" + "------------------------\n" + "Mode: {0.mode_name}\n" + "Character: {0.character}\n" + "Duration: {0.duration}\n" + "Auto-stop: {0.auto_stop}\n" + "Auto detect auto-stop: {0.auto_detect_auto_stop}\n" + "Auto-stop frequency: {0.auto_stop_frequency}\n" + "Auto-stop duration: {0.auto_stop_duration}\n" + "Bots: {0.bots}\n" + "Stealth: {0.stealth}\n" + "Mute: {0.mute}\n" + "------------------------\n", + "resized_warning": "Размер окна игры был изменен. Пожалуйста, прочитайте инструкции. Бот сейчас перезапустится", + "danger_zone_warning": "Бот в опасном состоянии (в внутриигровом магазине или боевом пропуске). Перезапускаюсь", + "invalid_state_warning": "Бот в нестандартном состоянии. Что-то пошло не так. Перезапускаюсь", + "no_steam_exe": "Файл запуска клиента Steam не найден. Попробуйте запустить Steam и включить бота заново", + "delayed_stop": "Бот автоматически выключится после завершения текущей игры или перед началом следующей", + "cancel_stop": "Все отложенные выключения отменены", + "menu_pixels_error": "Ошибка при получении пикселей для определения состояния. Не волнуйтесь, это просто значит что будут использованы значения по умолчанию:\n%s", + "took_screenshot": "Снимок экрана сохранен в %s", + "bh_not_running": "Игра не запущена", } diff --git a/levels.py b/levels.py index 8e79113..7cf9e24 100644 --- a/levels.py +++ b/levels.py @@ -15,31 +15,146 @@ locked_color = (110, 200, 211) first_digit_dict = { - '1': ((10, 17), (15, 45), (17, 4), (20, 15), (27, 45), (29, 4), (19, 32)), - '2': ((5, 11), (6, 5), (9, 3), (30, 4), (34, 45), (36, 12)), - '3': ((3, 37), (7, 10), (8, 3), (9, 23), (33, 6), (36, 34)), - '4': ((2, 25), (2, 34), (19, 45), (30, 45), (32, 3), (37, 27)), - '5': ((4, 37), (5, 26), (7, 4), (23, 20), (34, 4), (36, 32)), - '6': ((2, 28), (3, 37), (16, 3), (25, 46), (29, 3), (37, 27)), - '7': ((4, 3), (4, 11), (5, 45), (19, 45), (36, 3), (36, 12)), - '8': ((2, 38), (5, 12), (19, 27), (20, 3), (36, 11), (37, 35)), - '9': ((10, 45), (15, 3), (15, 30), (19, 23), (22, 28), (24, 45)), - '0': ((1, 30), (9, 6), (15, 13), (23, 46), (24, 34), (37, 17)) + "1": ((10, 17), (15, 45), (17, 4), (20, 15), (27, 45), (29, 4), (19, 32)), + "2": ((5, 11), (6, 5), (9, 3), (30, 4), (34, 45), (36, 12)), + "3": ((3, 37), (7, 10), (8, 3), (9, 23), (33, 6), (36, 34)), + "4": ((2, 25), (2, 34), (19, 45), (30, 45), (32, 3), (37, 27)), + "5": ((4, 37), (5, 26), (7, 4), (23, 20), (34, 4), (36, 32)), + "6": ((2, 28), (3, 37), (16, 3), (25, 46), (29, 3), (37, 27)), + "7": ((4, 3), (4, 11), (5, 45), (19, 45), (36, 3), (36, 12)), + "8": ((2, 38), (5, 12), (19, 27), (20, 3), (36, 11), (37, 35)), + "9": ((10, 45), (15, 3), (15, 30), (19, 23), (22, 28), (24, 45)), + "0": ((1, 30), (9, 6), (15, 13), (23, 46), (24, 34), (37, 17)), } -second_digit_dict = dict(((key, tuple(((x + second_digit_diff, y) for x, y in value))) for key, value in first_digit_dict.items())) +second_digit_dict = dict( + ( + (key, tuple(((x + second_digit_diff, y) for x, y in value))) + for key, value in first_digit_dict.items() + ) +) -single_digit_dict = dict(((key, tuple(((x + single_digit_diff, y) for x, y in value))) for key, value in first_digit_dict.items())) +single_digit_dict = dict( + ( + (key, tuple(((x + single_digit_diff, y) for x, y in value))) + for key, value in first_digit_dict.items() + ) +) -level_hundred_conditions = ((0, 9), (0, 15), (10, 42), (11, 8), (27, 14), (49, 39), (62, 37), (84, 9), (84, 40)) +level_hundred_conditions = ( + (0, 9), + (0, 15), + (10, 42), + (11, 8), + (27, 14), + (49, 39), + (62, 37), + (84, 9), + (84, 40), +) levels_xp = [ 0, # infinite xp if lvl 0 (undefined) - 210, 368, 455, 542, 628, 737, 867, 997, 1127, 1278, 1430, 1582, 1733, 1907, 2080, 2253, 2427, 2622, 2817, 3012, 3207, 3402, 3618, 3813, 4030, 4247, 4463, 4680, - 4918, 5135, 5373, 5612, 5850, 6088, 6372, 6565, 6803, 7063, 7302, 7562, 7800, 8060, 8320, 8580, 8840, 9122, 9382, 9642, 9923, 10183, 10465, 10747, 11007, 11288, 11570, - 11852, 12113, 12415, 12718, 13000, 13282, 13585, 13867, 14170, 14473, 14755, 15058, 15362, 15665, 15968, 16272, 16575, 16878, 17182, 17507, 17810, 18113, 18438, 18742, - 19067, 19370, 19695, 20020, 20323, 20648, 20973, 21298, 21623, 21948, 22273, 22598, 22923, 23270, 23595, 23920, 24267, 24592, 24917, 25263, - float('inf') # infinite xp if lvl 100 (max lvl) + 210, + 368, + 455, + 542, + 628, + 737, + 867, + 997, + 1127, + 1278, + 1430, + 1582, + 1733, + 1907, + 2080, + 2253, + 2427, + 2622, + 2817, + 3012, + 3207, + 3402, + 3618, + 3813, + 4030, + 4247, + 4463, + 4680, + 4918, + 5135, + 5373, + 5612, + 5850, + 6088, + 6372, + 6565, + 6803, + 7063, + 7302, + 7562, + 7800, + 8060, + 8320, + 8580, + 8840, + 9122, + 9382, + 9642, + 9923, + 10183, + 10465, + 10747, + 11007, + 11288, + 11570, + 11852, + 12113, + 12415, + 12718, + 13000, + 13282, + 13585, + 13867, + 14170, + 14473, + 14755, + 15058, + 15362, + 15665, + 15968, + 16272, + 16575, + 16878, + 17182, + 17507, + 17810, + 18113, + 18438, + 18742, + 19067, + 19370, + 19695, + 20020, + 20323, + 20648, + 20973, + 21298, + 21623, + 21948, + 22273, + 22598, + 22923, + 23270, + 23595, + 23920, + 24267, + 24592, + 24917, + 25263, + float("inf"), # infinite xp if lvl 100 (max lvl) ] gold_levels = list(range(7, 20, 2)) + list(range(21, 101)) @@ -76,13 +191,17 @@ def _get_level_hundred(image): def get_level(self): screenshot = self.brawlhalla.make_screenshot() level = screenshot.crop(level_bbox) - for f in [self._get_single_digit_level, self._get_double_digit_level, self._get_level_hundred]: + for f in [ + self._get_single_digit_level, + self._get_double_digit_level, + self._get_level_hundred, + ]: try: # noinspection PyArgumentList return f(level) except TypeError: pass - logger.error('level_error') + logger.error("level_error") raise LevelNotDetected def get_percentage(self): @@ -102,7 +221,10 @@ def get_reward_percentage(self): bar = screenshot.crop(rewards_bar_bbox) for i in range(bar.width): pixel = bar.getpixel((i, 0)) - if all(rewards_bar_colors[i][1] > pixel[i] > rewards_bar_colors[i][0] for i in range(len(pixel))): + if all( + rewards_bar_colors[i][1] > pixel[i] > rewards_bar_colors[i][0] + for i in range(len(pixel)) + ): count += 1 perc = count / bar.width return perc @@ -110,7 +232,10 @@ def get_reward_percentage(self): def get_xp(self, level, reward=False): if level == 100: return 0 - return int((self.get_reward_percentage() if reward else self.get_percentage()) * levels_xp[level]) + return int( + (self.get_reward_percentage() if reward else self.get_percentage()) + * levels_xp[level] + ) def get_unlocked(self): screenshot = self.brawlhalla.make_screenshot() diff --git a/menu.py b/menu.py index db8ce6f..e0bd20e 100644 --- a/menu.py +++ b/menu.py @@ -14,17 +14,17 @@ class MenuItem: def __init__(self, name: str): self.name = name self.position: Optional[int] = None # assigned by parent - self.parent: Optional['Layout'] = None # assigned by parent + self.parent: Optional["Layout"] = None # assigned by parent def move_to_parent(self, vi: VirtualInput) -> list[Callable]: return [] - def _move_to(self, target: 'MenuItem', vi: VirtualInput) -> list[Callable]: + def _move_to(self, target: "MenuItem", vi: VirtualInput) -> list[Callable]: if self.parent is None: - raise Exception('Trying to move from orphan') + raise Exception("Trying to move from orphan") if isinstance(target, ThirdColumn): # noinspection PyUnresolvedReferences - if find_element('first_column').current_position > 6: + if find_element("first_column").current_position > 6: target.current_position = 2 else: target.current_position = 1 @@ -32,11 +32,15 @@ def _move_to(self, target: 'MenuItem', vi: VirtualInput) -> list[Callable]: return self.move_to_parent(vi) if target in self.parent.contents: if self.parent.direction == Direction.HORIZONTAL: - return (target.position - self.position) * [vi.right] + (self.position - target.position) * [vi.left] - return (target.position - self.position) * [vi.down] + (self.position - target.position) * [vi.up] - raise Exception('Trying to move to non-adjacent node') - - def move_to(self, target: 'MenuItem', vi: VirtualInput) -> list[Callable]: + return (target.position - self.position) * [vi.right] + ( + self.position - target.position + ) * [vi.left] + return (target.position - self.position) * [vi.down] + ( + self.position - target.position + ) * [vi.up] + raise Exception("Trying to move to non-adjacent node") + + def move_to(self, target: "MenuItem", vi: VirtualInput) -> list[Callable]: path = path_between(self, target) steps = [] for i in range(len(path) - 1): @@ -45,8 +49,13 @@ def move_to(self, target: 'MenuItem', vi: VirtualInput) -> list[Callable]: class Layout(MenuItem): - def __init__(self, name: str, contents: list[MenuItem], base_position: int, - direction: Direction): + def __init__( + self, + name: str, + contents: list[MenuItem], + base_position: int, + direction: Direction, + ): super().__init__(name) for pos, item in enumerate(contents): item.parent = self @@ -59,7 +68,7 @@ def __init__(self, name: str, contents: list[MenuItem], base_position: int, def current_element(self): return self.contents[self.current_position] - def _move_to(self, target: 'MenuItem', vi: VirtualInput) -> list[Callable]: + def _move_to(self, target: "MenuItem", vi: VirtualInput) -> list[Callable]: if target in self.contents: steps = self.current_element._move_to(target, vi) self.current_position = target.position @@ -126,62 +135,65 @@ def path_between(source: MenuItem, target: MenuItem) -> list[MenuItem]: def generate_layout(): options_items = [ - MenuItem(name) for name in [ - 'system_settings', - 'controls', - 'change_region', - 'report_bug', - 'image_render_tool', - 'legal', - 'exit_game', + MenuItem(name) + for name in [ + "system_settings", + "controls", + "change_region", + "report_bug", + "image_render_tool", + "legal", + "exit_game", ] ] - options = Layout('options', options_items, 0, Direction.VERTICAL) + options = Layout("options", options_items, 0, Direction.VERTICAL) mini_menu_items = [ - MenuItem(name) for name in [ - 'inventory', - 'store', - 'battle_pass', - 'notifications', - 'friends', - 'clans', - 'replays', + MenuItem(name) + for name in [ + "inventory", + "store", + "battle_pass", + "notifications", + "friends", + "clans", + "replays", ] ] mini_menu_items.append(options) - mini_menu = MiniMenu('mini_menu', mini_menu_items, 0, Direction.HORIZONTAL) + mini_menu = MiniMenu("mini_menu", mini_menu_items, 0, Direction.HORIZONTAL) third_column_items = [ mini_menu, - MenuItem('smol_ad'), - MenuItem('smoller_ad'), + MenuItem("smol_ad"), + MenuItem("smoller_ad"), ] second_column_items = [ - MenuItem('big_ad'), + MenuItem("big_ad"), ] first_column_items = [ - MenuItem(name) for name in [ - 'play', - 'ranked', - 'battle_pass', - 'custom_game_room', - 'brawl', - 'offline', - 'meet_the_legends', - 'store', + MenuItem(name) + for name in [ + "play", + "ranked", + "battle_pass", + "custom_game_room", + "brawl", + "offline", + "meet_the_legends", + "store", ] ] - first_column = Layout('first_column', first_column_items, 0, Direction.VERTICAL) + first_column = Layout("first_column", first_column_items, 0, Direction.VERTICAL) main_layout_items = [ first_column, - Layout('second_column', second_column_items, 0, Direction.VERTICAL), - ThirdColumn('third_column', third_column_items, 1, Direction.VERTICAL), + Layout("second_column", second_column_items, 0, Direction.VERTICAL), + ThirdColumn("third_column", third_column_items, 1, Direction.VERTICAL), ] - main = Layout('main', main_layout_items, 0, Direction.HORIZONTAL) + main = Layout("main", main_layout_items, 0, Direction.HORIZONTAL) return main diff --git a/modes/default/__pycache__/fastlvl.cpython-311.pyc b/modes/default/__pycache__/fastlvl.cpython-311.pyc index bf5ef58414c114d6fa88036892a192080ea7f0a6..260a5e5db033da540dfcedeeedf41c2b9d32784b 100644 GIT binary patch delta 270 zcmbQmH<^!jIWI340}zO8mQ1^}k@qX3iI1~YOlWaxQE`l4W^#6nOHqD7Qhr5DfU|;z zk!FlbesXDUYF=>3IU0a%nWRNEfF7>U;b74lH7EGcen&Ma40GnaSBPE=BnTN%<8q?uogX zdFe4uMTzA(8HqVLi7`$dPWdIexrxOksYRR3nDUqeM1cksa{`G5h65p2T%#s$Vae1` z0rQ{oi%v+Hl5s`M=z>KQ2=PZ>AAKPv_6mR81&+AM0j$#*r%rysnyJso8qfHF z0YrXeW?T5Q0b`lanji+!#YAA7GPl6l4VH#7=gkd<4sU!69EH2y`g`|A|s8 diff --git a/modes/default/__pycache__/gold.cpython-311.pyc b/modes/default/__pycache__/gold.cpython-311.pyc index 4c1600c38a9f2c19bcea34b51ac6bc9b27a6c4df..ec3aea686042de2d4f7fd9ae0180e14b78ba44c9 100644 GIT binary patch delta 270 zcmX@hdyJQNIWI340}#k;mP{+z$h(u##M9X-CbT%Us5r(iGdVlPr6|83DZe5nz*#}V zNHfMIKe;qFHLs*N#yzvdqckbT$-^nXWb<#v1SSD7pgF~yK%#+RhshOd|H;`bX~z0s z{!@O@8$1F%9#?oYF6j9K(FbNG0j`e>K;jFCc)-Q|g_TKw_X7hHC-3B+EYlhLCQoF| z)L>=>3IU0a%nWRNEfF7>U;YbA>0mQ3r|$a|g9#Mjv>CbT%Us5r(iGdVlPr6|83DZe5nz*#}V zNHfMIKe;qFHLs*N#yzvdqckbT$-^nXWV0kw8Iy(>(41mLAn~JtVTR%ywFNFqJeK%h zQL(+Q;(STP`J#&J6&2S@Qm&J?v!q#>LDW6v7rx6QbVI=P0u0>{5ShYpML_u@D>Ek9*r_`tx*$u-%RwUV)U@^RKU4K`Mw5RmxD%)rLi67hivCIA*i5R*gL f+!(zl&t{V`7i9!$!6Z6LK7vHQV38^k1UeM}+(%T1 delta 309 zcmeys`-7KvIWI340}x#L_c&E+Bky%aQ$J^`n9$h{K(9}#@7<@fe9f5kw7LVhq1XahEAT# ZCgUi;2-1n2>?rvNmidB1zDN-0VgOlIVod-5 diff --git a/modes/default/__pycache__/totallvl.cpython-311.pyc b/modes/default/__pycache__/totallvl.cpython-311.pyc index 21857a14de5388a316ab40cf340a7ae153a7c35c..6072dd3feb1a52583c37a154b6c22b96ec983673 100644 GIT binary patch delta 80 zcmX@cdz6=VIWI340}yC#mQ0(xk(Z0f#Mjv>CbT%Us5r(iGdVlPr6|83DZe5nz*#}V iNHfMIKe;qFHLs*N#yzvdqckbT$-^nXWU~)b6$=2z&>4mR delta 81 zcmX@gdyJQNIWI340}x#L_c*m~BQF<|sh_h|OlWaxQE`l4W^#6nOHqD7Qhr5@dtz>8 jUV4mEQDS*cMq*A*VvLiAQ+|nVZenpsYSCtIrYaTyayA^U diff --git a/modes/default/fastlvl.py b/modes/default/fastlvl.py index 7c17d19..2feee55 100644 --- a/modes/default/fastlvl.py +++ b/modes/default/fastlvl.py @@ -3,9 +3,9 @@ class FastLvl(Mode): name = { - 'default': 'Leveling up characters closest to next level', - 'Русский': 'Повышение общего уровня', - 'English': 'Leveling up characters closest to next level', + "default": "Leveling up characters closest to next level", + "Русский": "Повышение общего уровня", + "English": "Leveling up characters closest to next level", } character_selection_enabled = False @@ -18,4 +18,6 @@ def next_duration(self): @property def next_character(self): - return sorted(self.bot.unlocked_characters, key=lambda char: char.xp_to_next_level)[0] + return sorted( + self.bot.unlocked_characters, key=lambda char: char.xp_to_next_level + )[0] diff --git a/modes/default/gold.py b/modes/default/gold.py index d74aec4..1a319e6 100644 --- a/modes/default/gold.py +++ b/modes/default/gold.py @@ -3,9 +3,9 @@ class Gold(Mode): name = { - 'default': 'Gold farming', - 'Русский': 'Фарм голды', - 'English': 'Gold farming', + "default": "Gold farming", + "Русский": "Фарм голды", + "English": "Gold farming", } character_selection_enabled = False @@ -18,4 +18,6 @@ def next_duration(self): @property def next_character(self): - return sorted(self.bot.unlocked_characters, key=lambda char: char.xp_to_next_gold)[0] + return sorted( + self.bot.unlocked_characters, key=lambda char: char.xp_to_next_gold + )[0] diff --git a/modes/default/powerlvl.py b/modes/default/powerlvl.py index a4866a0..731d200 100644 --- a/modes/default/powerlvl.py +++ b/modes/default/powerlvl.py @@ -3,9 +3,9 @@ class PowerLvl(Mode): name = { - 'default': 'Leveling up one character', - 'Русский': 'Повышение персонажа', - 'English': 'Leveling up one character', + "default": "Leveling up one character", + "Русский": "Повышение персонажа", + "English": "Leveling up one character", } def __init__(self, *args, **kwargs): @@ -17,4 +17,9 @@ def next_duration(self): @property def next_character(self): - return next(filter(lambda x: x.name == self.bot.config.character.lower(), self.bot.characters)) + return next( + filter( + lambda x: x.name == self.bot.config.character.lower(), + self.bot.characters, + ) + ) diff --git a/modes/default/totallvl.py b/modes/default/totallvl.py index d437829..59ccd86 100644 --- a/modes/default/totallvl.py +++ b/modes/default/totallvl.py @@ -3,9 +3,9 @@ class TotalLvl(Mode): name = { - 'default': 'Leveling up characters with lowest level', - 'Русский': 'Повышение низкоуровневых персонажей', - 'English': 'Leveling up characters with lowest level', + "default": "Leveling up characters with lowest level", + "Русский": "Повышение низкоуровневых персонажей", + "English": "Leveling up characters with lowest level", } character_selection_enabled = False diff --git a/utils.py b/utils.py index eb7a0f8..dfd8160 100644 --- a/utils.py +++ b/utils.py @@ -10,6 +10,7 @@ import PySimpleGUI as Sg import requests + # noinspection PyProtectedMember from pyupdater import rfh, log from pyupdater.client import Client @@ -26,7 +27,7 @@ def get_text(element): return element.get() elif isinstance(element, Sg.Button): return element.get_text() - return f'Unsupported element type {type(element)}' + return f"Unsupported element type {type(element)}" def set_text(element, value): @@ -39,8 +40,10 @@ def set_text(element, value): def my_emit(superclass, record): record = copy(record) - if isinstance(record.msg, str) and record.msg.count('%s') < len(record.args): - record.msg += ' ' + ' '.join(['%s' for _ in range(len(record.args) - record.msg.count('%s'))]) + if isinstance(record.msg, str) and record.msg.count("%s") < len(record.args): + record.msg += " " + " ".join( + ["%s" for _ in range(len(record.args) - record.msg.count("%s"))] + ) superclass.emit(record) @@ -59,15 +62,15 @@ def format(self, record): # noinspection PyProtectedMember format_orig = self._style._fmt if record.levelno == logging.DEBUG: - self._style._fmt = '%(asctime)s %(levelname)s %(message)s' + self._style._fmt = "%(asctime)s %(levelname)s %(message)s" elif record.levelno == logging.INFO: - self._style._fmt = '%(message)s' + self._style._fmt = "%(message)s" result = super().format(record) self._style._fmt = format_orig return result -logger = logging.getLogger('main_bot_logger') +logger = logging.getLogger("main_bot_logger") logger.setLevel(logging.DEBUG) hdlr = MyStreamHandler() hdlr.setLevel(logging.DEBUG) @@ -85,88 +88,101 @@ def excepthook(type_, value, traceback): class Settings: installation_folder = Path.cwd() - logs_folder = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'logs' - installation_info_location = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'installation.data' - settings_location = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'settings.cfg' - config_location = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'bhbot.cfg' - hotkeys_location = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'hotkeys.cfg' - stats_location = Path(os.getenv('LOCALAPPDATA')) / 'BHBot' / 'stats.json' + logs_folder = Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "logs" + installation_info_location = ( + Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "installation.data" + ) + settings_location = Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "settings.cfg" + config_location = Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "bhbot.cfg" + hotkeys_location = Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "hotkeys.cfg" + stats_location = Path(os.getenv("LOCALAPPDATA")) / "BHBot" / "stats.json" def __init__(self, settings): - self.APP_NAME = 'BHBot' - self.APP_VERSION = '3.5.23' + self.APP_NAME = "BHBot" + self.APP_VERSION = "3.5.23" self.APP_CHANGELOGS = { - 'English': [ - f'Updated to {self.APP_VERSION} \\o/', + "English": [ + f"Updated to {self.APP_VERSION} \\o/", 'If it\'s your first time using the bot or seeing this message, please click "Instructions" and read them carefully', - '- Fixed ingame detection with low connection', + "- Fixed ingame detection with low connection", ], - 'Русский': [ - f'Обновился до {self.APP_VERSION} \\o/', + "Русский": [ + f"Обновился до {self.APP_VERSION} \\o/", 'Если вы используете бота или видите это сообщение впервые, пожалуйста, нажмите на "Инструкции" и тщательно их прочтите', - '- Починил определение нахождения в бою при низком подключении', - ] + "- Починил определение нахождения в бою при низком подключении", + ], } - self.compiled = getattr(sys, 'frozen', False) + self.compiled = getattr(sys, "frozen", False) self.load_installation_info() self.clear_old_logs() self.add_file_handlers() self.load_fonts() - pyupdater_client = Client(ClientConfig(), refresh=True, progress_hooks=[self.print_update_status]) + pyupdater_client = Client( + ClientConfig(), refresh=True, progress_hooks=[self.print_update_status] + ) - self.branch = settings.get('branch', 'stable') + self.branch = settings.get("branch", "stable") - self.new_version = pyupdater_client.update_check(self.APP_NAME, self.APP_VERSION, channel=self.branch, strict=self.branch != 'beta') + self.new_version = pyupdater_client.update_check( + self.APP_NAME, + self.APP_VERSION, + channel=self.branch, + strict=self.branch != "beta", + ) self.languages = self.get_languages() if not self.languages: - logger.error('No languages found. Program will not function with empty languages directory.') - self.language_name = settings.get('language_name', 'English') + logger.error( + "No languages found. Program will not function with empty languages directory." + ) + self.language_name = settings.get("language_name", "English") - self.APP_CHANGELOG = self.APP_CHANGELOGS.get(self.language_name, self.APP_CHANGELOGS.get('English')) + self.APP_CHANGELOG = self.APP_CHANGELOGS.get( + self.language_name, self.APP_CHANGELOGS.get("English") + ) self.fonts = self.get_fonts() - self.font = settings.get('font', 'Cousine Regular') + self.font = settings.get("font", "Cousine Regular") - self.autostart = settings.get('autostart', False) + self.autostart = settings.get("autostart", False) - self.icon = self.installation_folder / 'icon.ico' + self.icon = self.installation_folder / "icon.ico" - self.debug = settings.get('debug', False) + self.debug = settings.get("debug", False) self.gui_handler = None self.loaded_modes = {} @staticmethod def print_update_status(info): - if info['status'] == 'downloading': - logger.info('downloading', info["percent_complete"]) - elif info['status'] == 'finished': - logger.info('downloaded', info["time"]) + if info["status"] == "downloading": + logger.info("downloading", info["percent_complete"]) + elif info["status"] == "finished": + logger.info("downloaded", info["time"]) @classmethod def load(cls): try: - return cls(json.load(cls.settings_location.open('r'))) + return cls(json.load(cls.settings_location.open("r"))) except FileNotFoundError: return cls({}) # noinspection PyUnresolvedReferences def get_languages(self): languages = [] - for language in self.languages_folder.glob('*.py'): - spec = importlib.util.spec_from_file_location('module.name', language) + for language in self.languages_folder.glob("*.py"): + spec = importlib.util.spec_from_file_location("module.name", language) language_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(language_module) languages.append(language_module) return languages def get_fonts(self): - fonts = ['Courier'] - for font in self.fonts_folder.glob('**/*.ttf'): - fonts.append(get_font_name(font.open('rb'))) + fonts = ["Courier"] + for font in self.fonts_folder.glob("**/*.ttf"): + fonts.append(get_font_name(font.open("rb"))) return fonts def get_language(self, name): @@ -178,7 +194,17 @@ def language(self): @property def not_save(self): - return ['fonts', 'languages', 'gui_handler', 'loaded_modes', 'new_version', 'compiled', 'APP_CHANGELOG', 'APP_CHANGELOGS', 'icon'] + return [ + "fonts", + "languages", + "gui_handler", + "loaded_modes", + "new_version", + "compiled", + "APP_CHANGELOG", + "APP_CHANGELOGS", + "icon", + ] def set_debug_state(self): if self.gui_handler: @@ -191,9 +217,9 @@ def save(self): self.set_debug_state() try: self.settings_location.parent.mkdir(parents=True, exist_ok=True) - json.dump(self.get_save_vars(), self.settings_location.open('w+')) + json.dump(self.get_save_vars(), self.settings_location.open("w+")) except Exception as e: - logger.error(f'Could not save settings. Exception: {e}') + logger.error(f"Could not save settings. Exception: {e}") # noinspection PyUnresolvedReferences @property @@ -209,13 +235,23 @@ def update_window(self, window): window[key].Widget.config(font=window[key].Font) if key in self.language.LAYOUT_MAPPING: value = self.language.LAYOUT_MAPPING.get(key) - res_text = value[window[key].metadata].format(self) if window[key].metadata is not None else value.format(self) + res_text = ( + value[window[key].metadata].format(self) + if window[key].metadata is not None + else value.format(self) + ) if res_text != get_text(window[key]): set_text(window[key], res_text) tooltip_text = None - if hasattr(self.language, 'TOOLTIPS') and key in self.language.TOOLTIPS: # First condition is for backwards-compatibility + if ( + hasattr(self.language, "TOOLTIPS") and key in self.language.TOOLTIPS + ): # First condition is for backwards-compatibility tooltip_value = self.language.TOOLTIPS.get(key) - tooltip_text = tooltip_value[window[key].metadata].format(self) if window[key].metadata is not None else tooltip_value.format(self) + tooltip_text = ( + tooltip_value[window[key].metadata].format(self) + if window[key].metadata is not None + else tooltip_value.format(self) + ) if tooltip_text != window[key].Tooltip: window[key].Tooltip = tooltip_text window[key].set_tooltip(tooltip_text) @@ -225,126 +261,145 @@ def update_window(self, window): window.set_title(window.Title) def load_installation_info(self): - logger.debug('Loading installation info') + logger.debug("Loading installation info") if not self.compiled: return - if all(Path(folder).exists() for folder in ['fonts', 'languages', 'modes']): - logger.debug('Writing installation info') + if all(Path(folder).exists() for folder in ["fonts", "languages", "modes"]): + logger.debug("Writing installation info") return self.write_installation_info() if not self.installation_info_location.exists(): - logger.error('Corrupted installation. Exiting.') + logger.error("Corrupted installation. Exiting.") sys.exit() - installation_info = json.load(self.installation_info_location.open('r')) + installation_info = json.load(self.installation_info_location.open("r")) for path in installation_info: setattr(type(self), path, Path(installation_info[path])) def write_installation_info(self): - logger.debug('Writing installation info') + logger.debug("Writing installation info") self.installation_info_location.parent.mkdir(parents=True, exist_ok=True) obj = { - 'installation_folder': Path.cwd(), - 'logs_folder': self.logs_folder, - 'settings_location': self.settings_location, - 'config_location': self.config_location, - 'hotkeys_location': self.hotkeys_location, - 'stats_location': self.stats_location, + "installation_folder": Path.cwd(), + "logs_folder": self.logs_folder, + "settings_location": self.settings_location, + "config_location": self.config_location, + "hotkeys_location": self.hotkeys_location, + "stats_location": self.stats_location, } obj = {k: str(v) for k, v in obj.items()} - json.dump(obj, self.installation_info_location.open('w+')) + json.dump(obj, self.installation_info_location.open("w+")) def clear_old_logs(self): - for log_file in self.logs_folder.glob('????????-??????.log'): + for log_file in self.logs_folder.glob("????????-??????.log"): dt = log_file.name[:-4] - dt = datetime.strptime(dt, '%Y%m%d-%H%M%S') + dt = datetime.strptime(dt, "%Y%m%d-%H%M%S") if datetime.now() - dt > timedelta(days=3): log_file.unlink() def add_file_handlers(self): self.logs_folder.mkdir(parents=True, exist_ok=True) - file_hdlr = MyFileHandler(self.logs_folder / f'{datetime.now().strftime("%Y%m%d-%H%M%S")}.log', encoding='utf-8') - file_hdlr.setFormatter(logging.Formatter('[%(asctime)s] [%(threadName)s:THREAD%(thread)d] %(levelname)-8s %(message)s')) + file_hdlr = MyFileHandler( + self.logs_folder / f'{datetime.now().strftime("%Y%m%d-%H%M%S")}.log', + encoding="utf-8", + ) + file_hdlr.setFormatter( + logging.Formatter( + "[%(asctime)s] [%(threadName)s:THREAD%(thread)d] %(levelname)-8s %(message)s" + ) + ) file_hdlr.setLevel(logging.DEBUG) logger.addHandler(file_hdlr) - error_hdlr = MyFileHandler(self.logs_folder / 'exceptions.log', encoding='utf-8') - error_hdlr.setFormatter(logging.Formatter('[%(asctime)s] [%(threadName)s:THREAD%(thread)d] %(levelname)-8s %(message)s')) + error_hdlr = MyFileHandler( + self.logs_folder / "exceptions.log", encoding="utf-8" + ) + error_hdlr.setFormatter( + logging.Formatter( + "[%(asctime)s] [%(threadName)s:THREAD%(thread)d] %(levelname)-8s %(message)s" + ) + ) error_hdlr.setLevel(logging.WARNING) logger.addHandler(error_hdlr) @property def fonts_folder(self): - return self.installation_folder / 'fonts' + return self.installation_folder / "fonts" @property def languages_folder(self): - return self.installation_folder / 'languages' + return self.installation_folder / "languages" @property def modes_folder(self): - return self.installation_folder / 'modes' + return self.installation_folder / "modes" def load_fonts(self): - for font in self.fonts_folder.glob('**/*.ttf'): + for font in self.fonts_folder.glob("**/*.ttf"): load_font(str(font)) def update_stats(self, **kwargs): current = self.get_stats() for key in kwargs: current[key] = current.get(key, 0) + kwargs[key] - json.dump(current, self.stats_location.open('w+')) + json.dump(current, self.stats_location.open("w+")) def get_stats(self): if not self.stats_location.exists(): return {} - return json.load(self.stats_location.open('r')) + return json.load(self.stats_location.open("r")) def __str__(self): - return self.messages.get('settings', 'Missing "settings" entry in language').format(self) + return self.messages.get( + "settings", 'Missing "settings" entry in language' + ).format(self) global_settings = Settings.load() def box(text, startmargin=True, endmargin=True): - lines = text.split('\n') + lines = text.split("\n") longest_line = sorted(lines, key=lambda x: len(x), reverse=True)[0] length = len(longest_line) + 6 if startmargin: - logger.info('\n\n') - logger.info('#' * length) + logger.info("\n\n") + logger.info("#" * length) for line in lines: spaces = (length - len(line) - 2) / 2 - logger.info('#' + ' ' * ceil(spaces) + line + ' ' * floor(spaces) + '#') - logger.info('#' * length) + logger.info("#" + " " * ceil(spaces) + line + " " * floor(spaces) + "#") + logger.info("#" * length) if endmargin: - logger.info('\n\n') + logger.info("\n\n") def format_time(seconds): seconds = int(seconds) m, s = divmod(seconds, 60) h, m = divmod(m, 60) - return f'{h:d}:{m:02d}:{s:02d}' + return f"{h:d}:{m:02d}:{s:02d}" def get_rotation(): try: - res = requests.get('https://sovamor.co/bhbot/rotation', timeout=5).json() - return [character.lower() for character in res.get('rotation')] + res = requests.get("https://sovamor.co/bhbot/rotation", timeout=5).json() + return [character.lower() for character in res.get("rotation")] except Exception as e: - logger.warning('rotation_error', e) + logger.warning("rotation_error", e) return [] def get_menu_pixels(): try: - res = requests.get('https://sovamor.co/bhbot/menu_pixels', timeout=5).json() + res = requests.get("https://sovamor.co/bhbot/menu_pixels", timeout=5).json() return { k: { - inner_k: tuple(x if not isinstance(x, list) else tuple(x) for x in inner_v) for inner_k, inner_v in v.items() - } for k, v in res.items() + inner_k: tuple( + x if not isinstance(x, list) else tuple(x) for x in inner_v + ) + for inner_k, inner_v in v.items() + } + for k, v in res.items() } except Exception as e: - logger.warning('menu_pixels_error', e) + logger.warning("menu_pixels_error", e) return {} @@ -356,7 +411,7 @@ def chunks(input_list, n): d, r = divmod(len(input_list), n) for i in range(n): si = (d + 1) * (i if i < r else r) + d * (0 if i < r else i - r) - yield input_list[si:si + (d + 1 if i < r else d)] + yield input_list[si : si + (d + 1 if i < r else d)] def compare(fst, snd): diff --git a/windows.py b/windows.py index 1afa19e..c91ff95 100644 --- a/windows.py +++ b/windows.py @@ -41,7 +41,7 @@ def window_enumeration_handler(hwnd, response): class SteamClient: _WIN_REG_SHELL = (winreg.HKEY_CLASSES_ROOT, r"steam\Shell\Open\Command") - _PROCESS_NAME = 'steam.exe' + _PROCESS_NAME = "steam.exe" def __init__(self): self.path = self.find_exe() @@ -72,7 +72,7 @@ def _find_exe_registry(self): shell_reg_value = self.__search_registry_for_run_cmd(*self._WIN_REG_SHELL) if shell_reg_value is None: return None - reg = re.compile("\"(.*?)\"") + reg = re.compile('"(.*?)"') return reg.search(shell_reg_value).groups()[0] def find_exe(self): @@ -92,7 +92,9 @@ def __init__(self, hwnd, proc): @classmethod def find(cls): - res = get_window('Brawlhalla.exe') or get_window('BrawlhallaGame.exe') # support for beta + res = get_window("Brawlhalla.exe") or get_window( + "BrawlhallaGame.exe" + ) # support for beta if not res: return None return cls(*res) @@ -106,7 +108,7 @@ def responding(self): def kill(self): while self.process.is_running(): self.process.kill() - sleep(.5) + sleep(0.5) def get_window_rect(self): return win32gui.GetWindowRect(self.window) @@ -126,21 +128,46 @@ def fullscreen(self): def resize(self): window_size = self.get_window_size() client_size = self.get_client_size() - logger.debug('resize', *window_size, *client_size, *Sg.Window.get_screen_size()) + logger.debug("resize", *window_size, *client_size, *Sg.Window.get_screen_size()) w_border = window_size[0] - client_size[0] h_border = window_size[1] - client_size[1] - while self.get_client_size() != (1920, 1080): # getwindowsize or getclientsize or setwindowpos or something else is weird so it sometimes doesnt work first try - win32gui.SetWindowPos(self.window, 0, 0, 0, 1920 + w_border, 1080 + h_border, win32con.SWP_NOZORDER) + while self.get_client_size() != ( + 1920, + 1080, + ): # getwindowsize or getclientsize or setwindowpos or something else is weird so it sometimes doesnt work first try + win32gui.SetWindowPos( + self.window, + 0, + 0, + 0, + 1920 + w_border, + 1080 + h_border, + win32con.SWP_NOZORDER, + ) def move_off_screen(self): - logger.debug('move_offscreen') + logger.debug("move_offscreen") w, h = Sg.Window.get_screen_size() - win32gui.SetWindowPos(self.window, 0, w * 4, h * 4, 0, 0, win32con.SWP_NOSIZE | win32con.SWP_NOZORDER) + win32gui.SetWindowPos( + self.window, + 0, + w * 4, + h * 4, + 0, + 0, + win32con.SWP_NOSIZE | win32con.SWP_NOZORDER, + ) def make_transparent(self): style = win32gui.GetWindowLong(self.window, win32con.GWL_EXSTYLE) win32gui.ShowWindow(self.window, win32con.SW_HIDE) - style |= win32con.WS_EX_COMPOSITED | win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT | win32con.WS_EX_TOOLWINDOW | win32con.WS_EX_NOACTIVATE + style |= ( + win32con.WS_EX_COMPOSITED + | win32con.WS_EX_LAYERED + | win32con.WS_EX_TRANSPARENT + | win32con.WS_EX_TOOLWINDOW + | win32con.WS_EX_NOACTIVATE + ) style &= ~win32con.WS_EX_APPWINDOW win32gui.SetWindowLong(self.window, win32con.GWL_EXSTYLE, style) sleep(1) @@ -153,6 +180,7 @@ def hide(self): def make_screenshot(self): import win32ui + w, h = self.get_client_size() window_dc = win32gui.GetWindowDC(self.window) @@ -169,7 +197,15 @@ def make_screenshot(self): bmpinfo = save_bit_map.GetInfo() bmpstr = save_bit_map.GetBitmapBits(True) - im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1) + im = Image.frombuffer( + "RGB", + (bmpinfo["bmWidth"], bmpinfo["bmHeight"]), + bmpstr, + "raw", + "BGRX", + 0, + 1, + ) win32gui.DeleteObject(save_bit_map.GetHandle()) save_dc.DeleteDC() @@ -178,14 +214,16 @@ def make_screenshot(self): return im def set_low_priority(self): - handle = win32api.OpenProcess(win32con.PROCESS_SET_INFORMATION, True, self.process.pid) + handle = win32api.OpenProcess( + win32con.PROCESS_SET_INFORMATION, True, self.process.pid + ) win32process.SetPriorityClass(handle, BELOW_NORMAL_PRIORITY_CLASS) win32api.CloseHandle(handle) class Singleton: def __init__(self): - res = get_window('BHBot.exe') + res = get_window("BHBot.exe") if res: window, _ = res self.set_focus(window)