From 4c7aca42e979516a59935bbc1a9a278de86041f3 Mon Sep 17 00:00:00 2001 From: Mikkel Troels Kongsted Date: Fri, 10 Apr 2026 15:26:36 +0200 Subject: [PATCH] use angle from backend to move skateboard and show score --- game/Cargo.lock | 10 ++ game/Cargo.toml | 2 +- .../BitcountGridDouble/BitcountGridDouble.ttf | Bin 0 -> 162000 bytes game/assets/BitcountGridDouble/OFL.txt | 93 ++++++++++++++++ game/src/engine/game.rs | 3 + game/src/engine/sdl_io.rs | 100 ++++++++++++++++-- game/src/main.rs | 58 ++++++---- 7 files changed, 241 insertions(+), 25 deletions(-) create mode 100644 game/assets/BitcountGridDouble/BitcountGridDouble.ttf create mode 100644 game/assets/BitcountGridDouble/OFL.txt diff --git a/game/Cargo.lock b/game/Cargo.lock index 0d7efc4..3a06627 100644 --- a/game/Cargo.lock +++ b/game/Cargo.lock @@ -104,6 +104,7 @@ dependencies = [ "lazy_static", "libc", "sdl3-sys", + "sdl3-ttf-sys", ] [[package]] @@ -112,6 +113,15 @@ version = "0.6.1+SDL-3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d73fc820b0b6b9bd0557de7b799ec9da1ca4f35fcd1cb42f7ceb85e640d1477" +[[package]] +name = "sdl3-ttf-sys" +version = "0.6.0+SDL-ttf-3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c30da3dcd7e062f19350b47a532f7629c42801b801687535fd31e8ba2adfd71d" +dependencies = [ + "sdl3-sys", +] + [[package]] name = "skate-slope" version = "0.1.0" diff --git a/game/Cargo.toml b/game/Cargo.toml index ef3aa1c..2b0ca34 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] -sdl3 = "0.17.3" +sdl3 = { version = "0.17.3", features = ["ttf"] } rand = "0.8" diff --git a/game/assets/BitcountGridDouble/BitcountGridDouble.ttf b/game/assets/BitcountGridDouble/BitcountGridDouble.ttf new file mode 100644 index 0000000000000000000000000000000000000000..78fd014c7e69a704cfe5d305a05f46a1bf71c69e GIT binary patch literal 162000 zcmeFa34ByV_Ag#_d+&5QD|>(tWDCfu7sye4mojT{# zsZ-UrD~vP7y5iT{nj zW-PgqG0)&hr{~TLbU8_5EW3!Yg!-w--&6lXU&bc9&sdksrq3ENy0-YN37}U4dgsoX zojd!(p-b*&%(0F!o?d)W@yhiNp70mI2Lpaa$+F@V&DT9Kjxl#V7&_}bzz>c-vp3SW z0q2;-#T6@nmyEh-pLrLTtvRpHj4|mT^&{gYUo8fHW%=a^jFoR=?7k^WN{bg|H$L|e z@b>|K>=G2XPju!X4|?pFCCe&TUokZeykzDu=1(sxFDd?4%Zg{wmkh>Sr!OmBy}~)H z+hf4L4EUMLN-K+3-1_P$fF(DUB1qP(JV?A#}IFgAY%ggSl2 z%F-1}+#73wUj}^7^E_7jxbtdc8IQ$S5n6<02&x4`CX{P%|n3(raV!$Ib7dHCk|LwyaPxPUOLjC&Aaffs6!I!DeeL077+INFKrWNE=SL#^iXJBh8*=0 zqV6Rbuyhv0{SQpLc=3`-?qDphXx*hUCO6{Qr@1?g*ykY!_ zavJR#q>>K(bticc4z(|6Fzz7P2wb`m?~FT1PLvXtEOU{}ganLo#@!@8nXN)KN^uYO z;zQJ-JZzDqM%)4=<#a8*m;#9g?MfnfQtE(2tWLL09_$Yn?pTw`=oB209ruEWJ z&|cF%bvPYfN5C=IQQxPw&mDdCXQpTN%bWUR-de)S(CEP%_=?Sg#oWO##%J^a>g<-22X;6dW;{(Pvle3%GbPE>yB1l)pj|U z!|m`p2BMWJw9=57mf1ZsBQsO9a-yx36s46#dMh+zF!n9p<`w%~CZju|PcattM}1Lm z+b?YejD5ZrZ%CM~&$B*v;+3%H)jccrl)VTJdASiB>)?THz_PVy+Dpymgov7vTos;=k zKAJD%WB4q7JRio7<+*$w&*MY*L_U@0^8$W0U%=1dg?uKo8pK@Lg>_^7SQcisY<3(Q z%W~i$PhsO(AuD22*=cMpI}?6=F)L%s*h)T}PvawaB_GT$X0`B#*RdPf&Fmg_FMEhR z!k%Hzu@~51*^BHIc*wWdcJ?mY#s1B{fqZ{rKePROB%c9$Sp{!5g)isjdD&ci&5`*?^C-~;&}UIfqT!c5?SU-z+#;K{nP z9+)9|v7u}jJC+THuRfJcfalJ`tW?0yVr$^R7qGM0Iczz*lpSVuY%N>I*0ZbF2G+7$@a(s>un^_8R*qKa;hvPuXW|H`~L0VQp+5X1Kc}USuvGuVQK6JjF7FE$8KVmu_)$_J6LaaC+h>-?aS_h9pBCRv->b^?q>to18g8> zk|FG2b~%e-Bs~Euf0B)2Phn<$nvKD5JDxquPQXlhB4*N)*#EGTF`r(+IlGc;Y#e5w z5PO;BvcJKm9BdN%7n{spXH(c4EQ}d#8r#99v$rvGy@ROpJvNKI&t|g^*c|pDo5wz4 z^V!F&8Z+Lxhyse)7i=N>l9jNpV556kDf=3;-FK{<{mNFb3U+{1vV&|DJH!UD2U#N9 z#4ct{>=M?>PRHDS2K$6x%CF>AyqaIeSF@$;dv-qifnC6UWEZjqc!nH)GREym{11FQ zpTOtxC44??_B8Rjm;D0BdcI2=q^;Cmb|gD4aQxYs=A7fa)%lUDmutT3R@Xjvu6wh4 zw`Z8=a?f^ee{ZGtWnWj{V&5aaL;jHeD*xLFeG--=yb#a=69d(O`a~^pVq#t5o55gk zc5rj>%_Miy+@wd6zDv$ZE=hhQxg{k(<+_wzse@8irq-vq(~8ozrtL}3PG6n=w=RJ$ z^Sk`1tGDZ%u1|JzbUVGNT!cxYsKg zu8e|=t1~|7-M4pH??-$8+-F3e%0AEaY3+Mr-;4S_*Y`l?DVf!ouk}mrcR{}wv$|z1 z&3dXo>p#E$ll}J}GxnJE$Ltv}XuxFyUL4RiFf=eS@by6{gXRsob=j5|azT@P*e;E0P zEC2BNDLqapIpu*<8crR1>gA`tG|n}yaNG^!J_!v7m4{vk#d0U-Zp!`F_|)-dj=y*O zPZLg>5T5YH#2ynbnE2PcguJ4>>+(L#@0GtK|DpVS1t%3$6}&#F-=vC3FHUNkJZ|ze zli!uNQt&_)X#AB2UryqUxf5 zPVF{z;nZiQIi}@L+cNE|X>HScPCsG#h0~v!esD%$M(-J8XH1{5amGJR^Pg6D+Pc$T zo|!On&dh6P-Zk^ZnLB3gnb|h0=dA2m6KBntRXQs?>-Jf1&5F(*JA3l%`LoZPeaY;r zWfDp&mdw3u?zXuv&;52@+Pq=& zCeB+jZ^OLj=KVB3IDhv1&GX+mqt6-Vow4bR7tc6+X8xJ$&wTGJ_gT}=y7R0(XEiMd zF6gyj@PbnoOj~fyf)xvD7i?W{&w@u5yu9G`1@A3rIXnC8GtXXe_N`~Xdv^4kymQVz z=h<^}&)rkJW8u#wkCuGCX!xR4iw>98mOfv)@4VsXU4Gtci+e6!viPCJjY|raT({(t zr305PUAk@Q&hvYpzxezo&W~L%^@3Y3*jqNHEL`@+vL4G8F8kxMhUFFItURxLbNTx# zx~`bF;*J$xU6_4g<%NG<$yVmBjI4aMBC%q2#hn%3R~}z^S>UZ7Y)4V zoQt+y^v%ViFTVWZH&^#seg5ia);QP9T66cBUoSc7lFCb-yky^{qc2@?>64eXTvl}1 z4VQg!`H0J_E`R5Wfmd97#ow>wS5CO{>MM6v^{!f6^+?sh@QCoT@WbJssz+2`Sp8ge zQ{;rms>lP8&ua$Ol-Jx_^J#6@+PSs2)P7nwxURhJ*|mJ_gthC}?pW7jUCFvf*R`zA zTfb@j`&ae6s^qH2uWH*cWy5tF{=ISN#)~%oKCw;M!i-F1YrdYxiE)_qs*bJ$T*Eo5yUvZ1XFdo3B6q`W4sz>H6=t4BfI~%QIV| zH%z@@+YR4s9kF%I)@QfIZY;X7_QscP^4~P)rY$#pcyr&I&%gQhn?Jav>n(F`*>cM# zxAwSo#;w=fy8X5Zw>^Ek@Aflpf8h3C?-+B(syqJYPJU;>o!8yDb6f9i=WTobuEe`e zzw6<<_T4?=?v-~xb9d`K6Yja{p11BzzxT|0@3?pGeIxF>$D$OBhC z@X;T${#gFUNB;QhgQFj;c<}iL4?UFg&?OJmKkREQSB`}qARbMWr`Ms1b?&18xEMt&2&ncu>1<+t(M`5pXDzK!3-@8txj93t<%UXo+Kt*x+RfT6+O67c+U?pM+MU`q?Jn(Z?H=u3?LP4Lfc8i2L2ZTP zoIl5(=P&R-^Z((0;eX{X@|So$f0_S{zk)t>fnI41+(pP~JR&>?Vx`q6yA&}?l8ES1 z5YgR)$mL$dCVdgfy@h%I--roLKvdL>xUdbe%>?d1)RD*g@IEY`XF^T|yp)%+$%xmM zvMG?|?W~Zb$%^=1{xzGXC22hnjgVTUZeU-02Y;Kt!{0@T6Pi0-7~s^=~E2H;-@S}!72t!3}>>tLUMMGAF6@h9{&lV8)WtT)Q8ww3k8 zD%#(!ZV%p*Z|b15JKu~_ij{9h%VuxTbIzwKvY_1$Y0+-J6?%Mu-+=oggooJMlqx@t z-N@g=X!wAC$anHx{3HG`|AhaWf671OyZIjeIsbxx$-g3!Nklx?zRZL+gpvV20LP>!bD6GPQnMmeyZ8MjL(HFHbl$ThGH#r ztTtR5p^el=X`{6<+Hu&yuu|^VH7d zOe6V-a>l+I)j_}AXcr*`Ev3DY%M%$8CGPbgs}?kg}uT*@(1}t{9*nGf0RGQ z|HL2XPw*%CQ~YWE41bpYo&STs%3tIEuJsgyROyK8VkCr^|XUcapmY~C(FSECF{CaPv_Tq z+Rcu0KB=cYY_l^{Ps4{f#SRME^06#OgI=D%{@{34PY0BABG1yc=;c8+Ry$iy_h83p zC&@H~G*HuNV$RIsI!(-^J^3yj$ALb-si&R5c}P!#XLu;VKlVEPyiQMhSP<(sS?y0NG-XEr4~SOeb0L#9mrY%qg-w z1vqc(=~P&VE<>IMJ>9LBr(?d_sHeNY6V~YIt}GL4R!OHDR^c=Cba(6s7_#aC4?9vX z?}@sD^>i<^o1v#O#Jni!^cFMZ9ISm;Vl}%Mb51385=yZLQjAq_8FCYFFUQKb6m3rj zUMY4J#-pqlYv>D@fi=D01j6|wo9?E zQ;K~HlIn8EEe|Q`FUh(RTAu|f#X|P&A;iH7QH!XQ1Ai`R8Jbjkn2B}_Dizo>DF@vW zQA6sfLbS93bytHznXWJ5U=i|*gzc4rMv35x%82vvz^@c^Rta7UgdHt{4V9yB3q?Q3 zrYgZ@1*~)gUgf$Kou{`hErwbmD;ti|m9Ru9u?n=Cuj7#%%3+z6*pop74%|$%P8jhN zh*#o9@|X$yB?5OAEVKwP!-`ePq*K{5;$axDPRFOz4M{ZNusiI*vOrT)>FF2{(LHn<4AoDZx#e9p9K+3G1tmqt0sdKo@ffXJSWnB;LnCRy2ZW zjM%Bo5L!4N&=Tk`6V|v2pG!cEEIt#nMtj>@tlKY*X~Tv_nAS%2zFgS4YC$97EZ|rh zA49O-#5V~4eg9Vm|Fwbt+CV2Zun6V&HZg{0W6XDsmBxz3DpzqtTBk`ge#_5@cBrim zaMiMZ>Tu&zg&A!SjLMBqqujuyPa|!>j86lm)}=C|Jp+$Ejq=W5Y8z_Ws7qzWTc!KI zBW>_+&^JESwhbKmG}45p;uv{6d~{?Tl9fZ&U_^~+|z#|p(7)~-CBuT|XB?`zDB_#JSX5Qk`u9vK3P1KFbI_bu0aT4|sridqogfg^fG^)`q?9_TZsB)w$ z1kQfzIvO0;E1lDEy^>JdIW(a*Wm4zRlu6zEokP3%Gv4YPn(Rn+DJ996EUB z@bu21!_&v?>>N5~=ZUv;4kcJT)X-7;a;9Ihykn?QSE9Dpf(o^3L5142phE3hP@#4$ zs8G8WRH$7GD%7q86>8Ul3bkuNh1#{ipcP9m?Io!~?UGcXc1fyGyChYpU6Lx)E=d(? zm!t}{OHzf}C85Nz#ILBuNX}kt8i>N0PLl z9ZAxHb|gs)+L0tJXy=k790^rz*hqIQXBBFfvkJA#S%uo=tU~Q_R-tw|t5Ca~Rj6Ig zD%37#6>68W3hhYFQidH%lIfm-pms^BP`e~ms9lmO)GkRCYL}!6wM$Zk+9jz%?UGcX zc1fyGyCe-ccWkjn-Oiv?E^&@*?RKa^IRqVb07XcMZy^UF1nc-2D-Cdjhs%mrUckC8 zyI|P`;yXzE7KTX(ec${po>=xWHv&aG`Ghye9)20_ry_(*Kxk;Z=)2GEdJfM^Y4ZZl zf;v(T`o#`it}6Tx3%@(VZ$ysrZT%|8XrK7aK`_d(_f6mYdc_N^#B%gj7+SG@!-IYV zZV_+viGJ5(J>3%67U<3#fqZ;leITJVp&{Yh-*Mli)CRfT z)9;jrgzciIM|nij&(!Y-@QA^j6O;&a}{4bRp znZqNtek3dU`|lbHe@|#nQ_?~C5ji0V{5b)?I7#?V+wG`D+18QoaWX%$3~f-^N0UCd zmX@jZQB7NIsdD&Fdn&z?q@dr)q3Yfyjy@4%b2gGvgz zu5G=Rl0Wj@&S9(2zF`mTc|I~V6{=D;_#`o?QXBV#sfN5||}e7Uq=-4U@emaJ#9LjXN60>!+6(enpbfZMqmyoSKenCOy7U|z7 z6G2DUxTIiGHZ}M=D3fI>aj8kum@#FGN4{IKm2yz~+s>BMwvIAw^iH?5qpW3@b9_&U zn%PH5Q}$g=W6p8#Vfc0h-$!CC@K5|s`gK``JHC6QGJI1lS0B8Ju&+hwRtdp9#u%lvy$n%Oy`~rqnMnsE#gU^!D$J9r#TYinYdIi*Wg;>Z|*1q6q za5~!)BU&BpFj#GLw8LOQJKABepdIZnSkR7k7%XTT85f(o^3L5142phE3hP@#4$s8G8WRH$7GD%7q86>8Ul3bkuNh1#{i zFcLzhn%YZJh1w;lLhX`Np>|2CP`e~ms9lmO)GkRCYL}!6wM$Zk+9jz%?UGdGECm{0 z5>n($@OY@KA#v zX;?Br?UGcXc1fyGyChYpU6Lx)E=d(?m!t}{OHzf}C8r$trr0giDb(&6)Vg-hphE4QL512qg9^2K1{G@e3@X&_8C0m!I^AU>|Eo8U-HMjjPiZI{P^A1pMUh$ z)5$66-Fo&NG^WU&FmcQTTGC&!XX0N*xyR@8CkE5H_is9U_|X1_pT7Ono-lFD1fI*+ zU$NWbUq-q4Jf``SrafWem95%N@h_u1DLJi6_ddsrEckWbPd|M7#lPQv!JaU2 z%mj3KJakTvozf$w@eh~I!LJDEj5yjPAaLvulws_ zhxAu87K=q=ZFsfB4q@MpUh>Ha91e(GwcpHxP8LG}`A&mR)xH^)>PjY5YN_;)*>3AJf65J%mCB*YHfrqc{!kHJs$P zkd@$^8Yg$*oCA7jdJ$_A=a9zOAUvQw7-!{`;v~`6ao%hK5AZ}Dq(`cG5^LniJcXyS zp*VwJ7*5g?CmHf|{WRKUb}aAChVve51WtA7#p#ULQS3b48)wAy1#j=+G=q0=ir`|N z$@}pvF=CGAbeiD+HkQsW#A$+G@xkl_oXMEYhhj8+h|~E#U`xbljl*%e;z&M<9^b|} zj$?4<0v*|oWoS!q7P2=;}bUq&^HJ&N_@M?Y*PL@0y{`eC7 z{`OLg@5^w`ySMiJ30)8>hsrUwGg>2(%aN6Kq{1Wyv#^>lJvsAWoCK9^Y{6W0`oszaP$!ya8v> zY{J;O31>szf-`|`#CegMaWbVx_`JX3%$aL&dSosYAi`kZ}&^Bh0MIh6kv zXH@RNiI!JkY(@Ace8=(von(X)Q?KV+==>AoyeljcU$&olCC|$G%`;azJzuKh(^!mC zp8kJ)TFwldS2VM;vx$!4EU^{;7fusGH+JC95h+6DE`_U0Jef*?DE9tNjxel~)#*l$0*7bWbcPMm0;(f#`C{j6TP)R4tXoa7Wi=&M`~DKM zMfV~p=u*9Ysi^NSH4!|ey492l#Dv8PnZ+hnV6iH|#S(Rih3Z}^Wx7;vW~pRzsV=|s zEzH0LDy1?Zfusx$XWS#PCUZ>1zzAwUTg3O5xB znzz!hno5Bms8oRNRkDYx^tM-twq2`))vQuE@T}5%eX-S!`(kMk7Yh!27n@eIM!{Jl zaV`-!{!2{Zdx}dXpsIG2#QlmW+43UjGM&U-#`Wu+ArK0yb(FZ79elnPqy2!QAe zrTnr#qEK{+Qi*2UlnWT#QpQUirbM#ZXDaXsp;0OcH^aqHK0%i&rTtPklyv5;T3H?} zDPOT>uydMd@-;QCqRvUAbmOY2``}otHVhti%mxOCOaNQ_fqtWMwJkJ?E9LS}8u4 zUL@cZOIH)T0xo(vB}$1yLRh|35b#TcbC#D>1`HTw5_CAF{i02wvqUR-PFabXt0-|6 z4_{j8D;7!c7>U!2MDcL2dk%F~vS#EYYciLrO4UG+tjS!mC0$t%kP?=e zpdn?Ws^rrE>e80k6h-2)#5ra7>P10i5=GWCh1g%cC`Bz6CC*$xe7T00eYu8+gSm0y z_2n9(4dg0f_2$x81qr_%a|mjNT>X0Ffx<+AXb^XfN0D;^ZiqzWG)fgG81SGHpHO8h zL=kAh3n1kr^YNLc(NxC`_De zdRF9M4PYQ%?ao4!C8?nmAr|50lOYy?)hJ6q%q~qO>}gas&9DidjK8R!8iENNvmOm< zpE;m?=3oxS4`knTgK9t>z?xnt&;|DNO_LB!C$R|J+QN{)*y1-kAn(D3QVJLlpSRANnJeKOn>DTA4S8FW&md32jk z2=fhupc0D$5SXuWD`QitbOu$DF)C7tR+uWv4RN}1Y2^C#$Q4Osh_;5|9Mm!Yt;xlov8Tb-Psc6rQIEQfL zOe!aG0BIsen{pxtSWM*fNfSBXG*1IACvqg@WDNu{QKMzdcyuqL`*@l^TodW;*F6^% zOcw?E=pdLrJdjLM$A-@zQcX6|v&z#~Cl=;msR~tkrL0@rDIv78# zxfTjG7SY`=mm8vBG2N4t^@KoJY<4|pEiVX?tS&Jmsx(2sTtW&-%7TLsT+0R8aa5)OCYIv#6Def^s>`LC9ZjNvvEM=mKO?Pb@cw5daDLB1DohJCzc*7$Kl*g}7fR z?m}b!RaVQZtd>{Bx16|2ZO$*3A4DWUUxHBDFP9;d^ogYirBdRSBeIGWmw0G65q_l@ zrbJ|sX;U5=WGM2`upuNlT#)jL@q$Fs3Pj{fi^~=*J?}h1p(k+hH9y^5=Zn2#=A!3$ z<&}*6xZ&9In}$8VW!TBQ8vo1XG3@cZg2jt!k8zH1j(5&4^|Ka?= z`L*+q%j4?e>hBum8t0nkI?HvwYqe{w>ju|7t|weCxwgALbN%RQb^G1j-2>gnyC=9$ zbD!QuCBdK2Jz-G7*o3@<*$E31E=;IO*pzTb!lMa)Nq8gS(Sh8+ zjKH~p^1zjWYXY|g9uB+^_-Ek5z}~=tL`Pz3V!yc9EBI0H+u*?@M^ai+R?^6% zaY<8?&PXavT9I^d(iKT{N!KK8O}ZoLKKw74r<4AY^!KDUlHN=DB$&uuZ$u}oIko-*Yi^;Dh??~R2yf?Wag{1^jdZi3bIRXEJW>(6X zDT`9dQYup}PpM0}CgsMIJ5wG=c|7HXlz*hWlk!>0*C{`z9876Tb)*JUd!!zdIz08n z)QPE6Q|G3ho4O+P^3)Bfx2E2c`dI1E85|^d9M%>4VZoq@R#JF1;Xqdivb- zv(rn{m!(&xUz%Q*zB&Dl^!wAF!0RvRZ{YRs^aEXjT?)J0)8*wZZ;ku&xPOh?J?>CQ z3k5?Np<$tuLxrI;LrX(zLhC~}g&qh!8~S_bozQ2YpF*v<3Aw#;hvuG~J0 zdo=HV^8S^#GjDI+{(LPzIloW-u>73-y!_euCHc$qFUhaV-;#fK{^R*C1#cJZF8H}1Iw^5d?@7ZZoj56f((Fma zlgcMuK54_GTP8g?>G?^oP5N-sSCjTn=97bydruxV`Q*t{CeNR|Wb&%X)sr_(zJ2mT zlb@gb+T;%>@11;bifc;xl>SpjPYF$#KINP#WmDEnSvTdzDfdlzYRccHyffv~Dc?;w zT<9uHE6ge!S$IleVd4D3<%QwG&4qUtK3Vv3;f}&j3x6zZDe@QfC>mUJLQ#Iv=|zi+ zE-G4Ew6*Boq9=>$i*^)!R`gR*%T)i=9#aQR9XmB|>g=hDrdCd^ow{}E15=-$`md?G zrhYs1&@|Vyv}ygOjhuGsw4!NeOj|l_^|ZCqwobcm+SAisnfBha&!_!@|H2c%|KS-j zJ!g8s^f}WPO(} zLf#`c3ON4X9ji)4aehWlMvj2fEpkIVG!B@rL-9ZHSXBzJ398VJY?W>Q2XM4*@n+OF zd9&y%y!{8%JLC@kiH}vKlRz@^8(6$-ZWg_dYr$eK-{8-pV01n^pFxtrjGTz1fd4+o zlBfy2c0S7w=0vU+)ah(761)y;z}JNY+#+R{3TO~eQ4!x-<`8VNgc($ISiS^Gs_p6l zc69Z+lQhzZ9iuOTEb@_?+FO#@E|5_}w|%YWtdc4;yq|&6;XIB#zY&{4Z2ig_AslRKzVm za-Hxk5+b<~0I(t~NL(?p1Xv1d14JLwa!%5VtUr?1T*KHPUTIHgc<=4sX5cBPe~qQoJ4DAl59>)#Cqcx?m77lWHH9v zW1DG(|ACBv(r&JnR+0gsA_ckX{L_DEuIi#%p+e%84re&&KdF(p84m7O%#u(V@k^!^ z7fZndlZ&S2<228vL*<%hkL7lmObnX2leJF7?I$7j~dH!A8Q z+lZX>uGm`ajOy>LcVupiF=yxk4?_{>rp=00sX&y9a1cN#A889ZAOlGFS99&_R#5;c zx|%Pcar2mj<->Efm%CL@pLq3p)EgO{V7@>-U3`jcF{|cdOacg+XxZRa^hA`4zUr*X zp3q#CNN$)o#UD8aYZmJmB5Y37As+Nfx*f;S(^-`C^cjQ75W!G%3U18#M(-VQAx5}_ z8lJa3lqzE;*88qgHtXu7bvw7bSKKNCNcp<=GG}GsHFTq=;8OQ276sWM3c!qMMfyLY zAi{UBPu&8XToYtzt@AAZ4bKDq0oFN;w%Qr?+qYquD@LP_q_eoVA{YpAsZu zqA+N*LDDsSk>ETi*BTN9Qd8p0B#aWWClWT9N47EsliJlYMvLHAhZ$oZ6?I!XN+>l; zZ3>|nb)_GZ3<=5#M@LB)mF+yKbj1vE)O2k=km3>&lWuupG$T(0G2p~Jt{mJ9P)l$Y;X&`Abc=Z%%YNnc|B^gc&~(!lt=b(Q6G2Oir-S$I9Eb#XU)tj~!?tS4p@ z1<#1}j8Sgri9r);OhOiCz|eSnEYo1Yj<%k(3TS|=2JRc~7Jus8Rh1kEg$9g$NjbVq zC>a|m$Q)hL9wl7N4S@aUwP`u6vSy8gBz3xmpp7-Aup7)8sAmp9f_7e09k&r zqvlz*7}u^^*NiDmIT~>xS(|iV#;TDxlDl(q#jFcj8Sx4fElMF<v{J zJb`6`3f4z0^`|O{%uu*5Gz;F%{s=iDf{=zogIeWFdW1NrFc5VPN;7KL3iE#?HXCOP1ap%inHi?yJtL;=Iv zOKi89zQN>5taN1HsE#Gku7IJY;LYGlw_4gDr0-|Qgzz@vM#J(%In|7tZw!k!pz$kd z8RrR@;x{CL_?5cw-qf;e9~)}QN?p!_2877z_A8?_2uU_i_2Z&6y^3Uj8LLexNl2kR zILqW__}KN-^G@L7>&qIYB>-hhl2#6gOIp80TX6QpK9#et8on}G5-lrv%eztB zD2KYWCsw74O;@I_e>n~zP%0LKi7eC5p%~!+!P<>L3S_Mns$tc7XrPl0>Q*Ns4l-4G z>jY5W*!h;!4f8Qt31%4nNz8hZFPIRv2eE%_%$IsyX$e+UbCe4>43b(k?3XOnpzVc{ zoPuOzozxWlmw6X8gErO+P0JFABuj*Z!G`d(@>Ab+6ujz?YzjH(s|a=1CYve&A%ld0 zqz)5&G|*-6A+(y2kzr_AU?7hYol|>>9N*MG;*1(FzPS?|H<(UEh(Owe;MhPnYsuJI z!i-uvlFqT9DCZdfI&KT4f?|d)9cd@JvJ7LvG!{31iU>|@0+S>f8ze_W31sw5 zND56Ek`nqto{FTt+GDiO+iTcML>=@rt`+2|NNFVlU-+mVA((AyP}&g+fTCEID9F%D zg0Mb6N~G9;c#X75QJxKp5^Bvh%Y`4z4jO-2t*N3HUbIbj$ z$&itqofA)$R42ca)v1!?s`PJvq~z$#S0X4Fx=I*3ujHuEeF`-#8PGorpCY_Do@+8>Dd!&Q5d9f45v3WrqBD$A z`*70@?hG7d0{rxXE=Q}48la{rOQ*cBJdPnQsto9 zhIgTMJq;P?Kek8pc4ZkOp)_RZWwHf428HpQjj=4PLrNBbi0C!V#)e%A-m`I$EOg%W zm3sMWpNcr}>cNs^wh-(_e?%mHY?{Dw?;BWXi=s4mp0 z`x~JjC{edDU7m7$LXjk9;!z);(!UzS$TSS^B)Jfwk8ZC}8*I+77GY`BC&OopU21hd zMcRlMdo+i^4y8ww&rFb{1S`!(GSk;<(t1&5%$Z_pMEI&i>q!l| zLy?^aQ1T<}h6Z-FVI{)W#e6Iy84;``GElPh$ za;N&nNED?|U=HFm`pQg>9-+Z_t0v022F1=a=nyT5BvQy3JJSPU`B}QP9qA)-zx13# zfj>w`oc&v$k%xtqU5Gj-fB2QeJ}LjJeg9dC;^XSuIbR z-4JB~62VD$j`wKO9wD>Y3s*$e7PRh(-PU>;{u|Oi9W`u`BQx^9sbqtc?ilbp;w+H6Z4w44aty^-LgldT(1}f)@Mj2 zuiYa)6KZ7^;jf7sl0~=Fi_ervG7FYW+>k7~zg~QHBkwLQ3pXSS&e$V9Gv1O}#w^^B zESgs@KC?E-EMpdKNEYcBgJ&8T7Osv_TrWVwX#|PO!VSrSh4tcd%ubnQ%)$-HqRsW< z^Tb1|8H}I>_IKw26Ipz0jSMay@EWS4bvn_6Q6q<$9EL zq!B8YQl?Z#8liG2WlGsJvPtqGrEK8>)!=o7#E?=Z44X#QOAIMx!mw%NN{Jz*Oc*we ztd$s2%7kIl$U2E3rA!z$jjY=vBo+dUiIhzv;FO@2BneaYG$c~&l05>$lBC2XsE(o2 zup}vQ6^!d-E0%3bTm=KYrJN;6iK}2hFO;(+DRC8yizS97Nr|gqOpzFtBqgqbF|oEw$uYBWYYnaQ5{^T@4+pkShJxo6Qi1}Zb(4b5((+% zH!v(*9b@JmMpwpL_2QHKkdX?>lJjIfOYl!c7H){Lj4iDfAA@JgPZ|+MDkLE66qz5M zZj@WNI!2L%jM-^mShzaIWfF3tKF5)7F>rN^{5_1WkZ#E&;d)U*W#YQE9x%hM>t&g^ zn>tt^G3*}Q#Mmk^q+JsY!w;DlcSsDoM>jFx7wW->v}>Yai~$n^^CMwMTO|x*444?g z*BW+B7&g1UTGEhqO&EqBGHGm(7}Bl@!x#f5hL{yXx~&q1F$PSGwR;4oA;1_?9oRKm zA#B4#5FTAr77{78xL%|UNs_I%!w?=Uq|A+mM>lCe9+WmDY0RTkrndrXptK=LL+T1f zoy0ICX-HkcfaWM?NYaqHf^oLQFeGV6UBN(PLpei|hSU{|3nYf&Aq=T27>Iq89E2rt zm9mSr3B5?2u({g=zMiYtBQzyFNC(@ zaNQ+oNRLvF0-N_Kml)Ec5QfeBTqrT5MIp zg)nU1N5_yJg)nU1=N?HTB-$bjoA-fCLnyc8BxOov^u`5wgz#a>N#ar&y>SWqQ{^Oa zLjchm*X5FiB_}D<5J2?CrN;}FoFr}tAbR7fmNYCmN!$=X^u{$`VpwvLxFLY(jq6g0 zVaZA2h5({BF3fBpd{}alxKu`OTzVWQWl9ASH-r?uafw-yd`rE^(oLp91_t;b?DkTp zvJfD+bd2^=S1`i$KqKEmpL7|xCXM-fP)fd~9-nj>xF&{}t;wU;3%F&;CdM@cA>U$P z7`P^ePNTilsa6Peafun9d`rE^T9#~LtRYCqCUu2Isl;e6bp->Sv>r6<{v%}Y0o@XY zlseHfG-P7vbB~lHVHg@RG47N!q$CN$CiPVkLrR@6Y*H8A!H_y(7#cELxk}QIQYQ?X z)b%+~N}Vta4Vg6Vmo%i*3B%A(dknk(Ffs0xG(tB2VPYTxqO>7NG$U}SEJPW`^jXr7 zq#<<$LyYW@DM>@>3I^msIYW|$)D;X^Fy#zM8d6s<^tsrOq#<<$L!ZA4Ng7gDFtDbj zoFPd=>I#P5m-e=HeM9cHR3HMRes82{+g zpE>*a(~o#;Q2*3e8r^V|H9!R50#xle{Ou?2ZpXFRGaA?M0o{Ey9$)tX!zs7fLy&hr z`Sx(FncBd%ZwTs@j2au*=nZbYQY51n{@=HgYaWNkArf)9UOV1-cgK$HJI0JTZp@gG zqsELHNhvDX@$Ngl-rBzXowv7d$H&|6Y~Ma+^ym>IM~}wG$Px5L7ahjA+?tU6?Inr=9>-S#Fmzp*0v~*X%45$1=JwE`uqHN5#|I1M+wK(7W8ku+4%tLv4BfIHgU+|m|}Rj0>VS~Rc6$rXPX3Fd;c7c#DEJrw}Cb zcoMtx%E}&vF6{U)>9f5*>~CsfL?PNr>{X33(}bYIP2x&}7ICXVA|4dftrjDheZhzu zI>dN#g9#+t<*RsOQ?+yGQ1-zG)sPqSda9ite87edt>#UQ)&8bNB zTT;zx%&fJ=YgUU?`C_rA1N(mb>SHjR&e_0>^yGjCPJ?q#AUQo_AXxUr*vDV}xbHwy zEEb{km*4-~&=`vm)9^Hn4L^VX<;M(Fdv{F@dO0VyLBShL?b>@_4I03+G(n$($EX1w zYdhS~&}_bN%X9^M4L$C7bffj~p~hCxoz})fhg;FD7<0A`Py_SxT?2+Aa75rd03Em>wCVXH_q{0k`sYheIFcM;r5I(4* z7{vIbf1$vjyYAhn99H4?h)*}yoN)d4V6kv2D$>;ksxkc06%J#f;=CGifGY;P2Jb4g z6eKbz95Q6cfIjJ7ULA}z{`kcwJ9qAs6=Pv72AV-ZB^E{bCtv*76srz$Z+f2rs3qFx z99&0e@X|#+NH*aHWhxCw1cNjPC!5U~q{Aq6$>t)%hLR^RT)ZHU3`#mI<%0)@6P-@X zOv0PDwYIi~9lLOa6On{C$oD&d5GKr&!GrazUIl|SW!ElveX6KeKwURx3-XO{+)ih- zsRfSP*$TsQx;$EIYpgmTTw@F74)U~^1X^1)j|-DPs}m;L+R_wtI?0PbMJ`uFIBt5YUJg+&d8yX+qH9CUK=fi@4Pw5f2LLR*R7;m&Xlv z%Ojr8KKtxbdR2S+XVEJwD+1xQwKg}gre?V5Sj2-e0ju^1Ow@=#at^246EPCuWCO$u zf?>UhI@k@73nv>5FoR&&C7Y-NJ03S;1-Sn@D1*i7AnX7JOEc!Vrl#gLFmlTdxBP7m2da%-JgC8D+Ec6qaqC`7!w;jc<{jf z{rdG|c=sQO9GW0l9br|&Q=`tupQ1MTc{445I;lviN!ub95%hIh5syK<#t+T9e1YVy zeHgEGqX}3;tTsKyzW9FMfx|6g#I+nguj@lO?u1}!w>}J%zWcf1U^7tyWl+KN5sNh+Z20-RFKWVxv8ICuo5-c%Vtid9 zkmzEXCn3SZtCKi=!hwlGk^@?l$g*Y)oH~mg-hZGmR_oV6f=l?21N#pP$H+8aV!+F5 z13Gcy1oN1Odjp9+(2i6)G21t`ReJ^x%IxCD$#m787;8E7!=8_JRXd$tLb7QH#%}!%c0GWCOqqY7q$$G(3(-vO${}#3D|I z2aVLivpF;)vNonAl+df)WQCHUL%O>7pnhEfP(&=&a^Q#EAH`a}-?{Vqme@zTe>ec; zLAQY}{RVMY*C9jcrswrKRGlj5b@e(_9RjY#JCdreIfPjls|vIQi^L6yNGf7*TB(R> zS6_=ns5(5q`0R=d(*evRC+XJ_sjj4Loi;P6N%D1Knr`Rm#WZazV{e61L@19in~7ka zjUF>5oGik41l3Fg@@)HCRmt1m(sMfgTh+;<$LN)G^f6(VgP^gR)aGV~&*zLWM_U`? zt%nb{x!o8wv|4B*WL}f%cDEfq+{#&-!@*)spU=_UT;qhxX=#bn^bwx`L^Y2GaWGsh zC8EqB>96Y}NwtB+rY6L{@Y$5mSeqojma@%QNJLUwTK0eU#it)^hc_QRw11y&SffN} z839KGnrU^QRu zM);yPQb+3tqKELNUj{78#au9&BSuIxoVuAM;MLVxE$yH;$FNxH-L6f0=ooClB zNvTx4H-RN={1h#;V;hZ&FfnuYp_LAMf4w>#vO*NFVS0W#7I=z7^QN z{POcxA6L5vW~8O2`eIw%F<(kbYR14CgjWe^$zJUySFG{ZpAXT*cIf9{8)G-QxHl=) zAFHW$Y2KtXe{3rd)04f98duxlUw%Bux4QVjANMu3!2vpb2|j1FYy0+{Un9hKxq>~1 zj2;c=dicQM)*AUy?HWCLNKZufu9k+ccW&QKl>$B|RrX^_sYO^%#c&lwqn1ee;eA^V z9X!-pi+G+W!c7o~T2Q3YD%aaPc73zIHR5^`mG-yRxtxhWJ1!D%CiWOSY9t#uYH*K4 zXT)_JswFz>T&?@j#G4UU>w#}}?RXm&peF(nz$BT7PL{|K86rnynIsc&NwRf)dXvJi zWQT{QB_!=QsM+4o7z_qpj@)!K1}J79nzeA#@V~3*qKv@6og5-?L?K1m(OCF6D$@aC zX@IzuvUG!8W2;2J>tz_|zz8Qu%CfGYRD-z7{iUz2IOq3_|o+Zu(%fB8ef3NSa5Z7>QJ!mKPXo7&YjAHOi|TpeI%W=v5R0 zMMDO|7h6Iaew~4)!|7-hRY5OGK9L+HTsN@RvFJL2x5*+@fJ4?W%!p6G5qhnI-(qd8 zSe?QLgt6x(QBZB2N1+t<=rp1mog_dW?nQFKath^GeSxc36upt8V>L{x=>;@{Eig;H zz*;vdYVj~FoJmZzetZ%PEW-`O!v1I*)J;V(ggzVr3JG0OCl=u@v4*1}tjWVz)y86L zS?mVns`0)<6x6I^d<(-rl!|R(u?^(yX!510^C}iY^n>oxAFpAtt5AqQ(hvu&<~A_= z!!0cAfC`EmKmi}?1YxvCX=q2*z?5C%CA$!oM}~s7By=6L%Voo0fZC>oKedL)6(Cyf z zF-|*ZZL!)a2b>@e^R_6-0`Vm7Vr2-IioWw|Kje$}5!^{89Mnbx%7vcbJtNqf!{`7I zzApv~fw_u3TXX>akt|x%AP6iPgyO=f_@E&sFN_PaTGCjU(L@qZT8WiIKq-Gq3H8v{+zKC5!RguNC z?T`S-jYc=!aYfM^lc!h`5x}%BAVi8rzY~Sg7?=XAm19Yaj4XqIBiKF$1#pBR?~Gv? zjf^Z?+qVz?=^q-x-lllN>BZ;!{JL*n!{Nrp#$TJ7ni>xs+@BZ>20U)JI}vY>&tFXz zMD`j%2eH86+;%fW&F%;@pCFyGSxyV`4j zJdyR#G~@;wrIii&&CSp>dJh|RlgOdUjbzyR&H|cb1cs*qIvjGXR1Uca#MpFICyMcb zJp&+O@r=+wTxiRr42z0_@@8A>8_^CxvD)M&>>tCfNkOo0TAT*S$ml}LWJk5h=ncXU zWFcZF_9#7JxHl3KEMYxwgHPn^Xww6MXWM32Z5R#!=7`ir+B~H_AeavR;F590=ljAW z8SO!My+R;SXpYg&MPs87nT7}ru}1VNVnNet!vly_@JKxq7uTqtxI~u(>%a&PAa)4> zl|==ZAaZ2NvNOaBrk$%P&RR>io5|9}^7dL^3=y!r*7qjwBrn9E*p9{0YCPW&>MsaSRqHFt!=aq zL)Cb78l;7NJ+Ie^$P@cK)CNafOM6&ohdM(%lgC2W)(h`fP1>a$S)z$I<^&i~zf2f~ zunoF_f?I2!=h(dP$StQfOy^BaWQAy6tbD?>lGz5Q3eSt60aMIKAw=>;LIrfkn_5J> zB29T&iY6g;OiZX42E4{cn!p>y*Z|Z=1C^l^hLwtL3OEH&)Cc6Tp{S1tAsZ?U(~KzK z6xvi%`39N`s*w=B2F;1(E6rO-2rxhq!XzY`;NbTJ6gOE11V9b5U4%%H%c|?uN}Z*) zAOO;|<`A2Ev`mCNXa!8G4g66Gy!M0)0Hkd~S}39-nT?=9!2|Jy`H-MA(}f`gNQ6)^ zDg{*1^ns?62#Hq0KAoy;r>-A_%V3u@_^1iNSTqWih!$ylKtfWG5LPDgghcId1U`^7 zb%rD!1ryL&q$-&RVM+li;AEIJS>PSh1eMTS0|cWhG{$6OHQ45$fkP#_oFY|NN29$O zvQk8Q5KF8Y1_~*=2?s6Kk);~Fg=xV;&@;3W!McW;!Kw=Ntgwe9i!~K3xTG115C$Y! z6x74$UTi(8f=Bd|6e!e)Z+$4tks1&h(m@me0F^eX&<_4!pmOi7MpzNrMh9TF`fgth zSx_BxA|wP3#YP*(h`2;Zi+w~9jSyXQg!+sPEs21L1zn?H0}zyfI}#2O1Rx0s1WYp2 zcdF_lG-?LCQ=UGtghdrFijC74f-EAAcNiOWpd!obHpOXus&S1)7d@XDXFzglc+A1t75N`=~5s5+G zYZJkmVKfkDbrM`Fb)3rJAV4AsXnG4Yzha^w{|f|=F&dZ5B%>WzHK`Gy8qKA(0l~av z22!Db3lSr}e*&X$x(Mq~3Frci)JTHF8@;4*T9!c@uy-N~IPe56B3IO|twBi*#SK)y z8kPdEjH~N}m#v{F10}QsmsrBkW7yV1M-Vg-NsxkxlS32*aixUeIhQVmDxIT@$-1wj6aI0X{*lP`=r?n9=rDDwp z5WeKWwidocguNjE6Iw?gf~6)YhPAd)G>-`%-js;;gFr}Dq!vg)ICfgnl9weAu3G8o zlVpyDCBBkDzv0(~?NUW)3=y(o_(JH1X8E?Z8o!`IDuiy)94%%cO{}$?*oZ^?MPD7F zX#Kd%zL}Ys{pgk{zWT`O*AL0Qefx-uiu>UuZun+^V3`usS5)kmgj%?$90h$d`_ir% z#6eqJ#1LKJgD$Y*LZg6+&=`h}$(*JWng-+tdW20m;<|3!UR+<}kFLFYzx?t`DnRy| zZ@$^P_bXBQH9_%~fUm#)YVY1}zxhVy_a=!d0v}a?h#wo$l7?CglO5~(bP(P~jBT1r zAU|@d`lcbQX}yTyF_LL-j;0%2;SAhpo~Pl1ot;;zLSjfl$v@&=GU$BrM27-m8-HKx&MY#rE%)rjr0wPYWdaW%e%#qrE!UU zVYyl$>qTXawvu5-81INsAQf0HmDdd%pbud@U4_M!v(O?T<4e^b;SdxMgqHov~4W}>>#==heWi*IJD7+Lc zn@ezquek}2_mD)I!)aL2H)ECHraCk;XyYT?n^-_h4Y4COfCk`~9B|rBVh(oUaDlzB*p+3p0Hs*@krV8Btf zCRNT3xYJ0csRRKVI)I6v963B=sw~8vcDHG?MxzjS7?mlEs|7)z2~QTQMFB+-wP@39 z4F4>QCLM=Ef0&~cY7k>qtbE9}(K@08^s}|K8B;vuC@z}0BoqXAkeY!20KzVT!&QUX z1;HN*F(SyhQOJaq1EP@_0!HKkfcqEYg_gXCl^E58Z=~<6QBFOKt%aj>I$T~*z}^Hj zTbqQss4W&pAW2~-ZPv9lMI&;BfL>mUw^$~KJ(Ox7T_e_16eYqJkAu#d8xP~TCp>2* zE^McX4K)07F|XH$N2|Qv1Rsq)+LQC+pMeGZ`sHiHK2;Lw9V3lKVbh_*O@|}n@BrOm zIrLE4NUuh;fRRcslu#1ig&)R~ab7u)G1AeTXb{aMAxSJC>2*7ij*Rp9yk3`^1S;Mh zH#osclHut|dR$Q`47!OOJap(#69xl%Mb_BZc;LW(jKH=eAd@!!zxKXu$ByG%Zw+UL zid^%THU=)vq*6y&YHElyXvd|@cn=NRjV5cg%7~=j;~X5 z`)rTp3K}QO68jptNt5s70DoZ{%;Tp}xD=CU>{LJgT{7F}ktaOi_&RHg(!wEJ@^cLS_a8FmW2ov8v;h@j3rnh?VvW~+$iCddc%Fd*NpMj? zg;k?()=Ob#u=+pHo*AUXo_X-|i%;l^3InOYQ#jKT{5wTt+=pRGxV?GNW;#HL9%m>X z?xDhn-~m#*NiIBsyZr7vn2D2|+gQy8X`--+T{%dE;WC(bfeumJyvP&|UcrVBurv;D zy9g;`6&EJ>9qL4&9sW;Rk*ONmehs^g7?~xt$41!mb5=q43EcnqHvWdxQPViRbDorn zZvJoxDDa=p4w-N~efs$oOfunm{F$Aj;a)&9r?4iP(1GyK&m=_vqwr_;jBWrwc(eyY z0EuG9m84?lPQZMGc=Q?uf*+C)et=H5Uy_9I6WKCWldn_2qyqc~3K}egN3uZhfc4>X zT!G(g#@Go_2D=mx!NBuRFQI#x%44Jg@c2K`?sy4jtB}?V>me!x_PBf`Wn{0|3Vs6(tcy1>v4QG5qsGbX_ZE@HUo zl?afM(6Y#5G82bHaE5F!L8bN?wg)|X_yjEf*?XM4l6qy32m8;y{q_-tjMDc0#X}g- zXHu`7J$`!dDt5?XThHSs;Mp&LfA1+4pbxOe;_(*;j~}6+e2cS0Bua;vO%uZj!W8$a zkaB2f2YZOn%d z0V3md5HT|OeL|qLPj9~hky9>MQGA9-OQm8m0~O#9f75#K3gcr&dkZ8k<_Wd1^0J5B z5D)l+tX)Q~6P|z$}@o3a6M%;1{!8u2Et-ft61Fy2W3&8GLZJb7xyC~pmWnZ_tQwvN025um6|c9<`gdKLHIdB?Gu>+C#3j` zc{ip$V9uk<1c4tg%~PZxjQ%KuVU0(UP@{5NZhZwGV!;_qh9)k-Ebzl?MV+U|zr)8T zKeP;#$_8uv0T5(Ut>vCLn;<{G4#}Y$L}-j&tW(h3P_4mY6p0s?^AwqD&19Ynh$I^3 zmPkV`@)|@h{IKSFdh)wBm~X-yVdzYj**3ZJ4JMf6FUgF_Wn5_e{1-o`Z8M?ftYt3y z`Z38kz_x3=!^aTlyJQWVh8M_-jl&t)76&DX6_!lViqPg~x=3}K3;2YNhS3>*Z~+?N z4=%fn?OL!znj9GBvVMpI!sa*^ZR3XmMJf$#!A8Vnkkm=l09FM3@)s(gzhkCCmw>%Q z5XBBbPGn$=a8jlYKAYtm!c` zpl&!GN>Kg=F@O=Ta}^h2MnvF0$zG7m$SXf!?(~f56Py{l6eT5*qfB$K3i;#`G8@T# z;CA1E18CM{<1g3=Q|Z&wFW}Tr&8=6UWoB2LaUX%jS%8M=?;X8O+QEt3wSf}a6XqsV zm<07>6yccZ1MdYk!{v-;$5MScxA)&fOE)Gjl<)%59^94=Y&<*PR)C~R|+^|$a)=CTsWP)F2Lm^bY2KcP_2y!;?QQ|@F# zegZ*~d(gxe{Du8|ycB}Figqz0pJR_5tx2|%;{;p!FSvv$H_L*DlASi>Bq$VqHL>UBF6!MO=X4^ysH# z7AXrHV5yGtZ?FiCAHAwc7))2-{TLb+u_Eh(ad1^erid7oO~8R3G57Hv=GOR7Cg51V zU}uyY{<=wWg&YuZ4^i^NuwcZ=5G|UPR z(-5%$63Tc3MGP{&_yS#-K7kB^*WJ>s*Dy~0kCTxQ0xI`M(-?u|Xtwuzq*{_KpehB&$ufcc_#W6f;f_8J~Vao6QV~q_(!^;gMtP?VfddMf^ptqJ$+B>Cy>%Vh#i=YPVh#_VV^V|3G2Iq2dGPInhmRk@gCBq~ z=*A}TddL$~GryO5LOHZC)Pmo4Dm>9w!vTa~Y@C?WIvzbjCF~WMvC~xd9v;63Dv_iNoQ^>}WS#O5g*`w42fZuln9wOm zA9)#c$21)qkFo2KEx>0myOBRG$QcZb_5qG46Vu<1U~@{C7ia)dU9b=Q$gylf#H5tx zBPo4E1wUjdfieEp-+)lai1Zu#LNJ{K~% zK2OA4ikH9XbSP8Kp&TrK{pj4``B$u5fE5H}%-|V*>e!&c&DjpU0~R2y}~x#X=;U3!gJj#;XVgp7sGr4#}lt)D(ahY#>67=I7+DlCdC5sdb39i!D_QY%e~ts+D+j(}Qu=yT?-0Lere z|09$mvLJVTlN<^Nq)G{ea7W~rM;A~#fgj>j=Iw-d^r4U&fx;M&WFyhUbW+5Qi}8v& z1L}9+9Bi$@WNYsb-+`0fkU>e+gVr!R{rnPZADk&F%7guzFvriYfAi1y?`!$_)1UCw zsXyV*Z@&KKn}3!c`TU!&zWKl3eD&4WI4H)t0`u}`&41(pr zj$VV0;Rl1^4@V%|w74l3C&-WFpD-QjjRbk;`jsn}FTZ#By?60*<^3yIF}!m5%DZpB z_4ZHSdh6Y{-;s|mUwQAn_ui2=avuJC>#d)@_0Bt&-+%wgrJw!mXP4i<^6oqDyz}vJi;@ z{~kYjf)om`QmXt`!iV@gO!E;U%EL>bOk9d{IPZP<2wx3Azha@-m;)r_a#|!+h~JnX z-7zcWo>O3wVuqOQO(431Y9ZF(;4EMbWgyb3#sQ%ul(Y_C{Q;)+_>R#9l)>eYP!%{4 zZ!VD>g`!^l!NFq`1Tfb}w)hA&3~W-vD(?Ym6c^5afxHwS-g*r|30uMt_!+GcI1yXV z9ls9=(Njn%SinP8f~{|;(P0oW0XjyainGsQ?+0w2h3Eaf$4}4`N|j~#f@|a~Dxj|T zq$qhj#-_PjbFd{dVt!GL7?{cKV+kIK6zm4>MyG=r;c`T=C(mwTm30P30v7&;zd(-g zA^gDSo&tnSIc~oK_L8%}uXkkTilPF7;+l1B8Ne$ zqC2D`e~UIC(*wjn=o|EeQwWej^;`Ud&!aJ3ETr`ak~u+Qh5ujKhh_O=xif82qAKsaL3VqDpz|A6FYu@|y7dZOq6}>63-kcZruI62n@gMGJ zD%YL-Pi^U!Jokh8Ts{>*?tZGjPyUzmdkJUrLCbvJj68+?Dfx z%+G&_)_KUD-Qjm-<~@c#eO+#JYxf7|-GuF}U8P=Ph`qQc2;?W=DUZ?iV z5xv1UA9s>HZ-fJ|QDD>P-lcdM4J(;e(_G;5s)^ZVPs?za`zArpq(&G{#2S)U*)!?EW!C~CV;Idy}^<=)tK zm-qG&u_H831<9>~WypRHVIK#M^3Jj@Ov92L)q`p7)g?0@^iAdEia+%BXf#N7N?bbohe!p(H z>DfQ;Ik(@l-P_Dz+`Y5=fq$Aznok?khn(g21Lu%rME*D%wM(~IKgLn}y~6#NO5Z!x z>ymBK@S;aUG~p<)8d|$H*^ZcHG_AI?O3NHidbaV$c*ZU7*WHi>0gl`x&-1_HGZ;}C$^y8Wp0pYM|w>YlygA@Dl zvUi#o|9S3I9OuE#aXwq%)6S#7qXB!?aM6NXo%c{9luiPn=8YjAdO_#;lKFEy^+6fR zl^teh%s{6_Zsqn-9Sz=*Zsl5DTJlahnzbBEpc=6!w&d4l+PsYCIQGMA{!9SIh+p{hnOi`$N41#DLOV{ZLb1XO(Pn*!VR~)rgbEKBg z2GabP(NG-A=M)^fKe>ed@ABg3WO02;RjCCWMe5qnLOerPed>p7^@7Nlzi#XNMKQ#u zv)|s*o_@r$FUIG8x&5DYHYIXMtsLzm$?Hm&*WiD=s14*=;W2-x5&ks!B_`o97@Z2r ztS60Gue5_^wfRv@l>Cv8nN6!*_O&dL&vAlh^>I5zPD03jEUbaO@>!s+QM)wjm8qoJ zdN7~rq4Hqk#h#-uKxfUwiJIH>m1E^P5X|cRHzba zCQ*O#?()yhC9?WznYAjVh*&`YHlLLYK#wS);E?(y`NW7%t^qd$pXgO-O8s%?s@!R5 z)H#SJV}wMXG*1Sb0J{ zg5Kan3;+=)#O^#XBd)I9Wk>J?Ivb}*p!bMaq#Yp$KJ(6PtGG&@+>@TSh{9IL8Yci;)@#9@eBz!o;p|9lXL0{%cKO6&xmL{- zt@Ip6Qjb!81l4Pkij_xYB#KoVi29z`LoUA}v;EWtscMJ%t1Q49%tyHx(bTG~WHDiV zW`IRDN4s=oxF%$5KjNbWlcxNbS}heQ|HJ~5&)Jp9HV~uyN{<6_2`*0ls8@9^`d5CH z9MeY$8%)@m<;NdT=kEnZnOz{4h!5Zp|3mY+%RKX~IG|_7+uQ$L&r0(lr5V^R|EKb* z`H-Ih-ex;lwOTySP{8~<^)5)QHpOQ#!ns@eP--D z@+h+<#oOo29E%-RydK{SuOIB|x3^`@rA(5bcT%dhOoqxQrr>>frl-rf))ca76{Tq^ zd6KnnH9T#9_|CpevX-eZ~Hvt;a(~m{#baUjPfoY zJXlv0YdJk_^wS@@0otX;oYZ>cWHR-ZE6sDM*R1+2ow}8FkNw4(O+3_YRei?u1|zVn z($Qi!wX*JYjz+CFjxA!KL|&u#X>%=$b*ov*@2>4L&yl$=1eBfBsi?`^C1<5fihAs# zII9|DVxHD9j#ZDnlAJs;QI|iSTFM61V>2&rU%9y5#YfreBD^{`a{8c^J5y#?1}S;= zWm?s^F5=;c{7Us%`8a67?9{wE*ADx%{zE(LJM)~ssK2TPoYg$`TW(?AY)6ZO9uMUx zb1v|^^(jMEMl=m|M|J#(UBxG}**$&TyJoL< z?e#v-y78sFyRmm)Uhkdi`+NF+@0z~fyJqk2+WWg`7p>#AbkYl6rI)?C+6!Lq&gMM6 zcb^Dnvp_fo+YQ4UsO){9vfi&*@7JvNYu5WU^iFhvdI4HRd3O>@l~qEi61^8Ft@n{A z`(&x8u1!F-Ni>utHLIwmXmkM8Y@HfiyUcbBUF{9i>}-)YoW1O};sraV(Sk8Hg)WN^ zOR=xV1m!cZoJN#&`fitDrm*nxIU(1`e8bL7 zelEyi6?(jux5nHQ^&~E#l@ycJuS%VE^ML)xA%0854;v&1!GDDBg87uS40 zmMkZ!v5b|&J@*OEIHFJE=I!-evxj5a0SvUs%nmjo`xVANh80)y;Li|nYUnz_3 zm(~!{CGECHcEUNxpxa0l;glS8qih(tzq84=LaGv@-+s>_m@&X%-0 z5>cKRo(Zzr{tB6G58<*}-5G>CB+H17T|0aM5Eee1yj%<%sdJ-#UiG ze~2Gvr2n1M#Z~cxos$P*Pw5v+R3gh*Gucmeksp?j!-#RO%OCEI=&Rr6d@Xomv9lW$ zzy_KXrd3$ahSC}_rDAKrSvIsWgki6p_%#)2R;An+QlGVXO-NVtC-gi%zwp(BRErd? zRC@?EHrCLJ+6C7NG6WOG47=yq%U0^L_*Gq32I%vEC>iz((HT9c_b z$}#0N2C+4qIZx4bAr@NE6y970lVT}Xh1MpQj1eq~VEmMW%coCg-v>;!O=}jGR^+!Y zrv|2%IyA;E<4i+dO)eO3>l@4IBKCMz*{z5M!eA+h$5{ldnL$}<4V9y^!tTs!pQpX7 zW7&OsQdC6cj4GsQvxbNTUFl{qUfrBW{`c?m-ue?h@|BbRXsmg8>&pBE1~Onji6gjv*8(PAMLqT3+)+d_YRM_y(~GVzKGMvF9Jz5I~*NxhGvUp+^LatXMu~5 z%zNX_R0PR#J2}@VS*!;^A%o~S#kqF;Uk`#I8LWatZBg@AuW6f~V5B@+f1j$VF98svRN6)pp3{9yMZLUbsVBb*3q^d<<}yvKt86?B3~}8IekynG;HSw&j!cj zJX=|NfjXfr?P?)f^wrO`gDBDe2yGWA^9a%Qj8@z#eb;jt8$r3QVR>I;_d&Kgpb}bT z9$#~-^Gk_Q#s*%|&--%ZeyP?N$I7qfyoQ|?43lG1Oyz2Jhke(oX|q|^sA;ma$y8I{ z98}xjK6Sfep-cZ$W$=Vv&Ws z*cpA*SK4_=%^xB`w5U9y=*lc6 z!^WKLrbIfyr7h6fwxD5^Ejf8E7C$BKt`yNM3Zxk9NLq4JYe>9TBkf90eW3T-+}d|- z=dMHNUc;Cy&nz*rl$Z(OOlB!Ja*HxO`W&3WpYMn+S#1P(C0}u$#WHTVvpK) zo)--@cx2u7!n*k?tk#I)tFYT{X{Z%#*{ z|CuWX`aJ?eUnMq`+%T4~tqOyBKl|1`V}*S6KFd8RQ#eTPm+Bd>FJZrs{FZ6?3>slY;z0P6sy)a^R#nQRsSum?rbg5?EAszV;G28*g9M zXEpzJU+7}0c}S0k?rNVHufwtOs$yanBdnp{uC{@#VOKjLw-*sx%Hcb9*2zyxt{7`) z4SA(|UN`KlfoZ*Qv1{{dv@h`;VD;VBl-A-LHOWcO8@>LcybbQKnm^vpC2k6jWjfS* zj+Z%H|6WtcMQ5pgXqY^2jG2dXgd9iD{S~I9OFgf#W|HkAS{v4+(}|cvo@SrEvr90V&xk#fP3)lzS7x-^Mz%3$SHj@l(9gW zEuR%!WrWaJ%yCR#ArINwY+xF~j_eeRW8k~cphdCK-?|&YKWJa(QFDzPUb`#q_}CXQ zM*7-lZZ6_hkH`1QBqXoAuzlG$s^R(Ws(S3%i(=~}W3s?@rEX!Qhjo-IMVbZWvqP~S z)Hm#jTq99wKG>F9)O)8R2>V^^?;{bU7+N4%CL3N0qET9*Z>RfJm9>!}n;Ddid!F*< zC`5&w?4Fj)a_a~&B#nsq&e1Lv=itI}7I=?IrN#h#PKr*!&>O(ANqO|xZ5APGxh#n# zd38fa$-AXbNU>8Zeqk|HacaPRzOQmB{A(xdY0bK07G|6{?g^muMXmcvd}*uz6p{ls zYEDt-8oi#Ep&G#kYQ6AX%Mjg~HGZYO8#%l0m`HbZs`H;NW0m_iwcp-7qc#(qn42Zc zHkK6Sqwk2V8Z|tFr+!pst(`+5Cz~hLvsGPnNjkHdB~Z;Jj=n#3Z zjNf#fR>=72uok>lpi&O0SJvv4;V;S?{X)^Qt*m=jU zj#-U!UISg7A-)JQ^VqO`+s`>@F#}dHR5b+sxi1dX?VOdN;dz%4!OA&~{uNSf-Debm zrm`m;#fqpbi_KAOTRJf_T}3+KWowg4b1s<6D$jB(aw0kj2E1A|5P#ts%#fXGLHP@wlmSNwHybx(d&3Mgq4R+vj5E<&l9pZfAe$3w@RDa?ZSq@_C7= zZ?EcZ`}}yDwtUujDqp)1n>Ow&Y&*-zCfjzl|88ykJUK=Tc8p*0)Q!C2twHl9I+eVbEw^vrXBclq*^6_e_)~3p631sF~qJ7wx z=EJFe0)BwqYc5__PR402j~`wyKC#CLL_<%QeB zl(eYa+2E(OVhc-KJo5>;?J&lwCb2_0o{Fs%gD|@(v&!V9;$`qzxBT9jbf!tqe3N%( zUfGH{8$^(7t23wH9Fj^ab!~iINW#xHs~uzPn$L%8(t*&j#gwa*Gi;wJC6+4eCSpsL zJ%!zn(QBI>iYSgD*(%yWoBDZ?!46u?1Prx}Sf6#JVF-(kk$Gh0!xn!-2W} z$RAULEqyuZM&yKc<`QvZzpCjdVWLb@-F(;%ssgjV* z#TxUgaF!f7_lX_3 z=hwJI?OIq6&iELg`WgJ&TgNL~m$q)yZJ~|U8FEB0bi%Ts?y=&rUz? zKmO&HI!+!(i;yNz^E$PrRMHLt^Y1Ye2`5hbEaA<3n14rIV>-8bH5z7m{&?Ra*Kv$)m0Oj|W>wG3c>}%gUDF69(S(Tl zylu|UMcSP!Dp@*q$1HkUcx8D-^6=Crzt6__LUehJNZ+^SuQm=?xJgWHy=GL)tvTyL zpU|MN!zz6`2W0Dtt~K{7;Sgq192GcRGEw$z7{tGNF|@|p_I2xXc{$)pa<|q9G-tLI z3mKtE4Os;9LgcB&DQ?-+1J8h03unNN>f>pT)J=335TDUG^$O`opW3}8b(IX`dd3MIScs0bWl2?i^Mf~*HrCW&hc$QpAI9XrMVwP_G=@XGMh|!fJsFy0&P1_ zjkJFBO-wek&LVNA@?rMIIagnHQ07w}JtP*?m%a^U(*6M`mJ{gzLAnSR6iK`?;39PC;TF&Vs(2*BCR1#xMQpDp%LYw6I zwZx=9&hy=FGK&y7%_$eLo+0akRKrpzVYyPk?EPrLc$~H|*n-LiS9Br( zHKo?7nsPQWi8^<9R-v8|))97S`9oI^$)okWB)!m+T2q?$h@52ZK>5`9gmG-3>&&5T zgn#Wp?i(*H79 zRp<92?U9W}T==SaYdv#xjrX=Fw$HJwIKvw0lvAd_L1ah6|$W^6*q)xw`cG#NA)O~DGYp{4=irmgP zRePmQa#V?-9F^iqJEXo>!=Vx#lIpC;V6I^qv>#ZhDG8s&dRiF z5{mJ;4+jRbJ+)zcYFcth7FfckbUtH!t0dJ%^W4Ba!h3gH3yF;E)%BP%F`1|Jj9iiE zn3(reIw|B)A#(ZHd1*%C@wIEkM5H>gTq$Z~o+CltWMX9S_xaKWSN#ns^Dz{#nCRZ8D zctv)sCW~+S=z1giQUO(z^kHlXSG)AIwyiW z!opNlg#{Xakb-#2c&c5=c~p1i$x_r`xb zSs)jT91>epuPB%)$!&37Gk%070ghDVEty4@-;B$bbtFD39-D)NPleY`%#VUIr zw(FQx068?E_P|_5-lHXb4ZTx~B9%YN%wd{TL^|LZ%4K}Kb?I=2CtFxDi^N_|xgH)W z6;+IFnzXFii>RsBkCB>6PooE-3#Iq$pYZLAKgvk(x4vQ*C}g5*Z$QPrl_&b4vtnigc}|duVAb($@AMb!l&P>05hut-WQmAzCqOxNq&- z_pYtk8m_5i_O4m`ch_tkcdh-q*8V!IMXbrK5ABQf?0=xlD%`L<7kvMimD04l9W^dx z<|Tc%k+q?RGCMK$P1&2}OU4zc`j+A-waonK0K9@qFaG<Kzxdbt zzy9@aKKj+i2fzB&{MYjG(OYl6@uU3@KfL;zkN(a3`)|HE|4=^u&71P_;niRN-M`!a z=%c^e+q-z_)r*IFA02$OcX;vDOBeU>Q-5TKKKjTG{s%ufJ}L7vjFHxKM){?pIrw|{ z3B5rgHMHPWB_h)Z*~)oCH>wk`x99>hKcCZy&NvekUXN_R_&s!J?`<^&cm&J2PxN#6 zufG4Iz6Vdpd%Oxhyh^|OANX3Z|JF)Xl?7S142V2?yvwzcJr)40r%VZ$$N60{A z51Husi(~=s+dqNd=&NK>Nj5&GGacvIlG-k=O4SvWxKt$YIpi!F$mVbw=~E?7W{zZq z@P*#?7F$^AN6*81Wd*Nba}GHtJGjQZKf~K=^(qmxBxAsXM)1rl&H`f8d`Um$O4bj&0}R?sn1>$$egzXMxKLLDBM zl$U1OswJk97OuirB63J%0%Bz$uLAkJHqB`odK{Fb>N}s42+B^KxGOV7r&93Q_awG= z+k=+aR>h<`R#sq?VU1}y%M@(7a%&P)G;VFGPySYU)N*QR)$Lv_sL$8X6YDgJVNgDb zXj^g-Y>=DCr(^XZpUk!>8=XU&gu=2KzNw5c$<7sxGDuz$F{6}?XukREKKZ6 zWAj@4@ui)~`h(8Mn(?CMu|jTH9<yCe&iyx!rDm;OOs8K))ko)zo(XMN zV;UPJr`vYjYo{fk5=oUmd4C2~ua><+Z}713bDi++$frhU(ovsHf(Ue^eoZDLT}jIZ*qZY8bx z)?{6*Vi!$$XvBHGu(Y%1$^NpkTpCzj>PlI)uAVg5c09MOkN?#(6Vq8vYzT`AN>=k; zn@6YY&HJ=US6&hNS*2E``Eu<~c zy6iElW3JEAQctiA@TJ%YEL=a&?h)ov4j<-PyRuJS%XJImYg!#s%S-#WI;>UqYF0M< z4Z|Th2nrojwtK9@AvoIgvKad?ijf)S2o#B33N(3D0_{LEigtl))`zN+%|tX4e56-K z;SVpk#>}T`Z{oUy*!a-ONN`m#|2KagUyt4{2C-;hoJ$n*oVC4LLo2~*&ggsD#5@9i z!@gpUeRgumJl(jrz><6{dAiD{Tsv5su66=bHQyZ2**4_Vz_b(cDr|cvFg4`W^t%x{ z9GP#7XG-;&R54L6^UNnPWoN}Mi)3r_x~5dpT@V#ps@4VrtX(P|VVx&UDlzlW&-2`< zpOc0%vhjMFj))AOz7OiVGS@yktFn?Em089JleHQ41(_nZP+g?G8vL}K-Ai175q2GC_}wUo@l!u9H%*5r#XGx(x-A@ZT|?CF{l zpUQl(Q`3t2Xt#nixwpz*CiQA{jmf?GoJVKGtuZG1@O%>ToJ*t-Pc~o4dtdwa@=G~q zbupDx&S^Q433I>5CdO9?R`0vW_s#@EUJ-=sAZxAHouM3(&3PSdbk$~aI(aD<<X=q>u@0QDu?*Vpn9c1fsB9&3M%Q(s~&` zZO<{)$7=bgJVH)72bKIwIX=m2o^n9T$NF*5UbVM28fg`Mbx=WDVn10;BA+{Ms<+St z`Y*-^4}HFRO`f<|!do44mfbb=8@3JWF{?ZY~?3>nRmqQR5zzAxqqZB$3YIP);7S32EgGd(0rq524HNZ*Ah>3Md8|knOP?Qne zg-LsH+%pA=QO$XCuucN9NMA2+L9zZ|rIpJB9PZY8WRvprm+p9mNFrrb|V;6(= zvzRXm1}}Uft@)~D*34C7U|g)VBghu!HZ7^R1rS?=P19+E{}h7LFa; zqWsSP)t!Yseh*oi^N`B6WJ=#V4zFFb_Yo-DQN8rghL#uD<6LH`5zV_k5Pj3`5=V~h zwLNaMhqnEj>}BsNSF%9H(8i4^ds>(DZ&cD3FH@OCmYn>*D~pN#W`34zLH#kwqGW<3 z3qb`bw5?$(mVyaqxg@l=9@|r|fpl74;0)uf3#yb!Vg&Q5GLo^~hDM0-G%T(wSH9bX zrEC8_12T)>|8w|TCV{5qW!_-tR2hSPp62te1+z2N9&&ck9-d1lpQmTCseR(TTzAf% zQ-@bMZ}eTOPtg8cpT(x$b@2CRJyILlCpI#FBHywS@Dctwv3FlZ#Ps2FZL)~k+xR`E zRgKD-5E7+TrFiC^rk~d_J-B6SNK1q@GVzO=6GkGO%!MZZ_^yrmL9kP*259Um@*(B~ zhOFjg)oTx9nhlh`TI+RHY1NpPluu?;#v2y5D37kjwz;Re#xj>k68S?@H%rQ)rv{-I zRXpvNx-&3bAe9}0~ko0dUW6Lk(W8AZlBi)E2myS9&(W}X)` zDK0E$C%Yw^zH0pQuAznpuiJVveqD;y8tWrtIQIF_5cmgNsxfT6wFPHnWtS9T5a1#OFR~=84-yrRX#tyN^-Nfg0ZzV@)ixal4y- zrca{OZwZLp)4c^^MNmJF%qlG2^tPVtBFnf}#+W!_BGxfhK)Gh$F){7G#7z5(`rr#> z6MeB&eXX;)@I2aYR%0BRRba$Cd-RG>+^{BG4wk<@9e7-+Z;svaxTEvPLKfgypI> { } pub trait Renderer { + fn load_text(&mut self, text: &str, size: f64, color: Color) -> u32; + fn draw_texture(&mut self, id: u32, pos: V2); + fn query_texture(&mut self, id: u32) -> V2; fn draw_rect(&mut self, pos: V2, size: V2, color: Color); fn draw_point(&mut self, pos: V2, color: Color); fn draw_line(&mut self, from: V2, to: V2, color: Color); diff --git a/game/src/engine/sdl_io.rs b/game/src/engine/sdl_io.rs index 89e91a3..09f5256 100644 --- a/game/src/engine/sdl_io.rs +++ b/game/src/engine/sdl_io.rs @@ -1,12 +1,16 @@ -use std::time::{Duration, Instant}; +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; use sdl3::{ event::Event, keyboard::Keycode, pixels::{Color as SdlColor, FColor, PixelFormat}, rect::Point, - render::{Canvas, FPoint, FRect, Vertex, VertexIndices}, - video::Window, + render::{Canvas, FPoint, FRect, Texture, TextureCreator, Vertex, VertexIndices}, + ttf::{Font, Sdl3TtfContext}, + video::{Window, WindowContext}, Sdl, VideoSubsystem, }; @@ -20,16 +24,67 @@ use crate::engine::{ pub static WIDTH: f64 = 1280.0; pub static HEIGHT: f64 = 720.0; -pub struct SdlIo { +pub struct SdlIo<'me> { sdl_context: Sdl, video_subsystem: VideoSubsystem, + ttf_context: Sdl3TtfContext, canvas: Canvas, + texture_handler: TextureHandler<'me>, } -impl SdlIo { +mod textures { + use std::collections::HashMap; + + use sdl3::{ + render::{Texture, TextureCreator}, + ttf::Font, + video::WindowContext, + }; + + use crate::engine::Color; + + pub struct TextureHandler<'me> { + texture_creator: TextureCreator, + textures: HashMap>, + id_counter: u32, + } + + impl<'me> TextureHandler<'me> { + pub fn new(texture_creator: TextureCreator) -> Self { + Self { + texture_creator, + textures: Default::default(), + id_counter: 0, + } + } + pub fn render_font(&mut self, font: Font, text: &str, color: Color) -> u32 { + let surface = font.render(text).blended(color).unwrap(); + let texture = unsafe { + let static_texture_creator = &self.texture_creator as *const _; + let texture: Texture<'me> = surface.as_texture(&*static_texture_creator).unwrap(); + texture + }; + let id = self.id_counter; + self.textures.insert(id, texture); + self.id_counter += 1; + id + } + pub fn get(&self, id: u32) -> &Texture<'me> { + &self.textures[&id] + } + pub fn remove(&mut self, id: u32) { + self.textures.remove(&id); + } + } +} + +use textures::*; + +impl SdlIo<'_> { pub fn new() -> Result { let sdl_context = sdl3::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); + let ttf_context = sdl3::ttf::init().map_err(|e| e.to_string())?; let window = video_subsystem .window("Game", WIDTH as u32, HEIGHT as u32) .position_centered() @@ -41,10 +96,14 @@ impl SdlIo { canvas.clear(); canvas.present(); + let texture_handler = TextureHandler::new(canvas.texture_creator()); + Ok(Self { sdl_context, video_subsystem, + ttf_context, canvas, + texture_handler, }) } @@ -119,7 +178,7 @@ impl SdlIo { } } -impl Renderer for SdlIo { +impl Renderer for SdlIo<'_> { fn draw_rect(&mut self, pos: V2, size: V2, color: Color) { let pos = self.point_w2s(pos); let size = self.scale_w2s(size); @@ -181,6 +240,35 @@ impl Renderer for SdlIo { .render_geometry(&vertices, None, VertexIndices::Sequential) .unwrap(); } + + fn draw_texture(&mut self, id: u32, pos: V2) { + let texture = self.texture_handler.get(id); + let target = FRect::new( + pos.0 as f32, + pos.1 as f32, + texture.width() as f32, + texture.height() as f32, + ); + self.canvas.copy(&texture, None, Some(target)).unwrap(); + self.texture_handler.remove(id); + } + + fn load_text(&mut self, text: &str, size: f64, color: Color) -> u32 { + let font = self + .ttf_context + .load_font( + "assets/BitcountGridDouble/BitcountGridDouble.ttf", + size as f32, + ) + .unwrap(); + let id = self.texture_handler.render_font(font, text, color); + id + } + + fn query_texture(&mut self, id: u32) -> V2 { + let texture = self.texture_handler.get(id); + V2(texture.width() as f64, texture.height() as f64) + } } impl From for FPoint { diff --git a/game/src/main.rs b/game/src/main.rs index c422249..2aef94f 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -6,7 +6,7 @@ use rand::Rng; use core::panic; use std::{ - collections::HashSet, + collections::{HashSet, VecDeque}, f64::consts::PI, sync::{Arc, Mutex}, thread, @@ -14,7 +14,7 @@ use std::{ }; use crate::{ - engine::{Color, Key, Renderer, Scene, Shape, V2, V3}, + engine::{Color, Key, Renderer, Scene, Shape, V2, V3, WIDTH}, server::Server, }; @@ -26,11 +26,12 @@ struct Skateboard { rot: V3, nyoom_factor: f64, pivot_deg: f64, + pivot_deg_target: f64, } impl Skateboard { fn update(&mut self, delta_time: Duration) { - self.vel.0 = self.pivot_deg * delta_time.as_secs_f64(); + self.vel.0 = self.pivot_deg * 2.0 * delta_time.as_secs_f64(); self.pos += self.vel * delta_time.as_secs_f64(); self.nyoom_factor += 16.0 * delta_time.as_secs_f64(); } @@ -156,11 +157,12 @@ struct Game { segments: Vec, camera_pos: V3, keys_pressed: HashSet, + event_queue: Arc>>, } impl Game { fn new() -> Self { - let start_pos = V3(0.0, -0.15, -0.4); + let start_pos = V3(0.0, -0.15, 0.0); Self { skateboard: Skateboard { start_pos, @@ -170,8 +172,10 @@ impl Game { rot: V3(0.0, PI * 0.5, 0.0), nyoom_factor: 0.0, pivot_deg: 0.0, + pivot_deg_target: 0.0, }, - camera_pos: V3(0.0, 0.0, -1.0), + event_queue: Arc::new(Mutex::new(VecDeque::new())), + camera_pos: V3(0.0, 0.0, -0.6), segments: Vec::new(), keys_pressed: HashSet::new(), } @@ -200,23 +204,32 @@ impl engine::Game for Game { self.skateboard.pos.2 - 0.4, ); - if self.keys_pressed.contains(&Key::Left) == self.keys_pressed.contains(&Key::Right) { - let decay_rate = 1.0 - (4.0 * delta_time.as_secs_f64()); - self.skateboard.pivot_deg *= decay_rate; - } + // if self.keys_pressed.contains(&Key::Left) == self.keys_pressed.contains(&Key::Right) { + // let decay_rate = 1.0 - (4.0 * delta_time.as_secs_f64()); + // self.skateboard.pivot_deg *= decay_rate; + // } if self.keys_pressed.contains(&Key::Left) { self.skateboard.pivot_deg -= 36.0 * delta_time.as_secs_f64(); - if self.skateboard.pivot_deg < -12.5 { - self.skateboard.pivot_deg = -12.5; - } } if self.keys_pressed.contains(&Key::Right) { self.skateboard.pivot_deg += 36.0 * delta_time.as_secs_f64(); - if self.skateboard.pivot_deg > 12.5 { - self.skateboard.pivot_deg = 12.5; + } + + { + let mut angles = self.event_queue.lock().unwrap(); + + for angle in angles.drain(..) { + self.skateboard.pivot_deg_target = angle / 2.0; } } + self.skateboard.vel.2 += 0.02 * delta_time.as_secs_f64(); + + self.skateboard.pivot_deg += (self.skateboard.pivot_deg_target - self.skateboard.pivot_deg) + * 4.0 + * delta_time.as_secs_f64(); + self.skateboard.pivot_deg = self.skateboard.pivot_deg.max(-12.5).min(12.5); + let ids = self .segments .iter() @@ -256,6 +269,13 @@ impl engine::Game for Game { object.render(&mut scene); } scene.render(r, self.camera_pos); + let id = r.load_text( + &format!("{}", (self.skateboard.pos.2 * 100.0) as u32), + 48.0, + Color::Green, + ); + let V2(width, _height) = r.query_texture(id); + r.draw_texture(id, V2(WIDTH / 2.0 - width / 2.0, 50.0)); } fn event(&mut self, event: engine::Event) { @@ -400,18 +420,20 @@ impl ShapeGroup { } fn main() -> Result<(), Box> { - let t = thread::spawn(|| { + let mut sdl_io = engine::SdlIo::new()?; + + let mut game = Game::new(); + let event_queue = game.event_queue.clone(); + let _ = thread::spawn(move || { let mut server = Server::new("10.133.51.127:8888").unwrap(); server .subscribe(|measurement| { + event_queue.lock().unwrap().push_back(measurement.angle); println!("angle = {}", measurement.angle); }) .unwrap(); }); - let mut sdl_io = engine::SdlIo::new()?; - let mut game = Game::new(); - let first_segment: Segment = Segment::new( 0, vec![Obstacle {