From 7cef3abd28b284a06ec84a4c1a606d41cf88c703 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Wed, 18 Jun 2025 00:30:12 +0200 Subject: [PATCH 01/16] Refactor lighthouse - Create Lighthouse class - Add texture rendering with placeholder texture --- assets/images/lighthouse.png | Bin 0 -> 21546 bytes src/game.cpp | 24 ++++-------------------- src/game.hpp | 3 ++- src/lighhouse.hpp | 24 ++++++++++++++++++++++++ src/lighthouse.cpp | 29 +++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 assets/images/lighthouse.png create mode 100644 src/lighhouse.hpp create mode 100644 src/lighthouse.cpp diff --git a/assets/images/lighthouse.png b/assets/images/lighthouse.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd3cee92356d5a103e987cc3c0ba020a31d0e0f GIT binary patch literal 21546 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 GIr6`= #include #include -#include +#include "lighhouse.hpp" namespace miracle { -Game::Game(gsl::not_null services) : m_services(services) { - m_triangle.vertices = { - le::Vertex{.position = {-50.0f, -50.0f}}, - le::Vertex{.position = {+50.0f, -50.0f}}, - le::Vertex{.position = {+0.0f, +75.0f}}, - }; - m_circle.create(50.0f); -} +Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services) { m_circle.create(50.0f); } void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { auto const framebuffer_size = m_services->get().framebuffer_size(); @@ -20,20 +13,11 @@ void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { void Game::tick([[maybe_unused]] kvf::Seconds const dt) { m_circle.transform.position = m_cursor_pos; - - auto const dist_sq = glm::length2(m_cursor_pos); - if (dist_sq > 0.1f) { - auto const dist = std::sqrt(dist_sq); - auto const normalized = m_cursor_pos / dist; - static constexpr auto up_v = glm::vec2{0.0f, 1.0f}; - auto const dot = glm::dot(normalized, up_v); - auto const angle = glm::degrees(std::acos(dot)); - m_triangle.transform.orientation = m_cursor_pos.x > 0.0f ? -angle : angle; - } + m_lighthouse.rotate_towards_cursor(m_cursor_pos); } void Game::render(le::Renderer& renderer) const { - m_triangle.draw(renderer); m_circle.draw(renderer); + m_lighthouse.render(renderer); } } // namespace miracle diff --git a/src/game.hpp b/src/game.hpp index 1601115..3194ce2 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -4,6 +4,7 @@ #include #include #include +#include "lighhouse.hpp" namespace miracle { class Game { @@ -18,8 +19,8 @@ class Game { private: gsl::not_null m_services; - le::drawable::Triangle m_triangle{}; le::drawable::Circle m_circle{}; + Lighthouse m_lighthouse; glm::vec2 m_cursor_pos{}; }; diff --git a/src/lighhouse.hpp b/src/lighhouse.hpp new file mode 100644 index 0000000..1a0fb4b --- /dev/null +++ b/src/lighhouse.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "glm/ext/vector_float2.hpp" +#include "gsl/pointers" +#include "le2d/texture.hpp" +namespace miracle { +class Lighthouse { + public: + explicit Lighthouse(gsl::not_null services); + void rotate_towards_cursor(glm::vec2 const& cursor_pos); + void render(le::Renderer& renderer) const; + + private: + le::drawable::Circle m_sprite{}; + std::optional m_texture; + gsl::not_null m_services; +}; +} // namespace miracle diff --git a/src/lighthouse.cpp b/src/lighthouse.cpp new file mode 100644 index 0000000..d15aa82 --- /dev/null +++ b/src/lighthouse.cpp @@ -0,0 +1,29 @@ +#include +#include "le2d/asset_loader.hpp" +#include "le2d/data_loader.hpp" + +namespace miracle { + +Lighthouse::Lighthouse(gsl::not_null services) : m_services(services) { + m_sprite.create(200.0f); + + auto const& data_loader = services->get(); + auto const& context = services->get(); + auto asset_loader = le::AssetLoader{&data_loader, &context}; + m_texture = asset_loader.load_texture("images/lighthouse.png"); + m_sprite.texture = &m_texture.value(); +} +void Lighthouse::rotate_towards_cursor(glm::vec2 const& cursor_pos) { + auto const dist_sq = glm::length2((cursor_pos)); + if (dist_sq > 0.1f) { + auto const dist = std::sqrt(dist_sq); + auto const normalized = cursor_pos / dist; + static constexpr auto up_v = glm::vec2(0.01f, 1.0f); + auto const dot = glm::dot(normalized, up_v); + auto const angle = glm::degrees(std::acos(dot)); + m_sprite.transform.orientation = cursor_pos.x > 0.0f ? -angle : angle; + } +} + +void Lighthouse::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } +} // namespace miracle \ No newline at end of file From d87ec640dd3afb5968898614a69ac64c27f76c88 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Wed, 18 Jun 2025 22:04:45 +0200 Subject: [PATCH 02/16] Refacotr PR requests - rotate lighthouse image so it better follows the cursor - adjust whitespace - fix unit vector typo --- assets/images/lighthouse.png | Bin 21546 -> 42226 bytes src/lighhouse.hpp | 11 ++++++----- src/lighthouse.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/assets/images/lighthouse.png b/assets/images/lighthouse.png index 7dd3cee92356d5a103e987cc3c0ba020a31d0e0f..ce3f73c230d02b40bae6840dcb47634b41feb453 100644 GIT binary patch literal 42226 zcmeENRaab1u${r(-5nA<1b5fq8gy`XhXBFd-6a8n1b1h!;2tEnySvPtZ{2nO#eF!d z&+3QnI#tzG)w}kIQdO2kMV?vz(qA0Ki83 z-vjjiDe4ITPyytn#5KKFPV*4`$rct`AY7|DhUpG^Y_9CB-%(9`BLMQ=lE_$8SPCfA z$UvNRF{CG$U3dwqP%Io@M2%R7Sk8$qowkalWco@k8^4^V^Q<&n3AEO#&lXc)r#iZ^ z4lB#^uCjk_Q%RaYqHqvVxID9Y7re=Tuca3t*dXW(XcmhH@ZSUj`u|V-uRc1~VK~st zoco)^8nJ6Ffa{z=I5?u3gAZB0ibEXZ*u4k^p*|ycof1gt zfFML06kr9Y#~tA(7!g(@f}h!aKm%tY$m1Wf!htyiiiWSJ02U9nO+>9hl9KtA0IeDNK!%lV`1WINW0^3*D8dfDv`!@ zN8eV7hLUT6y&MG8Bylu&DAcsHu$~=;8uxw==hB9TojfIp9hTelKc- zWkq$ypy|CeHAK@du+vwT0Q_YayZ~lGTm-nS^p2F7@DO@4nEq?A*5S3dp>r0zcs}iy zt3YB7G%y(t^s{2+j}8yTBqc-cD}MbF|FIg;_fCbt+aj^nPQKyvYtT7|yx~(lrxxEK zzXy6^h{&!|EY15Gh7uW&PyNhbH7prJK9S5+&n4y}%nb8t8L^}C(noWt5vlh%+=XaQ z2zTn+v83s#8sA0hPF+hSE-V6C??4o85fchd6dVdlvOeOiFtHFC*q{49QIHF5zN)Ex zpPf}t&B!qO``a+Fqn(o{y?3)z8*iSMM$%u!HsRJG*r;D^QfgK|K8jK%%#=+)G*a!a z7dZVFLziMQ z2+rHcjGO)PDQXlkrYns$EBdPL14p}b(&0}+|49R+g2#^;-1O{d!t%1xz@48(2~DpV zm};p4-;)a_Q6hm#$3p%{lMaaM(UJ>o_AqriX?@2hmgDEL>F>+b@rbY*_{y>x3uRGkww`Nc70n!%T3RU zD--g}4crPN;&7&NjX)n9Nuq6H0(x*5L}4(qwRg4 zej6e2UI+F@EBws2rj?78u;7*aNKooTynWHWoZ%q%fuXQ9W6t#rLN4CWVSzjN{PUH2 zM)FW>&A;!_W%bxq4KeVi8+{1vyp1JwqI10;0+O);Pa0NdM3Z{|v0y6mpX;8Ygin9E zu{Q8LwmH0S<+PclGq;chG$ro95J{)PX1B z>3U5!*9$E*+o!?--Z%vq_eyv ziW9JnI)JW*UD~cGFpmmO%&${L zq4aq9r2U7a?VyrxfzlU(DH8UYjj9r3D43)9BjKk4uNF$jJ~L-m_uj9KXP^EY4W;z4 z#byfNXTc(vxET=yc<#iFFfmPEBk}!8M&ocGqOF;Z=(?+N~o9bGZ&6umXY@fjsxLom75y|!MUSdMAOrux#>LK9|Xz%?084ygCS$PjY<_13v7-Nb;E;i$|80)^rqm!NAJZ9xP&#gw4`BY zSGnjILeX`b{G!s#|dv(cspds)PVW$}JYdgke1>y(&li(CLMEG*17X7tC0|8_vlo7-qS zq-|u?3*Wrf8u|T8x<^<2Ea!ftGjqay*J(VVeFie%?CT8)CA7{w5MNNtibS-KBmy7? zE91>0>ldm5n=Sm3-X?C;L_P5w+m9Hky8&szmYLnb=m3UlYkT|o`UhT=!N9H63);;a zo3z~r!aYs}1W1w?m+v#!VT6ddWpNo&(~tb9c((K|Mqz4L55lDsHI5viH~LZ$TpHj* zbm3g|x=+gS6sniWX@;OfW&RBJU)7;xECzaiSog#15P62Xz9-L|UP1{pm?1D9lyLmC z@aDA_&8+@o_q2KY2?C3ayMDJB3KE9AowD;uO`j`uW3>kQwKRX>*vl8){@M3dxBKem zGVXJ*J+Gm%5RmYK%h3fd3m_VkczNlb(4THM!Tu`xgmyLSS`An8<=IP;ys~J3&OAO6 zbc+tns&V&5W5%@Ga6IpglrVneiKqMi&E{C+QDM&gmugGA-T;~PSF>A6UWA?DgunWq zhk~I+f-VBds7L{(g@pyvhYx-K_}wm3_D+&m3ADOCUf(?l%OROR{8 zRm*zILk$!53Vqs0d0O}jnirpRlRv|z1@rm+a@$=lqv3e7Ht@e1B;quMlZDGng~l^8 z;(P5=nZebI!n=l_BP)hcr}xU$TZ--I&L|-&fj`jmsjqI^yh*X_GSu!A7>-4+`%5K- zgVuwF*q5EhgfMcGQWEGlL&FTvc?LtM*B&nE{mH$@Lbx-2Dk(Y3C z>L{goLU&94>1(-U|E;Y4)=#=#_hlHY>`&(@YR|enE4&?O1*pzUs_TF>%2I_C$E$%t?U$!^h6# zSjEM(&9*(}Ek6kEc=styuJ4lt6B&2(2{MN`efS-|5nIj(;^_@IqV=?AF;I+(diNQ^9*8JmY8b-CVwy z1guZSC{S+(&wgzp^kDCbZVroa-pc>z9eS;jk=qOo8~3qtgsF4n=kJ&s1Vqy3P<@!W z)qIz7?qSb}Eb+cWIMwe#MsJ88tathb``5^iHCMT8o#qol2s&WMIW;}qbTXH}_U*u$yGPTG07r zrRi~tRTMnpVzzbVeM^wQi3E*G;X$6owzjsH`*t;3yHAuL?I*`pQ_j$&W{fqEc(e*K zV~hOk&<+3p_ zr5Vu?b)fEWO2uSw+rs-H71#MBj+e&j(qx@P&WsN^aR#2m^X~5MYkA-Rb3>8_gHXT~ z*KsPrCbf^0Dr->5g_G?4yl(wY?84CpScA9Ugxn2WO(|I8fszdh|H$W~ffB0#&m8is zo(^0v+cN0_m`9&Z`bqWAUbZ=v z;X{h)rCk5X!-c0rJ#CjOB?d6M6nfqDZpeOj2IJ(&6?fLHUKok=7?zTRh7LD$*b%QvlK49k=O=ZEP7>PJnj)f%+F#Qvw z(~n~wa|=9V6)7N*kR0H&5~Y|Lkm8p9kFJnVcT(s^_DpBoLQ4e_@d4uGEFN0WV6p~0^cEW zOU9r%X3N8+yuFc{^$0<)(}6D!|3aV^cN-Q*{cl|zBrmU>IYdb4JtAe8Bvasu9a{bM zg~y0qgP&&TSEw=*TjwJ=yzL#e6obv<(MWhS<(NdV`hP0^1x-}<^tT6!#2g>nc1fG5 zqsam?Qzh1(^Zl>oci-;{_}Ga0_&x~ds)DD@K>VN5ZvOyl8DjwA3WPCbPK6G@dM3tH zds(w5M>s-u={E;aaNRJ)7$ZCj3@JcKAySHFso8;a{dsKXamb1pgc*+gqi2)5O=ZRv z5d4Quj9Zxz<~F??vyhXaGV=3kD}ori*iSkX3DAIuQS4GF4h!T2ocYO&;TjQRTL1Gt zl6=J8_alo#?~PnSa{gmy;nY@SbG2J!~!G0a%c)QD3Yme2*e7+xPa*-)ZLtFO6ZIM_(F44n@XKHB)`&i)Lc~1V&_cA0sTumx zO2ocI004%y>8It0dYD>gm96(%Z@G45g@A_lC zm=h6NnU0IkEKLWEmKDhUL1j1fwqyL%wyjVjiL$|#u9MTc(AYJ*?^^sX=lg;!jFs05 zr&f8ZwIz(YGR*SdSE7)b9{exd7Kb18;>8VBOde;NY-T`FlkXS)PjshBu~JY6r%@!5 zz|KKGZ!ONlhKQi%^mu;>jQZa$Y4TCnjHpX9_GH7u`Tt_M&sqV*dJ1(zu)1l$v(mT%rZ*-@aNnCcAK1tz_wT3 z6^o76;gKbYKmxwovajf{caYwz4|MqU$GJGAVnIWh z(6H$L!M!=~{&e{;aO!Qu?qW6-$4}+g_tXRpCfaL-50xv7-Rl`?Vc$YfunPpRuM;J< zy1b}5zOlRJjB+wfhZ^uBMCcmebDDv-0}*rEG*Om*7e9s~g_GqVtQh~3tMJvW8;KLk zgPp9?*j#NvTyLdZkSy(;JB)xuW+C~;kbTe@A5)qe9mt{=8$m~EOYXTVYvvr|N2%UM zYf-V}q&u^;?l(A?>2mkLu|I%&OAx~kfaXfXA0;KQl?8#=c~hN1)E;qFj~Ym8W1dCs z%?clzeYy@mvIV!)iq_*85?!EygxV7??1J?(5paB}OrzC5znCz73>z5E#U1LER|BE- zuH+upWF5%DJ#~+W^1EE(UYlHiphz z7zx~8To&`Ym}*z8#qm&-W)-%sGeupQcsIM`cR02fdACNHYv%RI_EDi4ekDXlNB4fs z_v1Le^Wt1Mm&%NgQ=kenjZtK38GDozB}EXV$xLH6^DPY)S0{2TsykF34)rSF3)ZJd zkR$StK>}OeNdc-}9|yjmdyVS8B#g5Q!#qBPik0P7r4!ZqC8Z!0?}t{Z)m`JZB7TwT zsaHlz7*d`27yH4`(DYvlCWIS=$l84S#`^j%fZSC}RQ%>Rk1VkLm zU?0vE{g+_3=&;aV?4oFwur4{&Gqp4;kNi>#y3F^6c_lTLrsG1WQ4CXLdJUasOQO|$ zG?v1a8JgaqA@XtL(;HqGMY34eG8>-sp{T~_?Q1`KiG%5Q3`VQb*5N@Q`zEkr>5q%A z@`}7JZ?(N#RxlKjuQU)M-Wp1STIt`vsao{1i|tjV;ZJX6S$MopdO2a&lki z2pv!wp~@86=5|1{GsC#Wwt>!Da3D-aW|~B6ArYM$U&2f)LnYwxG4)6zNzef$ItGzO zwHb(>@QReZcu}n<%MZXPJj`U0$)yP2o3dH?zK?@=&*DK#kc!bkUaK^vlxgpI{nX$( zlP-$mx~FX~^3IUk0fL&?Yv+eU$7V4re4V+a{HKEfGMJY*1_c}g_dEL5{rJ_vT&6|+ zs>TR(fXvQak8qUAIfT{`Y2b5SU&!`m(V#=ZT-4~+pz~z*_bN3oXFU_){kPSvijUt* zEX!(Z{a4uU|JdUeNXiFETFF#xkjWYtzsWF;id^=dD@;r*G~52jABlV9+DRX-JA7i~ z@Eniw{#8t1Rkab+=SqOk(qcP|g1ZUV9}*Ui3E?^&ZnUfEYa|T}vY9Kj@nMS5guh@2Wqaw5Qgi$6r>*ZkReKw`a0*j9=q%8w~I1@3zA+GZVuYL}HI zIQ_y&;n<$%2p^A>@T^F0-e=mXD;kK3PWbmHbl51U7?uKr?Y@WCw=fe#E}gP5A?6^YQmoBA*6G%^oh7VqP7o8y|m zIOMGBaVVW!?6akXa1xIFilRizYSiSmsd>JOK)ujfqK+scqQhh5EH>li&)`rFKsY(! zU}S@2;9qGfJLgTv6i3TXe5r{ww-IT{x^95FVMTBG4Acp}l##e>%|dGRGuq}u73 zBipO*H;8HQ{)QcpT_l@#1eRPsl zXte`eq+N_`7^rzVt~5`1iR@VzxqTuF1DTsxn#g_I>r(q0QkVsIq|;Kc+grf;*B2C5 z3*s9?Pco0YG@`(_CL&%(vIF>AYkV~#tRm#nTtbDa@lgA2CX8~)RgSs zB{e3YBffE=duuc#-oA(3>aUq?H4nF#_}oA=ezs^(1I?cSzK}7$B~_V#ve2~x@9E}M z$H-7w1rp6%id~ibu;0H9Ox`pSB=TOrjQc;K52V56nhv>qID;@q3XO-O@wz7DM`KUD zqD%WL)vB1lQhl+uvjgXDK!9uPUGZ5TE8nIheZg!(7%LrR-&T#|*m`$`@aMR2nBu*9 z@iA?BtlS5C3PLZVBPb|`BIk+|=~Ixt@g>&B!R8o^OO=DY^Cp_iiZBRYoIn->-k^4G3Gw8ympzX4Ny>1$bI$f zvip)mc}w=)_;ZxB)m`cb>(&QeBKr%dYeS?GY;*XWq?`FWCjVVeI7Z9Dt3YLmH?@?)_O&Ja1nLa%8i~83 zO#I+w!nbPg(+ayf$38@)h`GYNpYhIcz1ye%+Hj{{FNYuBn^E9=dW=KT^*^eS;L>h@ zQaEe)P?u98Orn|5oaQK)whGs2D3OHBcwmx}kt*X8k$;JTW5Lz5; z{`s8YWM4=0tp>8B@2N;kXe8`p7XQiAQUms2R}0b(!E{k+a>m@Gm2rI*FUeT5`aP`Gwj;f7tZ2^MNoDjWSry@x5o#D(A3X9fKU-ox5c_mUn-1(s&)g*so)qMh_z%pqn_6 z!@Ki4$FZM%)V9$x_vkgq2ACG=1+f(ush<>qw}N66t(09#MA8)=LhtNi@keU6pFd1&$z`+6%^nz`_dN4k z8a{jzp-zjm2@_&Llm^4_wOvWJQnbU_ zD%GV9L!?Ju5)jN!GM-!keLTo0d;FQYPq>`NnD`i=%WcBMnI2eI7qaRE2yzQE^}GQ2 zS6Sf{<86?*MykDTS>{wu{-fZc=v&p|?_7Cwdw^~vE3;1V-oCM^|B>UP=HT(<2Czsv zSs$5<05=)XGWh+!wh|nf8ql>`>=zKVVt~8`RkE$qCQI+6u%W5!Z(}1tO>d}t;@`$ zl)j15#(cfJ6hJs<1bM7KQ2Js{_?@h%1T9xTxmEhfZ?DdK_vKMQBcibH7L`9$D9E4R2IgojNkxT;I{p}-)GmaiNEqM@&^Hx83xVaTO6+!p!LM5`Bj)saSKUYsgm-_eH_T+q$DXl+*d@%q$vqe?*$22AIRmj8 zVuo30=l(hz6;1`bH#HGrm`VR3m~S`C6!|7#&Od?BkKL~iU@)I4miFFHH{P7wSB9HA zyh!py;!7N^Spddm_i!k1(gFr-4{J2=KooV-5A|Rl9-82h< zCqro{m*35#=ZN6n;(U}?HdCO2XYtA=Y3SdU%g&vxg8UFQWlClUyX3!AZ>jJ#cspN+ z@L!^Ky5aQVD8brGaS!ZI>CNPYwiaR4=)%v+T78Xdl<6Y( zI-OUe%$HC8zA+XgO8^dR{T4^*Wr%QWta3s6Spl2TR1UFGRolHE-%*~9i{YBku7h#3 zb$@&NnOu@NeqVaNV~}>2yy;%FyL@eoof59#qvCSGZ8rY3nfna_*<2HxlYu8&_I1O| zzWYyUagI@BzM?KtS_%lG$hUodDH`l9=}%C~LT2cFxC~4Cjib_f6RFFX%E4}NDZL0f zdGMkOG0}V**;bE1jB{|xYd6LULX)2slQtV1-2>hvvi=IA}qm zUX6!^bnN5UuT;!CY{>eW})DRqJh~>l^zoX9oqMBi`rkvtUJrkI{5={ zop|XKz@WvM{)xohN1YWXpg+DNh^(oS$NStDQ|H@5hc^=gEjvleYcoI^b}^PRE;h%h zKTXF7IcMT{87l&DbrOMr3}dJJ;RjcNuhrak6O&QsE+d8@+R{2j1y*dcWcgAYy(8M}vZ< zj9Z9s-#k-k9#j%`s$14)^~u0cs4GN*x-*TvD;T}!Es&25YgRwkC`{prrxHbb!xi?o z07%GRFF&@FwjaTe;-ZRN^ z{_p+t1vc?v_!kt0xXV4rE?YW$gDdrx+z9xDZgiWd&;qE?|FuuT{rw%7OM!O6s!$^V z>pYekMa2K$klS&`-}t^)_O-YGpYW%MEQcL$BzJ#IIb5s{j)Eg-hPVcG=Ku?WVq9IMgpywGr$9Kg$mU!l?Xv${l zZcy7yriV_-`D2&Z-*c>~d}=~Uvs@`-Z#-z1&n0^7*q{}Op1m`4W#udMfZ!1rQjDW| z+;{BzZGeAj-KA^2e?I&)0MQ{6MgJ{((bpKxjg-bRr_Z986nV?CuT3Vgoo#>r`Os@x zQPn7pl_HF5DgI_rB)(cF0CqK@ASx{?GCjhKyWC_&3xmCW!=DgS^tE<_hDjoQ8I{Ab!X+WT1OB)~(9eWXxt5q#E5`X1q;%+23JKqkCYwZq{e`7;YdNlAD zdv|0Np%FeseQ4-<{|};yJr&q9LNH?S=`~l<$uHU8HS$h%ET}$%lW1CY>1NBsY;3Gc zlhQ%zKnG+w^fc#l{e&p}f<9a;3dBDrGQ@@sh|RTh`p6$yQ85VOeF zaKeH>qsr#&-?hL(T6}zbrXC0+ac%uq^_+HHla1@oc1;Z$rxO8eWqpc4bknrgg49rh>A;Qo5{|QYQs=FEog&SPX#)IG zr^Lg|3=Jmv9<;GmM@T!LH5=I;zqLM@J>V8ajWhiE)q&|&XSXN+uhKqHlovHpzOfoJ4bZKy+P0{#R zq4(WtH|lcW8)A0$EIa42fXb{?mG6O}!QGHSu7#JKb~*m^B)*1_=QZ7JfnF%5ZN{Zk zbt}-pztJiQaM}oO(kDw5a}xdNeRPa!%@VdkvK4TyV@+`==~jncgK=2K&!!!e3=@IE z%x`vr@p^sGBUad;=#=z~moNQod?=0-^b&|^3xbqON5An6XzilZn5@quU%qaTXLfpJ zX}hM*Ab!m$5hu zlDgv+g znXcM13jdM`g-7@eZFGNpK|fUlY&#YLD!O^E5q9%$mLQ56 z&o*5$JV;u5pWPsb7Rm|9cS~;UNi8&W()gM=CVyv5Y81sW!!wQHHcIuQTYmrN=bI5Te z-X}rW@yQ<=?4E-<&f8Hp^cwW+6m2}qzl_XEktsU0lC&s~g z^Bo3!Kos+aD2&?@T8flMhy}0&{aVv^=}&Ny`pk%^EhjjW&c)fu)AfiV@_H%RHF%^o z{$7PPRaOuX{o1nMZ;|Ah5)J-dZ)3($X~kB zR?#y6cF>n_xg9ppe-k)h%Go7-j6eRCil)Sb*S~Smji|w!ZXG>`BJ@<u^`{fxy1MYGd?<2StiDwj^X#p5Id~5u`iLj zP87N*uHD-}dBxNQ0~{(3nA7AY3E9#`4gxxC3~H`ZaMfs=Dq9bdZNT^DfXpY(nVDWj zO+ru6H?oR~idzE4y;|Tj>gjqH=`lNm5LEO?%b@)(B}jhPaf_prmyB7{BaQmNi6C3m zT33t44eBBTZ0Sl^UTjQHlekRuVl65iMyq#b&kHgok3y^@y?($3zR-{<>Cjh#ZZ>cc{k z)=IM&e|KTlulDBh>d?}Eao1~RN*hGeD!D3Sby>Y$0(YswE$keIavIM2xM;MzHbmvmrTIrtIWBov@< z_w+P3XA)4qxQzX!d7d%X&36E=R$BPtJ=#t1TMgyvNfu2*SV>V6GXe&VXZz zIVpmXC14UR;8Crs6=yb*F*PAuSL}wB=eGJ|lANW`w)&vCbtOw7J^TvIcr-RLZFru$H89_1CIk>*|=LYaIiRR~Ay2erl8Rk0J=(;RzoamRs zT1pM@oK-fQzuoCKQ_VG2N_W?Nwq0HW+&=KIzN((wm|A+1kW&ZrP*&#q0de(Lz8IcO zG0P*NQ-tQG$WYf1N-vl9NzU#s=~-nfu{EQCjTW+f^I zJvnpt{q(BG#?WX2+ANg?kjIpmQST7eV4)`aa5Fgk>K5{#kr@vp^>JE{U}FDWD#Hwi zS6bp)tBq6GN-^=W(bG1biSkwZur9BA-|10N9PVeBbZK1niZzkrzu4#lBPl3Je0}>r z(l{BAoSeMe_Z}Y4(>-T#rkKOvEOUlPgf4iHH zc%|ds1$%G}msnq`jzsfQn#=J8S}m{f`5xJJLK%3zF>Xt`_CAODMn3O8WDVYF=|rYH za;CnIed8>T-98^ny)tOm*Pva0C6wv@YDfI5NVvInUBvd^Nc6m}j~k~_xV_WIcSOr| zFh9%k%~x>AHK|4j(6-MAjik)XN`D#u{Ycen*jdK zw^dL~_S-AW4f{YyW1A)`ipk3KJg~4UFt8y`5SUHo>-&S*%rur^NZuLRUsIoGt2Dzs zIehx*S9|iStm(YQG6X#c9Q0N4{M*}?-P2#6_MR1RC(6ppMIhz|Ee{S%tLe^%8xpGC zxubg(FZ$Vf@K*KRKtfgUC_Kao?`!WRKG zWCT)}k7v4l*u#^~mz+Bl`9M-A8^wTP1rVqd7Ij0I1xvlCh5`}nLQx`0d!4mY54~<_ zOXkKPvcpo5BN8#gi|^R+gd^vf`#?t$CC>S${$XMJS6RLU3(k!G56OP}2YoM^ZD7mW z5vN?9p>K?JaBht+;}9IpTm}hSR*ahS20-t2V220^zI{BDW}@jH@ws3 zpdrk2a6M^H%>oT)j=F-FbUuubu6CSZr4DTphf3tA{lizUW{1`Acuaz<>(`GUH@=z2 zvj&dezVzFj@n3ORk*6zZW-W@2zM#h&e0)X!BLIl?i2S_Jzpfp}{JR~kDa_}varH}C zrkoL{P~FX$mztqrHB>2dyl#n!77q!s6yMETvg#Y~=|fxcz6l^aE0~}@-4R3_(f`PY zs8Ix^xdOJ;^z`c<@q(I`j2DY;?ltP3g3Y!>uFN&=Q42$R+n-Rc&O0uSSx3de2))#o zlY$pi7bR1wRhjH<0vc$_^vtMlPMvU1tAq^?P8QZZ226og;oc*bUo@X+=8?)3XNv*p zf9y=&VOKC79bI@*sXfQfGs^NbKjFG?av(N(6rL{dHj;2!2B8Oq(2-%7-5W!qFFts} zDK*2i4NA3(&c|JDyvH7bMW?gfI2OZ`0cXmC0uw>LC;%CM1Gte2)lc<-TQSxfZ`26y zw+vEB&6Dc24xtnAlv)d*omq?05qJ~WO)8)FrvsOr-wIHmShE8{cjA~6`@!pt#7FQz zQbFpDAnFL#-cw5>eI*GKwU8ls8G_yOBTc}v3Yni#a$qU1-79JFK1*AltGdO!UX{+| z%&w-Su~E~>rJEpM0a0998eU?mC-yY+inX0x!mwqXb-W+`+9*_ef(x_{<2Is7O;zU| z{#O5VU(vmtU5*LR;@cIscS!X{TgI=vdPg=><8MWB1i@q;C5^iT2Or(4w{dsT%cCcU zpaJwMUCkLRddVn|r3Yi>!5q6egoUGQsIo&sE`X01?(R(Cm%*57#>O=*e8$M60Auj} zmmCh0zG3H&`))s9O{snte8$^ICdJ-X4?KdUhscJtD1`;xx0rAV+SWIVr#1tsoQ21A zB=mu>3K>NXJEE5fSF~S#mRozP2WHBBVHJ7Xgst|BRpXrdcII~2=iqWW5O7yPM77(9aj;6f0Y;MzS6GMr?2+@Kx{l-37Zu4+crtX7#k#rKxe}x1|n+>(*N{J zfr^ry5a_KMUud&Ke+^`9rR8oM&I^RoFDxk5|eNd?RXxgZ~`v7 zFlDAQ-ncnpco?paog$&YMoxkWM|lh1f6RSjF?>wBryKUo%x40PJGCO7>?W&%nDOe1 z-rt$>uBE(%=KS>3dL>wi>2R5+K$GGivJ}&8Qx=t_R|bQ%oS8G{tcp<&O|*aQm?EDH zJj8ea-C}xr#MdH^L_E0s5V{IcLDCl)^Ws`ta^7c9XjxT%5_M|nRWmszdEM-bzIiov z;!z3lI7m_-7+*^A8FQ+?Y(s?9NS-GSW={F@=~@i$y0_%2DG!QeTi~~$aCFkzcHf#` zWQ_HpiYj?wE@_n1=^QD$N>1YZ0G^2K@oe5CPCv1MkNB8lk?^9nf3U~`2csH zvzNt9KhKAT>WT$VSboRGE+YeI5djG}EHi0?Wg1wjR{sUA2ek}iNVzGqjVI&C4a2C| zoJcI>wuS%t&-)KD1-;n1TWk>9m1eIJ7+|?aA1^~cWU4cC2BKEoDcM4GE5M7%tO@hO z6`?;9%C)l;ZJhAgo5yx8_{KB-nTd(8aBpV{Ed{!k7-nkTveP{oU#Nv8!k&t`0?s-K zHw>4+BDhZ_`H2kgJ!5dpoxBPo6c(xR4$ZkiNx4)f>f^_F2}FdY<4yH(E~d{KKTUag z99DkN=boTf#!>v2(eO!mvq~>9!@6u6>amIT``w%3nBO@ez&+w+!#@FlQtfQI4i3|8 zYW_C(UJf}1xX}(?u+|;%`N@I!Kfvg%$#ik^cPvjfz3ArV|Ef~hAUQgm%!m6`z7@*p zul(7-4i3%yL~tvMK`@}~jmuapO1^NeQ2P);F1o-KpyY8LbL26CRLC2Y1pMFOxC5&YLh>*$_uD%>x zO9Q574sY@LfPLwngX+A$EML#=hZHaI>rdPVmXY6oZ9> zuZ^?V72Xdr{O)?hRGE;p_D*X(PuO+dkk;^F}=ctS+93uCE?Yb#ujR&d1hg9uRXnQ8#RAm>dvJ_2{0N79 zC~u=>*CJ(Mjl3dkVRI=0qZ70A65P6|_@60O(N35z>}xeGOtJFQ6$ejra-MDlFp0j= zfGqQhPclKp(r>3D!Vr6nsw=vp>Qamt0v0ir#TI@W8yf(NZYvh6W5x%XXbNhJr=Nu2 z`>mbYEOC<|EIL$RR1=5E`E*;wpWn8!B*gnBg}6Xz1ti9NKLVV$YtSth9bJx&e4*ts z3Y2_SrXnhMh86^+c*h#I5KC{BZ-F7HPsV9!aULFaoUKjjq)TXS9ICWb5wZ)>hnb52 ztV)ee#o!5L?hT(yF2sZihD7cQ;D{|VOnLHop!L{?En; zA0V|V;cIrLuwqIY3h$_1IVFmWW-K`UD4B`Xy}XTB&swXZeC6V*b#%1t0l1#_8^7~3 zPf%w^0fn@(SxCAK_*wuXb$Trzbv5iE)Ir}d&rKsUL~T**MVcab3B#q}>GRqW+sM|w?fq4WIZVBn#De|@k&T9y#my7vGcHtLXWxr@9a zjOK)anCussPc&m%8Dz5u{7Zx3Uu zPnw>I6W7U%14^3ae z7H8LUJA=EsyL)kWcXxMpS|~8MyA&(#PLU$T-3KjBp%iy_&OGnAz8^6A+LJpwSxHtB zkNIUy{+ITt-6BbJD?eXeChR4GRg-C-kL@|bwo zlfgFr0*b#d_|&nw6B)k+&>~KbCdjx}D!6G7FpCQSR%4g32u6HAqs8lIQ<qMpRe)hCQ)qs#^n$+E%#fZt|8cL=KA^o2x;Z}s%>u&>8v67$pNYjT^959Qe1 z@$VOuBQ>OHP}WFAFV^i|y+Z(s;a_G7qp2xSLN@CGLl2UxQR}piIo6LxG>=9uYofwF z1o5y(0fdCGNS;wNL%nHpI_%UH@)G^-SAmjP$eEvsuR5NFk%~nvus`dJS<^@-d!HON z<9>Bo_DAF`Y-17&MV%q>AUsKeAz6v^LF-Soa$EC9z`P$Q@hS2RO`^fSj9vyCml z%enQ7KO>EfkGjdKMYk92&k*B>*Egqcu~o`CqOK0x{aIVF4W?YDUM|Lw);GcvwPk#l zmXau)$4wL~8LZ1#(lJm@2($HSjW6oRAf3;jfmbdMDhCfwVB(4?A(Wj@skh>HS73b$ z-&M1h;s>)?U8eJU!qfkmfqF)S4nc`%e`IkB$>A0+7V)flob_PeGIN5?GY`vBVCCfm z%YMd=RwYP^=Yh1L955HnZMQnZLQ-0TQ?0pRTt43`a(}>yS98)9<&JQr~|p`y{0c(Qc;fh=)|3N0ttYo29|K z`aPhi@^sLe&+R^J2B&T>)ja&`9_hB5tdR=qM$RW2-{mi89?jY zk`s>hAtDY9_xjid&QKgap>P?Fy&MPvl$nYI0Rk==(c~hy^Eo~oouf|epFS;$W#Mo$RnjKn+Ktco|QFliz@4hUUA932sqGC|F$w}1!P13OR zd~5T0vNcpMZ(geANm1Kw_mzr z#hRuIK%e)g?|3Th3X`r>Ziq>ga#L{~Aty-*GFN4w&>#uk90qs^hFh=39<$+or1YB_ z`-1hXRyUQPD}j4ii<8^;XFA4>sh5-z)c-6n|S5P--dP6MlGn zx<6b0cKyIJdF?K*PX5o}ouga{Z3xwQeNOiPR*T-Ek>uY@@Kitb9fKsNEm4GL?P^(Z ztE$Y{cC(vnB<>k=eFBA5A0-NKVyEoHv08g>6{_V_>!bC%`SFRhcLbF}3Z%F}E;Mwh zK^u#*KI!*gyfBtyXu;|L_^%O6?+2_ZG&@F1&$7XWZgUoR4MXWIQ}PkGd+Pp>Vusm19yC93bWy0esAvcdH(_ssnve#_oGK3jjir z7zx;@x+fx*c#atl?mnaX&=Dxv^C9fh#{nOg6D`#m*UBfw`PGX;amPbPx1^&(pzOj) z0owd%zN~Q+chHtUwR0AyembXyU2e04&yykT$&jI=pXIcyQrW|qzv?aETm(@RtN*cY5JH}Q^|qt+Sz05`Som~0J3GzY93?}xLchXtPnDySglFr$W(|H! z0gK$la8Ih3_2wGoWE$hTH=rpWYR$bHs(;oXc+>twu%4`QR(7LcUW`^5ehIs2-4~br zZrLKW=E9-F z#^!e->7%S(A6S4?d%|K$;DBmxob%G%>Q>wdRYm8ONLwX_GYXDS35T_aMj;la5wq_r zDKDpdq|iX+*i`sn-`@!1wc3f}bXREo8Nf=TKaU`Z?;xEw7E27~n4Tsd!d z6!qf~UT~BPFj47e@8m)uj4=^=r9y{>*!pE_9SQ0N)Ob;K7z#B+%Adh# zuh|BXy0MxJ@rU`<>D>h>4R^_fSTQ}By2eHZPWH@+Sb*&tLGy;KB1k|uW1ruf%Hp=Z zS^krIS!NRXP`CBP`H+CUgt+CQAoo!E?s)d_s=K@0&f z!+)OVII%JL-1DIpUcmX>OYIG7Ik&GJr7jl|k(pR(x~LmnCQH?fo;08*;aL4|$es5^ zHY@65aAd@Wh<{s8wC;msmouWK;x_|Qh>?h&VZ^mlG>kiO@};FAi0(VZKWt>g*bD3(vKmD0PQl((IlHlzQ6<#KP3$7BI12hxs$c1oRkcYY8o0 z__oD{3V`TG(>~s#<|1|<7o2!|7xCb4SIL#WePSIZR)+0FPTHGR;Akm%dWzz;enN4KwM|Ohd5Gwz2;giMryD_J^a&(+wu6xT?F^8b|zd*+If{b3^ zp#fb^jSeeeS_vnLj7?N{P9r55Z!KOC)&n*+r1TPpBW+255kqAQ-tb@3sV3ruxd^-^ zDJB->sZ3XP6Q7_Zf?A6h9iQ0dSC@`Nf_X9wP_?{lR&d&t>_F3ku1tgjv-a>7V~Vej zg;^pU*W5z}evw|i7+8*Jp@Jl?U}CX2S}a*Laq_+l)&^N(B%VhCiw)%2Ja;aCTou{j z7F9B-fmA#7K5$%YWH z5eJ-=m9z6km(6&l_1%y8dyx;n8AbDUkc&f^mT%8j-8P$FIn4(m7s406Ze}PdcO-^~ z;5^9|LzhRh|8+_-&avoz6zNjbf#P6>sl6vnZrCOGr=hn`-0TgY3oGlOL&Uea;!rt7 zx;t#J1?z05O;J#b9UIW|TQ4q4vEad!=#CeYGiJdI31L*?c3#|+>S@eK<=g7gh1N3W zxAxP|*ikr~eeek?Ausa&3n|)*( zUwjRU;J}??66X&!2riNY9yi^g?qEnvMCyT)B(cqDzue>sR_3B~#4$_L1VIRUk@ukM z45uM+@Xp@B{&3;t&=kLgN8}r(yT7&!G`bMz)y}-d=E#^q5HNYkdHN^mX z8+?zd<5qe-&E%WCkT)XpVE^H6ox0hS8<=(S%xYy8a%U}&$|DcDQ0ZBx}($(lRn*bPrmu0Uczdpe1 z<_;I@L8v7v{<1qQ$_Yqx;c^dSUYAt! zok#&Un(ZpG0{q3E4h;cWL#a*&?}3l)3(#G%cIMdDELorMXE+y@cq5MB?cG-P|11@y z6p;aYX6%*hMjn&tuY7%qcm!Y#MSzNdZtzAf_RjXv+a!R_%lJgqcpSA#Je?fN5_gQU z24T4MHuQFcJ5x5S?Q=9q7aLx24N&?bTtRK_&)gr1*u|qptSsa* zUqOcN1*?TxC3 z1x?!y#s&B?`=qqWtD}^>4n}dhTycf3Y7rng;;+w59en&nWqOv@0X}hqnkYwCu+ry@ z4XalZLELwFdEb(bKjQ+jv$Gqy`J`%WI*;H2msb-vlU`-Nf(=_!1x(Zb2Ai@WVJCq< zk)n*v^<1&MqabfW%6uO~z7;ynq(FxqAu1f!sBI%}R)8=W`Z*hE@v7l8PO*W8KFFou zSA_xYIEh#b{>JLaxVc9R&h*=#9`un&DB+pGm&UqoL}0@C1Nb&=ZZ$%lBHF#(4_Vm+ z8I+SlRNH1`zEp{W9_!)dD~oom+8ZASAVlzdrHdP4?&B}5c*On(wD&$pi2-`MnRsH5 zvbOS{dKNt_-M`>F@`x?kUik2cL-eM3HU3OTCkM4&3r*a^gw*r6jdCb0!lLkPds%Sn zBT4V@T?z=G58dBTg)NN;lW0p$yokxQ zYgz#q-)qe5>?|nk+p}9cY0RHV{p*hy3S}!u>2&wfcSoFpBR&lWp3nc8E=P)3SHXJU z(qBJ$0Jj3{@4WlW3`3V)oy1X-TPOpTod-I>HMia`V3< zGyGL=^#K^Ls1-G?G58`Y0QiTBIu`5AWC@xR(*{IW1FrYTx5LBcti|hQM3lFiazoB) zw);&hq?+TD6%x&VZLXdKH!NX$j}BGNa~b=$6N=j32$U8oPiCUZw=^vOoVV;l&p(z-@>)F44(NuTod^yJM|^0`{&rQ z4^aLZ5}}SP8crP&0KkRBek9)MhBA66go%`)XoT@T zk@=1~d@9@wcqrx-&}g>fK05t?#T~RD{1o?}cpet__qqr#ZneV}E6vO;#lg-}M#~g5 z2CYXH_IRGsRPyrj;v6eowbl^jDVU84=HrW9YUQQ3R-7kT+mY~>r8@u+0Ele00p&GP z`y?R#DBHXtl$gN}xRLXdK6>(=kWI8GeQU?r(PX8j7~F9fKhFka;E^EwpYT-86(b>u zr=+zmkvT0u5w6?d+Rno6J>~+keO?4~9t^>SIt|0T35q-+@+*iKZJfqpDpP0dq>&ME z(=fc2Xlo9!qS-G7)R{jbIFvfI*XT6=gpHHk9p_?^Kn|LTcYh;N-Jn}C z$1`3PU$3$-`J*BZRe6=9SrQOp#}|3Wi%_+aEBy&zW6wPcRf!O0N-LvgmQ_^Zy8t$= zJe^A-pRIXOeKHN43)&VI7Zq>QHTz~K99yt-8tQ@lU60_QTrolPvT43H*axOBFCC^U zvfx+r?N>j}Q@(>5Xn6ibya!^R$E_vhG(zYe>~LO&oMbfx4Y?+UL6%8H zeuwRPcDc2SA$GgOM98A(QSS*zq&F>#r~U8+b+XN?wF+_y0sse5u}IDD+3pv`IR8D> z5!>b+WXh$M92f$e=D`(n1ckq9+Obfa&yjR5;WRFZJP;8zCFjw4U=@aV#AK8?jrwPW z6M-%Nx#h(0!oDFOgjn)ExDwVn0h9Cc;?>r2xQ%R*uDR0Cmnf{j{NXCy4Zo-Cp%tM@ zM~vu9#CVih3Y*NEpt|$?2uZ=T`8ShizBF}eZ3KO3G39dJtqa)A%|$vR0joIM9$AI$fK4ougHaST!72i z9X>I}R<(;FC!qK^x}PqJOttm==~rhYASoYf$l_{k8>2Ex;U-;_P!8Ak4@z&}uph+sP519+IU4WoQdJ1Mi1c>3PsJK|Nc^HL(&mwF+MlS6~1~xFB z)4lW2lxq<;H4Tt}w7E)=(Qnckk)Ym(}ZuiKe=c zws5VpxI^Uhr_bW@mxVI9+5D6iMy?c~(+y4U9FiPdrm3cdg|`IBzF*IAW|fpU4?qsK zbZGUvx!jdMcaERl*%6|f{Cr)3SlHMBZ)6a#sr?SlKa?%`?BGFMSYdW^n64a-6y*s` z2rd4Q4DlXNBx>(hF&lv&j95cd;H>bWK-SF%Sh{*cYLYr$H&1zGaRU|BN(RQGy8sAM z#3&6CJv;`flx$`Ehm0oViN(U+A|wh^Y@2IbU`vQJZ@CeyoY72fi64#ET>h!PmzE4d z$5lOGy0fz7CB-2Q)5@btgUd87s!1r{4zLl>9k6O(U1D+eSBlmZinh%`nqfSGhgm@p z`Idf2Vw19wc-`9cr-Sp562=9kPqqjo}-jXwZSva+p7M^ZLh#1|Xpm+{O(2_}z=I)%HvYXR+DJB}G(5xrFod7}v7;J; z1n7Af2`_g@5<$GXVUN%$nJFN709qV+p~Y!p{$#%>ae{b9aQb&(oGdNiY({$zw=a!P zzZthI>+h&D_AXlpa{mxBD{Iwet&{o4RoS8)J-?+U z35lyh!~sUNw$t_XRg_@)B>eYKOvl`TAq2xG4S3ZqrDpo@tf0_5DML0sAQxBq&kWxQ z*!JpAwFdACY+@QyqF&5!51mL9MN20_wvYlwBj;X}>)B^Vro@S}?ZM;lRf zH^bamq9)isp2z<%ItJL1=w?iIpK(O>Mh)sDqTxr=gQsgd8mH#tA|1Rb)PYw)4DFXZh;1KfO zZ5S~7;R97wT6vz)rlhH{jf^aTNrXvNdut_jXD|v99Yb`10k|6;3bnGxyBCm>^21N; zw8Xa31p(VCue)Jj{164}qUy`94>#|DABgW)aUA^pW#g4x7MPJDsrd-K+uIcnbKQVK z^grUNnYL~?5;qZn>%M#6IpBZkbECEJs}n&xe7WT0q^68v79WA;Wegp$BL|TnIQsUq zAtv!TE{=(Q2T(vR!GedJqm_zrEJY<9EG0qHaSRBhWTx?}E+0nnI%TK;I_JS-ppRL^ z-pkLyiN_aR<)~zB;EGE&KA7A;V_%cM3|cI9Vkc*0ELA+rzqyx_fWlk2y~`^kyRgJD zkovtF(K1gZTUjkE5$fJM*uRpP$CABR$D`5${JG$*{vpykw7DXGSGG^FZs9+bOunCv zaB7*EyV6is2Tc@1l`is8*3N17$xBNPo=t{Okw4zHdr*uPq^ihyfZFJ?^`hazQ-!e? zfEC0LLAejK)cw|vsIDVt-n?&-7hmC-unY@4zB9pZHr!o znsah_3?>^NLoBMco`S$n;Q)C>#ZYQyY!%)bTlVPJP+FG$i!}4)>J;UMA|0xKP?t<| z;?YsdN2!05q1?`ffP0rN*lNSH6(%wLK2bbfuz&E8H_NIB1COCr!Ym^1k}0Ls(@vM2 zk7PAn(7Q-K3+;xQtOwvKvwP4fGDzbsa7?IG4NZ?$2bNmIMlO6*frl_Ns-`>z07mT>DVA}HJfM%>htY`~$) zUQ#*@U&d(gsGs9BRb|Egw`*zW5q2uUS4M-mG$0GqE%Ij9t)%JODE95Ct*8h2k9%E- zTv`yI0)w$(OoBvFO-52uvS3>+uIGoIx>A;uvO|?@+MfJ=JwvP_f6&D$ihj0f!#+2GFn7oDY zz*do7!gmG7IwQF{%y4)JzNL0bqY>t8C%}uMrn|6X0GgAD;-t!SVS%8O#_Q@h3^p#a z&PJXuv)PYgplEEw1tdpa#-2g#O7fCF!P&(fh#32=47wR`DvSeyrirtTW*)t z=wBmvkK9D>lx`YsKP{{bDDzb9oa>Fw;$mrkw0m4f7v=OYbjHL4Fg0zb!Xdu|OGzct z&?@aZdyrt79(^A~-#-_f|~0*6?waAoccN zQt(dx==3boitjsvm6%ZCC6Z||xSlN?2(&Prnv1-VPrQeOrGFQn!f5_&h{-wbqOX1U z`EZ+`oiy~<>?mZvuD!6qPfSi)-RDPQ0kjDjwN0xHVC7(jnSXYpP6r_iQbVXRLqKf; zh7t-xx~u?Ld*rh3UWujF?_TM2qD|2U&HGbA?na7jsn!)bV{Lm^Rw9Gt1rZ?i9`uI)l)OF{pdil^Pg68D3?Vz98MgmtEBienIR5b+^o< zcMo*krR6G@i3iEey-!RNDS3G$|2vOjN)o08ci-W8pb})I8i?F;i3BXPk;=}JU=C)& z&YL=zcFcX$KX~^9Bs+{k~lL;rKSRukHIed2PV*7bg$x;)l9exlsa3ZmG zb`n=rw~N^+7AooX<*K7#mUtMIyFq@A>eH97Vk@l@FBN0)(0C%gtCU{;C`aFx#odQl zG1k0b>1NX~$FUWfPjsUkz+{d*z}VQBxRV51%daIOj87kb++m~)fsirevBZ1%rF}G% z+Sn1*KbsH;><@2vV_>zf8*zd90M=4JJL5jfkbi5$^bzfIywii!f zz=Vz2b|#PT=7F~qAg`<(Zgpr5!?QskAJX>mhSh2Ol&savErO8!9(Eq^ZjNY)(09K3 z3#7lR3dyf(bvVKC%np+?MTWSkGfC9 zY^cam6IL%Qd!zN~(vY1X!>@0sjDYC=F!(_c);2r37i5P)I{7dX$*RC-kq$>5KeH{i5drc@Mo zWdpW%xStB7LC0X&v9Bwy80^nPGV}!~7nQ?W_D?$PjbOj4P=MF_{Cm5@1*J6iuV;Gu zk8Q3mRGOO46&4W|umBA%OBm+}*#DW;%jSpy1f8W~9szV)T>wgtU}t6?nMh}5CWN+m z2h|gab&fvg!A8-gExE&iI74H4_i0-#RAvCu9u$FYWW>E4Unh~uX)|A%Rq%1q4~7EC zXA7vfU?(Ca^k3Q)Sa~r3b=i>mNPx8PnpngPuOyb56%=&cxa|%hE!=)+^^4a`~t}52ok#Sv*g^@P;#Oni;y|jxynYj0ZD{QIJdNt z1lRDUm)C<^QAz3L%#OemuyS+L=)WP~g7B|mDXy$zoz1qXm&cdtpzVMH7iIstZ^}>d zPCPFLE0qgWyfZh#o6cY_jR~f9wJ+G-NhC{Y5$b&wiWDHT=CofHxv$(cZWaA{=<)uv zdT@U-3$9j+XZ&P4)#n;ldK8XM6py$&nLk<}E4QVN2bg-0WHyV1Qx5{Pww1Z_<8~h% zc~+c8IR!=vt?|SVp4WA-)nthan;w8 zoDs@tHFA40JO8=qb%n$CR9{#pJxYC2wN>TGG`$Wa642CY3&Uis_25UaPmtW@q=+!8 zCI`3R8B*_iC9=c)W?EQcpi5G-ab~O|cb=$C;a?p>MVP*F=&dx-fENJqHmTcvw_>O@ z+r!;;8IK)nktEOxyFT|{4j7H_G{t1=|5_F)oQ1^EsY+MqtdE1Z2|ELWoSz`8vXBkT zmwq(G(+POCUMPlmT2j!$o8_mdO=lL{ME*Pf8xGx4eQvPx!0&~0rnM#MUmreDgcTbe zXfTY0713?GuDAMr`kJk8j+^g#vpDHKxfPZp`Ye~M^Y zet5E3=x=J~CHO(_p?V_{`SJ#sh+7dQSc8$@#0DwN7noFsp zo96iNp~I_zdpJT~J8z?+W}as^1saBz+s$SM7kc$aQJiSqeY*s?|UpqN*YwbPZ1W+@J_zKA95_RFN3|{UpMrxc*ygk2_ZIswbjuCKxJiR-&#=^ z#%ha+i&kRnEKZ2Pp`^+h$I53%!5Pg)2eBVWWt@suRiS+i6m2lL`4u2+xd; z7@H*(8U9YBh|mg*%B9k%;eRijvA4avFSg?@c5+`H<_ZVDp_%>F&>x{-2&sCN_#7~Z zH-%Rd+`9%x3OhfXd-KEnxEL1t{$>~IxGYb|2MJ1$->kwLp;i#&a>Co6n~a27U}}v5 z+AZxO^_=Yyir5eEgc&QMFPH6=qY9yd7dg0c7*?zKiiMo+B_5pkb^X1&HfaGNzNwJ& zSHP?(%+_aY3DQc{AD7LDR_W1q;uw5TpaI4OwH#=rFWKeTz<@?1=5;>j5lTBcdT1`_ zSxhe*HK^?rs-3aD{`?V{yv9*`)HLk zOcLzT8kdnKO4(4Wb=BId)$~#f$O^#y6;9-fW%p;5v7iH~EFX?2&DoqKTG&QZGH!~O z4{2jWK-zeC-h!%(JUqVY8*t*pvB`WB=sm|rQ)e{Afl@+xI#3jJJ63SHJVWZ3>#1$; zyN!vqTT*PSoX77W9_(tI!W~G&<|92z?_hu(30WnFA?+`Rfcp@Hrlpxver7`uIfr8T z!x>r0o>7#LFN^i^*!-gxY~62gkoT!j?9(|6h?y1F3)0HcJy`t|rp^Zx3By$FCb~-gM&X% z`(yze9+?P!M>ZO8)!#jL<%D!ik#pBX$xaPih5`BCfUVO4bccJVh*}j?pFTmpm8)@Q zy2W5*43JA1pFXuxH`iwY&v|Pdq0Y?+vc0Ynl{MXWz4_`0KR10yPy}rAlk%c;6#|P$ zzy-wLpeWec(L3m8HP}p%12uZk*WkvE>_>5H{EBPbQE2qA;sB8rSr?#8g@Ui`Sc62w zb7f)O!PinXaW|YnS4VHAFCXhcz8e-l*Ev1jg`Fp-rN5JeWE#QDUGl_8kYF}sGJY#z zWz-Yc8i3s7D{1wOZ(n9etvOm`Bz0%>b!7O0TNaOsK^7Xf$BE*9*(}#{7U#rpZw>n> z>4}ihg8qskm=$lVWlMrcLU|TT#|?pF;k+gZ5l{UX;S+JjJgyxvQv@p zlz9S0eZ;i;(n26;XyCwS81F)ys)(yXRrGQ)dx`Y{qlm@U8W*fVj(9o>x&3kSQ0`#5 zc{CER%x^Kl9i6ilDbYLYy@P`qODSVy5M(`vvTz_JlxhWAF@U?W%IqaE5+P@kP_aD@ zvdVHRMTHK=)-17>zCZ_0$)AE@|KjC(-hGq1Wx1VHFB)MD=) z+4A#g<8fg#!6_A8F3T-Vw@WaN{<$PZ+8Sq=j2>Bx3G08E7GylNh7 zRB6(udmLDT)&O!_mwKshGLr}RkWHG; zE}cdP0ROo2Cwq=aZeUy;7u&=9W5$7lmcPvMcq|T}P=YM0gGiB6Ryk-`tebpR_|Z>Y zS6iQc=OfzvIck4)xDaztrKFPi0>`B&GzkS|Z32*+KqJ9A=SpIUo{SCbCZd^WzflWD?6~0{Se&jjLDH#E zg;9i`Yzu3OmHC$LtHy>q;Hx=>e7JsjW!}Ue6(UR&7A-fmAvdP4NBh50=+M zLqjhxMPAxPOCNB{;khSoJ^dY;si!6?L|ST;1y1wrsTU*{C!%0-h2=Lh2ja?G1qHK{mX7I|FpjE zs*PjrH7yGu5hUGrR8f%Fd8)k0ubIU1w=+6C91mVMBCUfTV~`^KHJj}mT5%;o)(q}u1^Cl(>C+a`ydM- zn_{b6wyXFoZ>iM|%h$;z28P-3GVQ+m;IZJvt?hqB42X48*VGJEz&8P;u^jEoQ_#ZD zJ$)S$-iSRO{y;5Ymf^!hftVSqki6i{y)uT+?=Cy)cYj|I)AZBNa)gLw!r*IMCVro3 zt^?{Ucyl>D&c|0cZG;_JZ0+T00>vh(09X27-)H&ULC-LUGTr}e?1VL({ham#ygMzs zhM!yX4(zlVWvcyHJd-|hxcTGR0bx%$H3A;dPQS6sDFr1yf^M7B(I&X|1D^#V&z+^M zYzcqEnG=j)WKCiDe#?)34ekz8*B2A|z^OopZfMZPVS2JL41?|SFf?iK+# zKpm3B#SU_Z&6MjbtjG_*A@btmDLO7_7!-L>TxmvL;*Ei<#MMh~uCNL|nZ&cWUB95H zWPTt&d-mJxIB+UHz4+M=^+%|XNkxV_(&{z!6WIFG2_0oJ1*W^5mC$jTZ1K$Z5}H|v zL=I7Z$_(_*(_376HAs;ETkQ$0by4sxd5o8VG$ALcJxyt3rP{6d+(iK`17M|)`yUYn2w&>g3e zP5eCcX@y-bJ!ru{ge)U(LUBq2NA_<LTHU1*@8Q^2$onOb z3lcz6=|vfQXz9ZPt96)#mLmO2I5YLFpSRJSYy|Kl?cK)3#(7-Nf10yxK6b>9MT6|T z2$~9d6wuG5Mxe6`g$AMw+7sp!Z~PVmnVAK_syaR#R&TKH;EDb%OBK%hvi0RZsGtuZ zfu@1VWHEs1_=SU*+T*CMA!#1_6FK2ag{9GvGQU0$f-f`}Oc-D0&HnU226RC#(aH2E z1)d1*x~wL|&kLiJimY^*(SS%NUb#S&R6$8~6nO_=EErcNM?=`JCN`YOvu+`DCERla zxer>7PT1;jWo_&3uAON=Mw-mGnBa>xTM7)O`jh?oZn)zb)H;&Q<;mApu ziAk`b8T*@?FvtYAqL>x<8Y!)cE!< z&KI`{bQkMPwVcq$55&;tSGF;ZqP&3qf{0+hdqBDjBT;lk`9)#?1MHJoU0&$bKY9Kn zbVA1dqqV9)+;_;hwXDk%RB7*h9eTi&>2g{^>ruK|G4HIlIDM8y%bmedA5fXF~pe z=~~`C#(A*f>;xU|Ku`QBDRH|F01WMt_2XCh6}<@hg0F}_lGkm?U?F%&Tv8SDR4kAr zU~4^eV9hYxC-e5V)QWA$*TGiBbbYnU#ckwtSqVE<8TAcsks#0_4+jZ~<#GX8==OoE zlxJdnCm9z?N$}xVf#17M`+j{_Q|&W{ppWiZ1AH|`*A4tP^Vi2Ki0*70&IMPYRsGW1 zpN(>wvTre*YCL4vTYwgEL~D)@T=uy1p&4-2Sf_$$bIkG-S4)EcNQu`~?>g<4FHUy$ z?RNrUjKDWDxHy^MGzMglspKdW`ldV2Jt|ui@-q|KVwjn}h~uUmgT7hBS=tQz1BxYq zEk$FNihcnnbWD;+qK0km+Wsw3!BXf<^Q^doLH1htEfO9e@DF5n=m;XDPn+G)X2pj9 zmSSMwRDA{QL5_z*COmoW$Ood(f%d+Pq9;VoVh8B*!Mig0!(Xu%|6Mb7K5uh*BG~42 zNIQ@>$lW@$%0l=teTevG(WqMV12t`&fqghA#pY*%rwn57(*|MY2O9kx%JGy3Zc5Wb zou`POuM&kgscEPy7XpKer!Ax)gTgYNIs79ko<${b3+lV?4YO`;#Op$DJA>$eU4IAf zKHJ!lCZ`T(KtUWOqxYJ<)0HyK`*G4^6?>>IsZelK){$P`7otpdv-&p1z))Cml)^gD za@_3jBpNn}AZpRVRUaV_5C3d#ScVlFXsPr!b_e8*r4V^x<^1%c@VE%lw|<*p=e2k$ z_n^KSG;)KaSw#n-DB#p_%=exSip|Ff6$=0&XwKCR$#g+Jy5vKj9zLa_mQsca1q z76fPFb3-uA;rIh#oEHB^J8Q0jX}HPT_?_kTeMKj{a~}O@SOAvN9v+1}__rt#W4Tm$h)#_F5wF_>agf6>q#WI0gYmO7c$naN$Ga)xDoRz+E@6IVW8)$Gl3vJFB|R{2ZWQW z6qURbbH~vCq}CJqg||YJOK;XY9oOnOfscZdQ^Q5e5^DC8GaL!#?eX~}g*PD5Q(C$~ zU8eqK7))S{Ek6E?SNd_UQf31RYKXST3K=mKhG9;VEj;}9fQbm3AxteYOKF<}6Z{2! zB$2eHjgd197E<;MESmeqI$9oY{XGtZu#ph8v(btCbMwv!W@A-clIj7?l3>3Q%$@RF z5`|G2Pf^pR``aS8`n-UFt|#(lX6QCeO?N#nYXFL3M4g`DrG=6I^NxUGV4z5F-(uz` zERIQ#!<-h>0vCAGNz81U@)N8_&5rK(iYDCjSR@lyiAi^y-3=U`5!~c~l@l z>ouEMVqz-Q73E0)7%cU*>37r>s_M#vLgU+B&_Da6o`zV9EPY7VvYStZi_0 zz=7H+_c+5Z?`+ENV*OBs2#_^a}rl{MUAB83|B6_9hS}Ak%{QdESnR#f?1ww@*ceE#9@mp@C4u*|>fbbBzR7GK_!CARiR|H#T3W6L>^EGkt5C+j9$J-k zByAnQc)ov3DU7mzJjy}R)KxC3z5N0J=sqL2?ug5~pA_nT1OllB0=|yD(ErB;Kr69h zpuPvzz=p7p<0XzuNlXGC2cCQAA}tcpJ>O%nRR@Ul2w6TpBV!*Mfhpw{d21l)!uD~; zk;L4F!MdN@^=$%gL3PSvpZv~^YlXdlyaII`k`^Ls>q4{SeOP8}OweWW2Jhs;$pmyW z(fVhV)7goke7wc4?rD^HViGB984L6~k-4s#>*+yJ2WP-O%cei6N^BDEp$q03>sbWi zv|&{YvWiwBw!c0IO)GB)u~RYvxu6e;A@>Va`J9+guem6Cjm6dGqLo*UNfD*bQ;Bl0 z`33&ex7&`3Wc`i{y6dMKF3~%$<9-6N|D!$+$tIKG|Y7Ex#nlEImJbx$B}o z`B*B_<278zc$z$oRQ_%K!}5Pl@l#1Px50xS3e<-wcs}~ zWqyygwS5$RV|Z~H$U?a=_L)w=e2?N@7K}PsxdPo#8-a9M8(C$P^iC+ zLm*`-{)Ag?#L(T0UJq*9x|-4FUrz{$BDi@FXz~mqTaq1*3G2a{T3Le8()*^lGs}>~ zAt3tydb{ecsJ^c~bPeUuH4Y%HfHX*V2#ClKlG3e$bV_$AT>=u4f^-etDM)vB4Gr_& z&-agbf4S?fb=R6RckgreIcM)@KTq9tqr(8+f6A-Ts@u!wB0LQu6Z2(&s2CA0-``h@J+O5MB!A33{Cz%Oyg09v*ay(Y7#=+^_(% z#POR#+3%;ukPy?6Y*f&~`ww)(M^jb^1JK^&(o&4##Opg@J1lqj@)>MON^Sls!s+OVQeC9cJ4ea4ifi{`h59U+~ zwC;4u?qy6^v*|=k2T1mQUxm+){F{F*@>lc4oMM5vR|y)!rPkA{e;g5mMKc6^HA|W! ztf7~JPmqP@G`CR8HxZfK)23H$cI}(+2FQati_`+eNXcUzyVGY0R`vc+DigYf==+nn zh--%!*6NQV*MC*I>}vgaJ%QPbl>-YzJO!wf1K^G=^#6`3q!Xpn?C9`rysED~y%Gpj zLqy+j2{4Y5$?=$#7U6VS({JkWV?d)Nh!e9>R53p(Hv7tZrLcTGox`xEXwfRx?)~oO zmrERCEKSgIctyp;P7&v$>kqV!2rXG{-0LqBYAnrh#$nC5)XoKWn%>S3wOo+QD;jD` zDcql%od%0&##Ri!FQJ6UW zgMOe!8w25B+PQZj1j@=IY;O{n@!V$2-y_E`ben~JMepf}xj_7C!gLC+EuM`S*5LY; zJG|3Brr~~mvadPp4=+?xXtJUoQ73>En1R!3OW(|Mg^WL|*(>al$TpXr3N-%En+Gtd zXSEExGTR8x_AIs6sO-FLT3%U;tlx=+nH`8^*2O)Q#kQ2uxsPxLG*qL z^x#~CjRNvF!N>)NLD2k%d~D$$uf|%T22o8t!OMOt(Z)LnP6H6@v00L zNsvL!JGGHY)*o8grsvt{f_8$evVADn{d{nXX=|xUX#WZ2a#ly;)T@FA<5JoghLn5q z{V zlp?@MXK&obASrr$y&mW?8<7=f_iYfb9Kx&dRC-0Xlh~ojVVI)~Gs{Boao?ku5SmnbeRZ?9Dz_>@W8T z8Nm}yU{KT`PoP!L2x+&OjJw-{f=*B7WqivS`?|=JRy7K|4MrcH85y-;ScyNpOz7rF zLI02|oC?9!$Mk7kG3R>dTJ@B~rSlYC01YU**3u0UnUQ4h|{)4}d zqW^5g)MQFd7q4S32+xbG!%A^)H-hJ{W$4&BUVb3^I}V+!d(%4$L101mPQ206t~xNl3~osg*5 z1zeWa99*hzr+*>}{5EQ!rrARD6R4$Fbd7Y=$RHoe8cVqPZD@5am}3;7BiV6kEPct`odu}le|vR={V9ia$-V#8lS#j8pX4+%5+<3k65 zF6tcBm2o*zZ*=ohg{2vKhIr*jR~t(oVP_R3+o`D0*!!N%a5=>3dOr&;P+8f&>BWzCxW~-so z!i${D)_2-RzHhdi?-KYllkF6@a=$s(Of$kZf+dY4=@eW&f4F2`%F&%EQIWC-*EN#X z{S`qiHjRL*nzPK09~DVwCpn7fKaDqEFn5ao!`N!)_E`u zUx`bdr1qrg0De1AVxgMi=DDAOa8|P3kiQJ4Z~FDxXrKIbMB7Tszh&u-Z$ee|fIijEoXzIzS-UD_Qvz~*OC zXXdLTxFq7k5aRPFtTp7DKj?eiSckp#P;GWgB;uwAt`8&0oOnHIHn=@sc~=0havvn3 zs&BBW$Vnv92=+;FJ}0bS*>-EUtS$!p0*uF6YS=_!$MAr|0rjAxoP~^a*BJ%`fh?%* zy0%xz+9Y0?lbp$T#W(J7MG^jGtlyOt?nJv5VQ#0xiD*yyQQ^;(JG~L|9x7w=0Tay+ zoy$nle(m^L{1eoKOVpr}No4S~Yd1CEjr?G-RuB^y3^MDj>tkH+O?rmkD+x(dQkm7csrVS_FHy zwo^@#bT)RmbXN)2VU$*@hItk{6k+FfEj+Awcsf{9m$zMwrU=_407rR3V9+ESs5go0 z)lzJ;_#N+rePm?oYA?Xl3MyfBZew2mafV+i@sVS|4Mm{0&cZnRHg@EBwr?s=U$)78 zZWJjTf3A9gD z?#yEC>4*18Kg8F_KZ$C7Io3azZGhq|8MEu$p%fH0(Kb9=BB(a(ACE&MvN2E9{%9=? z+pw+ywAx+35pznA^T-5^q<5@LvhtFz8FiA!=CS&YIYdT<*&nCNO*XMn&s!i|qdze| z8hbXh&gyly>swZ{^;dqPK+cmH zBOpzG)0(}^K=$MNLX>XqXgDU)Pv3k9g*2?+OSAhc$nI#0)}B+=KrPvL)SGg8gr9UH z8}V{m`lHK^`Ac~$YXK#V@#)mJyq4!22hB*zKF>R1B{~t(m_|d~DQ%7$^!J{+l4C85 zp&BwT5)31z7Z5vr41r(K#B47aq}&J9xn^r|)K`ygvKXMzYmi^GykyVZ76Ls8D4hF@ z<#jQlWakT0j{1mlzs1`PS-1WOA-0a(~C!)#MsM zh+DATIqR(?qSaT3|0l96hb|n*d>b$E$SurT&ooeGLMsA359$cJ9V!I=D-4t1nCq-N zoq-8Ck&iqsC-#_~In0g&CatD{e1jmvbgmoT0LV;8j^le;VzY4==an9AYR?Lq%Z+K)|~rANto74Ftr1Y zsiRY%kjxKQW68!sz+(U=<-5)5lplu{>9Q=^cMeoO!ckJ`n&?K#r0C+*-rlsgMB%IC zShG|gk;GLybH5r&D?&^FNp5JzD$ky+ZRP!hDiGMz!DrY>jPzXSo{FP_Xb-lOoUkX_ zTlviQiaC&EV^_k^YzQZqh68%0@lnd_CCdVL&;8?!pxpBZvkuq z(3V*4b2N@#k*nyd_8~ZyjA4zT0o*H=Fzg8gpoEHy;FJyK< zO}Rpl8uxKPkH^UYOiJ$-J;TWv`UiulM)OhJrCkG_K-=V@euH4N53ti9s=Lz+455{qpL$h3VT^oxR~8-qa0E)=djId(;NeGP2r5Lbc*cjtf{N` zpL&hyyJJ^@zcGuQ+0;4<+j(uVKPjjqS}oR})&-e5@vZmdU_D;EGXHYJ%Rz$BJ%w$! zeZe;+F`Ru%y~Gp68d0P4L4hVC`FSTE9Oa+c)&qncIzF^E=+%-NbOP#w-*ERUPU)^> zMcFkcr;xA*6!)xq;q#RZp1p~w5- zolYTw-7%DE>ecj-C&b0&WkyzQj%bOg#$>HzOnyAvF zYX5srA?}uoCp5+$& z%qfW)Wk0ni>fwXa1EoKY$_yG><<3ERh!j~mIuOL!EhHl0Hf%>Hxx12nQGVtWKiJ#I zm=^krtaqjMvRagi%0t-cbb4llV%Lz~6wLwxCv3fJ-T&&e7L-Ikz(($7pZAT9!sofR zr~oTmM2LG`n(X&N*O5)W(H_qasmy=>c)j%+?T^SO}Vo=q@PGG&Q8vl(;2S2Zb zJ)48I^jvF;^T9%}@iSq~jD!ef7RXt8n(6MI)`1`aMtX)${w;<|kiX8i=EZz*PdO_} z`A;F>4g~apq%cD;TlP`(D;8+duDK)JyVY0xalMC@*P_>Gh%)h2olZvV$@2J zn4VfyUq`YuQSYuT$6e#D|MGDUC0p#lbrHK}VujsGhH*WxSr zgcy8JhmO-ls3DFG)DZQIvRV-}D6l|uMR7Cul1&jy1pdwRGQn3A#)~usW6DROtBd7} zRIfH7S*gUna50DT`CWhsKrEJ;t2wgsZYIcO2C1GV&N!`QTquAj8-FgY>`Qj%VLl+7 z@wg5(1te~5)4RKHDL`s!xvO%n7)D-49yQA-o)D+S8;eeD25WF0x3^iaadP(8i0bb- zJ0+YVoy#Ubq`QqKmbeBrWqDFGaJPkQXkuQ5mAX&?dB`TsCG4rUIdrkTA>T|pT>qH? zJcmHK%{INKiwYQ~P0P?QI&*(I?=s6P{`Y6-nP}5kH>1BPKb{CmK_qbd34Mys;Z*%e z$bLgWY`}kWRV;~C6Alk0zmg0q7h3A+YMI*ESa1#*r;`rF2xZZbx}Adm2a`FtCnO>O zSxR|q4+~WtN7wPJK}R{tWQG^wf=#u*>fEa+TZ+s@?H%ryw+{;~M*-b(KwSqL1c0Oc zWyk#oGR0P#&f&O+lKvYp)~}wn8(@I>a`*>H57d=qCc^ouR=%gd;jzu1W8uZXB>*Xq z1IslHEo~L*IM!_N7ODHtlU_e}q@&XIo5UpLF@Weoq%oaGuY3O}f>Ety!)(*q*v6)K zZi^QKaE^lOe{4Es`_rp#btt+z9Pf%GoMKVr|bcUTt(dZ9>MKTbMSIl3M}LL-`;0 zhnJ%R-G3a+PgY#B17y#l=l(6r^;UD6y?+Xhy!MR?W$(O*=)KPji(&}xAAHeq##!M% zxvM?TtiS|#mmg1ns${7Z;}`|VoVIO)CTPVS@p%i zD)q~3(|fIfITU4?D~FJJ>c7q4L-vyr6?`@-$Yy|^)F;=QEOT9=N)Ocd==Z&tyMzCp zkMgL^@}ktcj=jDWit z^lzH}>0Hf;VHT8lif zG~s%Y6lSX(!2tSXV?%<4<-cDbct>%+GWBl-<;{dGi9{z#XU>;5FTnQHby%m`qSa*l zMLjP1Abl#pD@l3Gof26^px#;kaXl_At)wu#E! zEspt`bQrSYi5^8!RFg8M^&WRVA5SS;g1mZrSXM{p`O*(&>iX?aWMO=?`|8Rl7DWN^@3j2dDGq{9* zs&(~6zK|Lj+U1$G2c~To;L4Qxg6)g{)mJ-8Wb4;=S)Ct_IPA~Az-)opYFq{d^nSZ8j9-bF>^n8KjIUBLZr39|LJ0$ZwV?1%hmITw&_DW zC>#n9_%j_pKYvbjH3W$NPg$nvLQunQZw`=oi{>&B?n&0qW<2D^VLd5h;=T;hbV{lU zcKRGWuT%UJ)kX30x>efp^D$o}s{-%3RRm1H6e9q0hCx@E9A7||a|HeSS%0_N;W?qF5(U*9~nqB}$mXKX0 zgo&oe>k}a3_oL+}#Z zK68o!Sbk5L^@sZ@BQu!3v^E?JfdK2ZT9l2Wqk_JEnwE<-a&L-iVYAbC$%8g|N+^$$ zOrf$B?Jd`r8oX_@LI(_GEp@6BURl>L%L7HobsB%`F{RL|zSqRs=Ny!SQe8wiAHtt=<4 zZK~~`fCqW<y(Zwx_K5hO#ef&r2mp-g|4J5)lW?axV`&8pxv3XrE?=&zCQP01<|VWOg_$V4o;# zYbPhTuekdO%IWygcaCFSIL1$3e=a75QL?83RzFfQ6plv;X&I;D<7*KxI_yy8Za&v} zuxh^SasPwIq^eD2p7%Mxo}hd{P^CRENlS%5VtI1K0ObR@TcR%T^R= zCQk>Bj_a&-_23WiwZlfZ*x&79iI%ILq=e(vvXgzvcYDI*E<3DG!dD(HZ69d;V{t+4 zcoun>)6kR2`)0FQFB)p9F`+AXjgXrvRKcvVwJFblbMRK{@U}ZRC}3^LmITbvp1VfjY(cF3XlkivZmu4 zrqsV(T|q3YtO61e<=PxbKR^DJE=CY&G)}9S?7e6ud)TjEzxIxI$9tx~w`ONePd$|A z81mkGnyP0n=)JKyiLx|QO7V`H)Z{M-jjxm`@M)n5$9@Yr40V#94jHoRMW*8E1kKaB z)o=>XKpbYXRN;UuXRHjDwz19!C~w{C22%dC?McPwy^GT`8ub=!$REbs+w%^lhx9rw zwaYAD^cv8m>)TOtD2pT=VXm=;`mArSC_lt_&QAYAyzp%fS$W*s;cNgCI z13DQc0r^{HJOs(+dcvMlL?V8X_W(IxncSSJF81MaE;~m@b+~BLpSSG$<47CBV!B;8 z+}!Wg%)g#>0iUgb`}Hix;ik=kdx#JD?wC%=TRr%IlYQxaSsp0(GSYcI6 zj(?Q->}@0u)HrX=$_8n%%x z)>3X9IQn1iP&4%No5k!7tvn_3^KJW;`h;mG*f=<@yyporN2sL1%^dd8@=2l4Q?z?A zOi(*U84Q*^`it)M> z*|G7dX)=qamltv?)pxN^T+7knxpmf^iMyQSM)uzJdv)lPjOXFRv%?J9x6|aG(`jrv zPgB(tD2;}!+jIy!!o|;*mgYsfM`C(ozS*6(`$d94D^7nxuqVB}KP8H@BBq5x7t5Rt z+$+aJr+reUA3(y?xF8V7J&acn1bQV0+_uYN0@weK+Y441%jQ=ysR%q^K#;P$x?H)8 HN#Oqh+>IeQ literal 21546 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 GIr6`= #include #include -#include "glm/ext/vector_float2.hpp" -#include "gsl/pointers" +#include #include "le2d/texture.hpp" + namespace miracle { class Lighthouse { public: explicit Lighthouse(gsl::not_null services); - void rotate_towards_cursor(glm::vec2 const& cursor_pos); + + void rotate_towards_cursor(glm::vec2 cursor_pos); void render(le::Renderer& renderer) const; private: - le::drawable::Circle m_sprite{}; - std::optional m_texture; gsl::not_null m_services; + std::optional m_texture; + le::drawable::Circle m_sprite{}; }; } // namespace miracle diff --git a/src/lighthouse.cpp b/src/lighthouse.cpp index d15aa82..5d2714b 100644 --- a/src/lighthouse.cpp +++ b/src/lighthouse.cpp @@ -3,7 +3,6 @@ #include "le2d/data_loader.hpp" namespace miracle { - Lighthouse::Lighthouse(gsl::not_null services) : m_services(services) { m_sprite.create(200.0f); @@ -13,12 +12,13 @@ Lighthouse::Lighthouse(gsl::not_null services) : m_se m_texture = asset_loader.load_texture("images/lighthouse.png"); m_sprite.texture = &m_texture.value(); } -void Lighthouse::rotate_towards_cursor(glm::vec2 const& cursor_pos) { + +void Lighthouse::rotate_towards_cursor(glm::vec2 cursor_pos) { auto const dist_sq = glm::length2((cursor_pos)); if (dist_sq > 0.1f) { auto const dist = std::sqrt(dist_sq); auto const normalized = cursor_pos / dist; - static constexpr auto up_v = glm::vec2(0.01f, 1.0f); + static constexpr auto up_v = glm::vec2(0.0f, 1.0f); auto const dot = glm::dot(normalized, up_v); auto const angle = glm::degrees(std::acos(dot)); m_sprite.transform.orientation = cursor_pos.x > 0.0f ? -angle : angle; @@ -26,4 +26,4 @@ void Lighthouse::rotate_towards_cursor(glm::vec2 const& cursor_pos) { } void Lighthouse::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } -} // namespace miracle \ No newline at end of file +} // namespace miracle From e4360a070d511b48efd4f5ad8cb8defbdfcfb3ce Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Wed, 18 Jun 2025 23:11:47 +0200 Subject: [PATCH 03/16] Add very basic enemy spawn and render --- src/enemy.cpp | 27 +++++++++++++++++++++++++++ src/enemy.hpp | 29 +++++++++++++++++++++++++++++ src/game.cpp | 5 ++++- src/game.hpp | 3 ++- src/lighthouse.cpp | 1 - 5 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/enemy.cpp create mode 100644 src/enemy.hpp diff --git a/src/enemy.cpp b/src/enemy.cpp new file mode 100644 index 0000000..ffc6167 --- /dev/null +++ b/src/enemy.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include "glm/ext/vector_float2.hpp" +#include "kvf/color.hpp" + +namespace miracle { +Enemy::Enemy(gsl::not_null services, glm::vec2 target_pos) : m_services(services), m_target_pos(target_pos) { + m_sprite.create(50.0f, kvf::red_v); + auto const framebuffer_size = m_services->get().framebuffer_size(); + std::cout << framebuffer_size.x; + m_sprite.transform.position = generate_spawn_pos(framebuffer_size); + // TODO: add proper textures +} + +void Enemy::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } + +void move() {} + + glm::vec2 Enemy::generate_spawn_pos(glm::vec2 screen_size) { + float radius = std::min(screen_size.x, screen_size.y) / 2.0f; + // this is temporary and will be replaced with a more robust random number generator + float angle = rand() / RAND_MAX * 2.0f * M_PI; + return {radius * std::cos(angle), radius * std::sin(angle)}; +} + +} // namespace miracle diff --git a/src/enemy.hpp b/src/enemy.hpp new file mode 100644 index 0000000..0ecec74 --- /dev/null +++ b/src/enemy.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "glm/ext/vector_float2.hpp" +#include "le2d/texture.hpp" + +namespace miracle { +class Enemy { + public: + explicit Enemy(gsl::not_null services, glm::vec2 target_pos); + + void render(le::Renderer& renderer) const; + void move(); + + private: + glm::vec2 generate_spawn_pos(glm::vec2 screen_size); + + gsl::not_null m_services; + std::optional m_texture; + le::drawable::Circle m_sprite{}; + glm::vec2 m_target_pos; +}; +} // namespace miracle diff --git a/src/game.cpp b/src/game.cpp index 9d8562a..bef2595 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4,7 +4,9 @@ #include "lighhouse.hpp" namespace miracle { -Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services) { m_circle.create(50.0f); } +Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services), m_enemy(services, {0, 0}) { + m_circle.create(70.0f); +} void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { auto const framebuffer_size = m_services->get().framebuffer_size(); @@ -19,5 +21,6 @@ void Game::tick([[maybe_unused]] kvf::Seconds const dt) { void Game::render(le::Renderer& renderer) const { m_circle.draw(renderer); m_lighthouse.render(renderer); + m_enemy.render(renderer); } } // namespace miracle diff --git a/src/game.hpp b/src/game.hpp index 3194ce2..0b43369 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -4,6 +4,7 @@ #include #include #include +#include "enemy.hpp" #include "lighhouse.hpp" namespace miracle { @@ -21,7 +22,7 @@ class Game { le::drawable::Circle m_circle{}; Lighthouse m_lighthouse; - + Enemy m_enemy; glm::vec2 m_cursor_pos{}; }; } // namespace miracle diff --git a/src/lighthouse.cpp b/src/lighthouse.cpp index 5d2714b..fc24645 100644 --- a/src/lighthouse.cpp +++ b/src/lighthouse.cpp @@ -5,7 +5,6 @@ namespace miracle { Lighthouse::Lighthouse(gsl::not_null services) : m_services(services) { m_sprite.create(200.0f); - auto const& data_loader = services->get(); auto const& context = services->get(); auto asset_loader = le::AssetLoader{&data_loader, &context}; From 972e408b7a7958861fa0ecf795c0d634c6c79a51 Mon Sep 17 00:00:00 2001 From: Karn Kaul Date: Thu, 19 Jun 2025 09:22:13 -0700 Subject: [PATCH 04/16] Add util/random, demo in app (#5) --- CMakeLists.txt | 2 ++ src/app.cpp | 2 ++ src/util/random.hpp | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/util/random.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 84f33cb..1004ebd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,3 +23,5 @@ file(GLOB_RECURSE sources LIST_DIRECTORIES false "src/*.[hc]pp") target_sources(${PROJECT_NAME} PRIVATE ${sources} ) + +target_precompile_headers(${PROJECT_NAME} REUSE_FROM le2d) diff --git a/src/app.cpp b/src/app.cpp index 7f17df4..1ea15cb 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace miracle { namespace { @@ -18,6 +19,7 @@ App::App() : m_context(context_ci), m_data_loader(le::FileDataLoader::upfind("as // test code, remove later. auto json = dj::Json{}; if (m_services.get().load_json(json, "test_file.json")) { log.info("loaded JSON: {}", json); } + log.debug("random_range(1, 100): {}", util::random_range(1, 100)); } void App::run() { diff --git a/src/util/random.hpp b/src/util/random.hpp new file mode 100644 index 0000000..7300b26 --- /dev/null +++ b/src/util/random.hpp @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +namespace miracle::util { +/// \brief Wrapper that reuses the same random engine for all calls. +class Random { + public: + template + [[nodiscard]] static auto in_range(Type const min, Type const max) -> Type { + if constexpr (std::integral) { + auto dist = std::uniform_int_distribution{min, max}; + return dist(m_engine); + } else { + auto dist = std::uniform_real_distribution{min, max}; + return dist(m_engine); + } + } + + private: + inline static std::default_random_engine m_engine{std::random_device{}()}; +}; + +/// \returns Random value in the range [min, max]. +template +[[nodiscard]] auto random_range(Type const min, Type const max) -> Type { + return Random::in_range(min, max); +} + +/// \returns Random index in the range [0, size - 1]. +/// \pre size must be greater than 0. +[[nodiscard]] inline auto random_index(std::size_t const size) -> std::size_t { + KLIB_ASSERT(size > 0); + return Random::in_range(0uz, size - 1); +} +} // namespace miracle::util From 30c31537cf70984800f4ff4588d8c537720108d0 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Fri, 20 Jun 2025 18:02:48 +0200 Subject: [PATCH 05/16] Refactor Enemy - add targeted movement - change spawn point generation --- src/enemy.cpp | 21 ++++++++++++--------- src/enemy.hpp | 7 ++++--- src/game.cpp | 4 +++- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/enemy.cpp b/src/enemy.cpp index ffc6167..fc99330 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -1,27 +1,30 @@ #include #include -#include #include "glm/ext/vector_float2.hpp" +#include "glm/geometric.hpp" #include "kvf/color.hpp" +#include "util/random.hpp" namespace miracle { -Enemy::Enemy(gsl::not_null services, glm::vec2 target_pos) : m_services(services), m_target_pos(target_pos) { +Enemy::Enemy(gsl::not_null services, glm::vec2 target_pos, float move_speed) + : m_services(services), m_target_pos(target_pos), m_move_speed(move_speed) { m_sprite.create(50.0f, kvf::red_v); auto const framebuffer_size = m_services->get().framebuffer_size(); - std::cout << framebuffer_size.x; m_sprite.transform.position = generate_spawn_pos(framebuffer_size); // TODO: add proper textures } void Enemy::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } -void move() {} +void Enemy::move(kvf::Seconds const dt) { + glm::vec2 direction = glm::normalize(m_target_pos - m_sprite.transform.position); + glm::vec2 movement = direction * m_move_speed * dt.count(); + m_sprite.transform.position += movement; +} - glm::vec2 Enemy::generate_spawn_pos(glm::vec2 screen_size) { - float radius = std::min(screen_size.x, screen_size.y) / 2.0f; - // this is temporary and will be replaced with a more robust random number generator - float angle = rand() / RAND_MAX * 2.0f * M_PI; +glm::vec2 Enemy::generate_spawn_pos(glm::vec2 screen_size) { + float radius = std::max(screen_size.x, screen_size.y) / 2.0f; + float angle = util::random_range(0.0f, 2.0f * std::numbers::pi_v); return {radius * std::cos(angle), radius * std::sin(angle)}; } - } // namespace miracle diff --git a/src/enemy.hpp b/src/enemy.hpp index 0ecec74..db02d5a 100644 --- a/src/enemy.hpp +++ b/src/enemy.hpp @@ -13,17 +13,18 @@ namespace miracle { class Enemy { public: - explicit Enemy(gsl::not_null services, glm::vec2 target_pos); + explicit Enemy(gsl::not_null services, glm::vec2 target_pos, float move_speed); void render(le::Renderer& renderer) const; - void move(); + void move(kvf::Seconds dt); private: - glm::vec2 generate_spawn_pos(glm::vec2 screen_size); + static glm::vec2 generate_spawn_pos(glm::vec2 screen_size); gsl::not_null m_services; std::optional m_texture; le::drawable::Circle m_sprite{}; glm::vec2 m_target_pos; + float m_move_speed; }; } // namespace miracle diff --git a/src/game.cpp b/src/game.cpp index bef2595..05b1da1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,10 +1,11 @@ #include #include #include +#include "glm/ext/vector_float2.hpp" #include "lighhouse.hpp" namespace miracle { -Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services), m_enemy(services, {0, 0}) { +Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services), m_enemy(services, glm::vec2{0.0f, 0.0f}, 50.0f) { m_circle.create(70.0f); } @@ -16,6 +17,7 @@ void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { void Game::tick([[maybe_unused]] kvf::Seconds const dt) { m_circle.transform.position = m_cursor_pos; m_lighthouse.rotate_towards_cursor(m_cursor_pos); + m_enemy.move(dt); } void Game::render(le::Renderer& renderer) const { From c1b20c7151ba5a2b930b4d776cbcc3c0e5acf95c Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Fri, 20 Jun 2025 19:23:18 +0200 Subject: [PATCH 06/16] Include to fix compilation on Win --- src/enemy.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/enemy.cpp b/src/enemy.cpp index fc99330..cc44fbd 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "glm/ext/vector_float2.hpp" #include "glm/geometric.hpp" #include "kvf/color.hpp" From 3e865742312855852108abb8cb57d100f2ffc382 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sat, 21 Jun 2025 15:42:23 +0200 Subject: [PATCH 07/16] Refactor small issues - move spawn point position generation to the 'util::random' namespace - add default initialization to primitives - remove redudant includes - add 'const' to local variables where appropriate - rename 'move' function to avoid naming conflicts --- src/enemy.cpp | 16 ++++++---------- src/enemy.hpp | 8 +++----- src/game.cpp | 3 +-- src/lighthouse.cpp | 2 +- src/util/random.hpp | 6 ++++++ 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/enemy.cpp b/src/enemy.cpp index cc44fbd..c1c2b08 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "glm/ext/vector_float2.hpp" #include "glm/geometric.hpp" #include "kvf/color.hpp" @@ -11,21 +10,18 @@ Enemy::Enemy(gsl::not_null services, glm::vec2 target : m_services(services), m_target_pos(target_pos), m_move_speed(move_speed) { m_sprite.create(50.0f, kvf::red_v); auto const framebuffer_size = m_services->get().framebuffer_size(); - m_sprite.transform.position = generate_spawn_pos(framebuffer_size); + auto const radius = static_cast(std::max(framebuffer_size.x, framebuffer_size.y)) / 2.0f; + + m_sprite.transform.position = util::get_random_location_on_radius(radius); // TODO: add proper textures } void Enemy::render(le::Renderer& renderer) const { m_sprite.draw(renderer); } -void Enemy::move(kvf::Seconds const dt) { - glm::vec2 direction = glm::normalize(m_target_pos - m_sprite.transform.position); - glm::vec2 movement = direction * m_move_speed * dt.count(); +void Enemy::translate(kvf::Seconds const dt) { + glm::vec2 const direction = glm::normalize(m_target_pos - m_sprite.transform.position); + glm::vec2 const movement = direction * m_move_speed * dt.count(); m_sprite.transform.position += movement; } -glm::vec2 Enemy::generate_spawn_pos(glm::vec2 screen_size) { - float radius = std::max(screen_size.x, screen_size.y) / 2.0f; - float angle = util::random_range(0.0f, 2.0f * std::numbers::pi_v); - return {radius * std::cos(angle), radius * std::sin(angle)}; -} } // namespace miracle diff --git a/src/enemy.hpp b/src/enemy.hpp index db02d5a..9969371 100644 --- a/src/enemy.hpp +++ b/src/enemy.hpp @@ -16,15 +16,13 @@ class Enemy { explicit Enemy(gsl::not_null services, glm::vec2 target_pos, float move_speed); void render(le::Renderer& renderer) const; - void move(kvf::Seconds dt); + void translate(kvf::Seconds dt); private: - static glm::vec2 generate_spawn_pos(glm::vec2 screen_size); - gsl::not_null m_services; std::optional m_texture; le::drawable::Circle m_sprite{}; - glm::vec2 m_target_pos; - float m_move_speed; + glm::vec2 m_target_pos{}; + float m_move_speed{}; }; } // namespace miracle diff --git a/src/game.cpp b/src/game.cpp index 05b1da1..9f89b99 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,7 +1,6 @@ #include #include #include -#include "glm/ext/vector_float2.hpp" #include "lighhouse.hpp" namespace miracle { @@ -17,7 +16,7 @@ void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { void Game::tick([[maybe_unused]] kvf::Seconds const dt) { m_circle.transform.position = m_cursor_pos; m_lighthouse.rotate_towards_cursor(m_cursor_pos); - m_enemy.move(dt); + m_enemy.translate(dt); } void Game::render(le::Renderer& renderer) const { diff --git a/src/lighthouse.cpp b/src/lighthouse.cpp index fc24645..d40c723 100644 --- a/src/lighthouse.cpp +++ b/src/lighthouse.cpp @@ -7,7 +7,7 @@ Lighthouse::Lighthouse(gsl::not_null services) : m_se m_sprite.create(200.0f); auto const& data_loader = services->get(); auto const& context = services->get(); - auto asset_loader = le::AssetLoader{&data_loader, &context}; + auto const asset_loader = le::AssetLoader{&data_loader, &context}; m_texture = asset_loader.load_texture("images/lighthouse.png"); m_sprite.texture = &m_texture.value(); } diff --git a/src/util/random.hpp b/src/util/random.hpp index 7300b26..fd590c4 100644 --- a/src/util/random.hpp +++ b/src/util/random.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -34,4 +35,9 @@ template KLIB_ASSERT(size > 0); return Random::in_range(0uz, size - 1); } +// Returns a random coordinate on the specified radius +[[nodiscard]] static auto get_random_location_on_radius(float radius) -> glm::vec2 { + float const angle = random_range(0.0f, 2.0f * std::numbers::pi_v); + return {radius * std::cos(angle), radius * std::sin(angle)}; +} } // namespace miracle::util From 8ec1158b5ee12fe5605ba555c14f56625b9a61ca Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sat, 21 Jun 2025 15:51:23 +0200 Subject: [PATCH 08/16] Add missing namespace --- src/util/random.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/random.hpp b/src/util/random.hpp index fd590c4..f761e3e 100644 --- a/src/util/random.hpp +++ b/src/util/random.hpp @@ -2,6 +2,7 @@ #include #include #include +#include #include namespace miracle::util { From 5862bef6226239d8efd05815e288389375718894 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sat, 21 Jun 2025 15:59:41 +0200 Subject: [PATCH 09/16] Change function signature --- src/util/random.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/random.hpp b/src/util/random.hpp index f761e3e..5c6cef0 100644 --- a/src/util/random.hpp +++ b/src/util/random.hpp @@ -37,7 +37,7 @@ template return Random::in_range(0uz, size - 1); } // Returns a random coordinate on the specified radius -[[nodiscard]] static auto get_random_location_on_radius(float radius) -> glm::vec2 { +[[nodiscard]] inline auto get_random_location_on_radius(float radius) -> glm::vec2 { float const angle = random_range(0.0f, 2.0f * std::numbers::pi_v); return {radius * std::cos(angle), radius * std::sin(angle)}; } From acef7e4084149d7fbff54c286e36dee1a2e51149 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sat, 21 Jun 2025 23:06:58 +0200 Subject: [PATCH 10/16] Add enemy waves --- src/game.cpp | 29 ++++++++++++++++++++++++++--- src/game.hpp | 8 ++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 9f89b99..ca0b464 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,11 +1,17 @@ #include #include #include +#include +#include +#include "enemy.hpp" +#include "kvf/time.hpp" #include "lighhouse.hpp" +#include "util/random.hpp" namespace miracle { -Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services), m_enemy(services, glm::vec2{0.0f, 0.0f}, 50.0f) { +Game::Game(gsl::not_null services) : m_services(services), m_lighthouse(services) { m_circle.create(70.0f); + spawn_wave(); } void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { @@ -14,14 +20,31 @@ void Game::on_cursor_pos(le::event::CursorPos const& cursor_pos) { } void Game::tick([[maybe_unused]] kvf::Seconds const dt) { + if (!m_running) { return; } + m_time_since_last_wave_spawn += dt; + if (m_time_since_last_wave_spawn >= m_wave_interval) { + spawn_wave(); + m_time_since_last_wave_spawn = kvf::Seconds{}; + } + for (auto& enemy : m_enemies) { enemy.translate(dt); } m_circle.transform.position = m_cursor_pos; m_lighthouse.rotate_towards_cursor(m_cursor_pos); - m_enemy.translate(dt); } void Game::render(le::Renderer& renderer) const { m_circle.draw(renderer); m_lighthouse.render(renderer); - m_enemy.render(renderer); + for (auto const& enemy : m_enemies) { enemy.render(renderer); } } + +void Game::spawn_wave() { + ++m_wave_count; + m_wave_interval += kvf::Seconds{5}; + std::vector new_wave; + std::size_t const wave_size = m_wave_count * 3; + new_wave.reserve(wave_size); + for (int i = 0; i < wave_size; ++i) { new_wave.emplace_back(m_services, glm::vec2{0.0f, 0.0f}, util::random_range(35.0f, 65.0f)); } + m_enemies.insert(m_enemies.end(), std::make_move_iterator(new_wave.begin()), std::make_move_iterator(new_wave.end())); +} + } // namespace miracle diff --git a/src/game.hpp b/src/game.hpp index 0b43369..bf8c163 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -16,13 +16,17 @@ class Game { void tick(kvf::Seconds dt); void render(le::Renderer& renderer) const; - + void spawn_wave(); private: gsl::not_null m_services; le::drawable::Circle m_circle{}; Lighthouse m_lighthouse; - Enemy m_enemy; glm::vec2 m_cursor_pos{}; + int m_wave_count{}; + bool m_running{true}; + kvf::Seconds m_wave_interval{}; + kvf::Seconds m_time_since_last_wave_spawn{}; + std::vector m_enemies{}; }; } // namespace miracle From d76611d941366e73389908179eb8d6f91a11d2c7 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sun, 22 Jun 2025 00:00:38 +0200 Subject: [PATCH 11/16] Fix iterator type --- src/game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game.cpp b/src/game.cpp index ca0b464..b9a49e2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -43,7 +43,7 @@ void Game::spawn_wave() { std::vector new_wave; std::size_t const wave_size = m_wave_count * 3; new_wave.reserve(wave_size); - for (int i = 0; i < wave_size; ++i) { new_wave.emplace_back(m_services, glm::vec2{0.0f, 0.0f}, util::random_range(35.0f, 65.0f)); } + for (std::size_t i = 0; i < wave_size; ++i) { new_wave.emplace_back(m_services, glm::vec2{0.0f, 0.0f}, util::random_range(35.0f, 65.0f)); } m_enemies.insert(m_enemies.end(), std::make_move_iterator(new_wave.begin()), std::make_move_iterator(new_wave.end())); } From 26b93230e7e01817ac5e4f0949cc64b964e33970 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sun, 22 Jun 2025 00:42:38 +0200 Subject: [PATCH 12/16] Fix type `int` -> `size_t` --- src/game.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game.hpp b/src/game.hpp index bf8c163..194bbce 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "enemy.hpp" #include "lighhouse.hpp" @@ -23,7 +24,7 @@ class Game { le::drawable::Circle m_circle{}; Lighthouse m_lighthouse; glm::vec2 m_cursor_pos{}; - int m_wave_count{}; + std::size_t m_wave_count{}; bool m_running{true}; kvf::Seconds m_wave_interval{}; kvf::Seconds m_time_since_last_wave_spawn{}; From 19bc66870eb0d287fd67eb723098042f4962ea1f Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sun, 22 Jun 2025 00:43:38 +0200 Subject: [PATCH 13/16] Add simple collision detection and enemy removal --- src/enemy.cpp | 8 ++++++-- src/enemy.hpp | 5 +++++ src/game.cpp | 7 ++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/enemy.cpp b/src/enemy.cpp index c1c2b08..f60dd1f 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -7,8 +7,8 @@ namespace miracle { Enemy::Enemy(gsl::not_null services, glm::vec2 target_pos, float move_speed) - : m_services(services), m_target_pos(target_pos), m_move_speed(move_speed) { - m_sprite.create(50.0f, kvf::red_v); + : m_services(services), m_target_pos(target_pos), m_move_speed(move_speed), m_diameter(util::random_range(40.0f, 60.0f)) { + m_sprite.create(m_diameter, kvf::red_v); auto const framebuffer_size = m_services->get().framebuffer_size(); auto const radius = static_cast(std::max(framebuffer_size.x, framebuffer_size.y)) / 2.0f; @@ -24,4 +24,8 @@ void Enemy::translate(kvf::Seconds const dt) { m_sprite.transform.position += movement; } +void Enemy::check_collision(glm::vec2 pos, float radius) { + if (glm::distance(pos, m_sprite.transform.position) < radius + m_diameter / 2) { m_health = 0; } +} + } // namespace miracle diff --git a/src/enemy.hpp b/src/enemy.hpp index 9969371..439c319 100644 --- a/src/enemy.hpp +++ b/src/enemy.hpp @@ -17,6 +17,9 @@ class Enemy { void render(le::Renderer& renderer) const; void translate(kvf::Seconds dt); + // There are temporary parameters, will take the light beam object once it is created + void check_collision(glm::vec2 pos, float radius); + [[nodiscard]] std::size_t get_health() const { return m_health; } private: gsl::not_null m_services; @@ -24,5 +27,7 @@ class Enemy { le::drawable::Circle m_sprite{}; glm::vec2 m_target_pos{}; float m_move_speed{}; + float m_diameter{}; + std::size_t m_health{100}; }; } // namespace miracle diff --git a/src/game.cpp b/src/game.cpp index b9a49e2..1eaf63d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "enemy.hpp" @@ -26,7 +27,11 @@ void Game::tick([[maybe_unused]] kvf::Seconds const dt) { spawn_wave(); m_time_since_last_wave_spawn = kvf::Seconds{}; } - for (auto& enemy : m_enemies) { enemy.translate(dt); } + for (auto& enemy : m_enemies) { + enemy.check_collision(m_circle.transform.position, 50.0f); + enemy.translate(dt); + } + std::erase_if(m_enemies, [](Enemy const& enemy) { return !enemy.get_health(); }); m_circle.transform.position = m_cursor_pos; m_lighthouse.rotate_towards_cursor(m_cursor_pos); } From e241f8a1dc3eadc676f506a445949025815c970f Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sun, 22 Jun 2025 00:48:46 +0200 Subject: [PATCH 14/16] Minor formatting and include changes --- src/game.cpp | 1 - src/game.hpp | 1 + src/util/random.hpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 1eaf63d..986097d 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -51,5 +51,4 @@ void Game::spawn_wave() { for (std::size_t i = 0; i < wave_size; ++i) { new_wave.emplace_back(m_services, glm::vec2{0.0f, 0.0f}, util::random_range(35.0f, 65.0f)); } m_enemies.insert(m_enemies.end(), std::make_move_iterator(new_wave.begin()), std::make_move_iterator(new_wave.end())); } - } // namespace miracle diff --git a/src/game.hpp b/src/game.hpp index 194bbce..19c0b2d 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -18,6 +18,7 @@ class Game { void tick(kvf::Seconds dt); void render(le::Renderer& renderer) const; void spawn_wave(); + private: gsl::not_null m_services; diff --git a/src/util/random.hpp b/src/util/random.hpp index 5c6cef0..7f6976f 100644 --- a/src/util/random.hpp +++ b/src/util/random.hpp @@ -2,9 +2,9 @@ #include #include #include +#include #include #include - namespace miracle::util { /// \brief Wrapper that reuses the same random engine for all calls. class Random { From d378bf9c57ee62229c911e0c6567ed52275ac703 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sun, 22 Jun 2025 18:31:26 +0200 Subject: [PATCH 15/16] Extract enemy starter movement values to dedicated struct --- src/enemy.cpp | 5 +++-- src/enemy.hpp | 3 ++- src/enemy_params.hpp | 7 +++++++ src/game.cpp | 6 ++++-- 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 src/enemy_params.hpp diff --git a/src/enemy.cpp b/src/enemy.cpp index f60dd1f..1b49bf5 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -1,13 +1,14 @@ #include #include +#include "enemy_params.hpp" #include "glm/ext/vector_float2.hpp" #include "glm/geometric.hpp" #include "kvf/color.hpp" #include "util/random.hpp" namespace miracle { -Enemy::Enemy(gsl::not_null services, glm::vec2 target_pos, float move_speed) - : m_services(services), m_target_pos(target_pos), m_move_speed(move_speed), m_diameter(util::random_range(40.0f, 60.0f)) { +Enemy::Enemy(gsl::not_null services, EnemyParams const& params) + : m_services(services), m_target_pos(params.target_pos), m_move_speed(params.move_speed), m_diameter(util::random_range(40.0f, 60.0f)) { m_sprite.create(m_diameter, kvf::red_v); auto const framebuffer_size = m_services->get().framebuffer_size(); auto const radius = static_cast(std::max(framebuffer_size.x, framebuffer_size.y)) / 2.0f; diff --git a/src/enemy.hpp b/src/enemy.hpp index 439c319..03c37ee 100644 --- a/src/enemy.hpp +++ b/src/enemy.hpp @@ -7,13 +7,14 @@ #include #include #include +#include "enemy_params.hpp" #include "glm/ext/vector_float2.hpp" #include "le2d/texture.hpp" namespace miracle { class Enemy { public: - explicit Enemy(gsl::not_null services, glm::vec2 target_pos, float move_speed); + explicit Enemy(gsl::not_null services, EnemyParams const& params); void render(le::Renderer& renderer) const; void translate(kvf::Seconds dt); diff --git a/src/enemy_params.hpp b/src/enemy_params.hpp new file mode 100644 index 0000000..cfd23b7 --- /dev/null +++ b/src/enemy_params.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "glm/ext/vector_float2.hpp" + +struct EnemyParams { + glm::vec2 target_pos{}; + float move_speed{}; +}; diff --git a/src/game.cpp b/src/game.cpp index 986097d..fc79e5e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,10 +1,10 @@ #include #include #include -#include #include #include #include "enemy.hpp" +#include "enemy_params.hpp" #include "kvf/time.hpp" #include "lighhouse.hpp" #include "util/random.hpp" @@ -48,7 +48,9 @@ void Game::spawn_wave() { std::vector new_wave; std::size_t const wave_size = m_wave_count * 3; new_wave.reserve(wave_size); - for (std::size_t i = 0; i < wave_size; ++i) { new_wave.emplace_back(m_services, glm::vec2{0.0f, 0.0f}, util::random_range(35.0f, 65.0f)); } + for (std::size_t i = 0; i < wave_size; ++i) { + new_wave.emplace_back(m_services, EnemyParams{.target_pos = glm::vec2{0.0f, 0.0f}, .move_speed = util::random_range(35.0f, 65.0f)}); + } m_enemies.insert(m_enemies.end(), std::make_move_iterator(new_wave.begin()), std::make_move_iterator(new_wave.end())); } } // namespace miracle From 1c2b973bafec6b68977e8667072a47b5fd867245 Mon Sep 17 00:00:00 2001 From: NikolaJelic Date: Sun, 22 Jun 2025 21:39:51 +0200 Subject: [PATCH 16/16] Fix vec2 include --- src/enemy.cpp | 2 +- src/enemy.hpp | 2 +- src/enemy_params.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/enemy.cpp b/src/enemy.cpp index 1b49bf5..5f5f757 100644 --- a/src/enemy.cpp +++ b/src/enemy.cpp @@ -1,8 +1,8 @@ #include #include #include "enemy_params.hpp" -#include "glm/ext/vector_float2.hpp" #include "glm/geometric.hpp" +#include "glm/vec2.hpp" #include "kvf/color.hpp" #include "util/random.hpp" diff --git a/src/enemy.hpp b/src/enemy.hpp index 03c37ee..ab6da0e 100644 --- a/src/enemy.hpp +++ b/src/enemy.hpp @@ -8,7 +8,7 @@ #include #include #include "enemy_params.hpp" -#include "glm/ext/vector_float2.hpp" +#include "glm/vec2.hpp" #include "le2d/texture.hpp" namespace miracle { diff --git a/src/enemy_params.hpp b/src/enemy_params.hpp index cfd23b7..aa8e588 100644 --- a/src/enemy_params.hpp +++ b/src/enemy_params.hpp @@ -1,5 +1,5 @@ #pragma once -#include "glm/ext/vector_float2.hpp" +#include "glm/vec2.hpp" struct EnemyParams { glm::vec2 target_pos{};