From 4e7e6be7a8987a9ead679b1e059353c9d5269bad Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Sat, 24 Sep 2022 11:04:09 -0500 Subject: [PATCH] Turn orders work, move selection works Co-authored-by: sigonasr2 --- C++/scripts/md5 | 2 +- C++ProjectTemplate | Bin 538960 -> 543576 bytes co2.png | Bin 0 -> 5408 bytes diff | 14 + main.cpp | 99 +- olcPixelGameEngine.h | 6191 ++++++++++++++++++++++++++++++++++++++++++ pixelGameEngine.h | 8 +- 7 files changed, 6290 insertions(+), 24 deletions(-) create mode 100644 co2.png create mode 100644 diff create mode 100644 olcPixelGameEngine.h diff --git a/C++/scripts/md5 b/C++/scripts/md5 index 3e77b3f..7cf8083 100644 --- a/C++/scripts/md5 +++ b/C++/scripts/md5 @@ -1,3 +1,3 @@ build.sh:530634457ea9041267c05d4ced95eee1 - commit.sh:d03a46e721060c22ccb146e19d27e70a - -web.sh:241ce74055952325f82f009b494df250 - +web.sh:3dcc2fe7e036359eedd257a864e9a1e1 - diff --git a/C++ProjectTemplate b/C++ProjectTemplate index dc59e7a1cd0c8e4b938a5c15df93a4c59a04fde9..29e3bbbe955517170f40d6615465688971ba7d76 100755 GIT binary patch literal 543576 zcmeFa4R}<=^#{BOL;{KlDmJxRgH;=YHT(j=rg$*C*o&QKG(msRE{Y zNp!h@{@}BK|LX6P{v-rd-)22-FHZ_;eXq?ClBsXA9_gmzHX&`ty4df$!$td*Qa)9W zq@a4OKNs|^55<1xNqwqsrB9-we_2vY`+FkF=%c>vdP`+J`+JtC=2PF)27TszShMJ0 z+RKymlCKaA+TVN668zP-YHu#;kzR)SQEI(dw&yhtS#liIx3aU-%SsncoqT%PqSMMs z%WLaStIL{t+SJJtsw*Z;Vi8ELbo?WmI{%`4ABH7FIH^WoVuOGcKPo#)mJ|KeMQhF) z{=vw(6Y{ft!DoIk?eVUjbBTuJraDAJAL1v6T*I&9ifBk5sR>W|WiO}__w{} z_sjlLu;8At+kgL!$v>H~dhFW6W&$1eQ#8isJ0ecW$!ItpzV(>+_%R;zH>1<>_}o7- zKK{pFi;rL9!M_vC* zM?K`9?J@2bdGzZ{4}C88(ErD9Zt>)N+@oK2ddS)CVQ;J9h~x3U!9&jHJ@7dm{=t_P zpZ_Nw@?7Dew@*CuljxzJ7LRr>LEMVh-^m_+>7THxc>eHdkN%#D#48^E-+1(^++(~# z9{PFs>+$(ld-%gz2oq195)Z%kZx12i1heti^_A?Lu zr+fIlS3UR#J>+Tg(DR2Ld@lCz&qsUc=MInYO7zfki-)}(<6-CZ9_=pmh_g3)=wYpg zKJW6#Pl`S8UwYWp>mL33nTOqWc(K z%<}NJ{T|~r+e2>+9_>Epflu?8N2ht%&(R)oe#;~8o$En=nMZuT*<&2<85Q5Zo$28x zryy>|^XEG}`gN$sxX<^n!z(<-@p~S6{-cK-zT+X!#~%5{w>|W^+JnzX5B%{S_z@m@ zF7#;k4<7pYlSkfC>Y=wsJoqp6(C0D_`FlLGd%L`>pblF9*=fwJ=)Fj(B~A7 zd3~6Nf1Byi?uj1#s`1eCIuCt5<}vTqc<@Q^$cw)3LI1pmo@qZNUfy!AM_ilc;cqYT z=vS3T-1)Xge81mg-ktBU&U)R0{x}bL-u9p$=@F0q=%F8&gD)Nb?9WyYIWs-v8SjyY zoCn77#^r1ed;69L{~vqU?fK|0R59Q)-h)r2hku~`!FY1s>yfv-=%G(E8&{uidCbQ* zJ?!%{$aBJgUio~ddhl=eh=VtJ%&$EjacZu|x_F~U-tvNn|M}QM&siShc(aEdMtJzc zY$!UOer`v<#$geey&e-BpA7!%^L+?Ed;)g;S`r`_KADh%J|Xy3!ml4I@Q%bU0sR>4 zif6YAKB*G#E1W$)w{THORmqal>Y9?O`MER8D#}ad7cDF+@f8*>SyoYASY1<8Ra00f zNaGR~oL^FNX=(YQiWNCEHC3exYimlXeFbwWDz2@qys%_t^~zheFeEimFjY4RY_4ziGpUAm)6W*3e+Mnorjik^QtOpDvB%0 z0M9Om)a5xf6=*^Try5Do>@^=QMIh75;QdxRV5b{Ei3UAe6O;k zd{$|7Wm(Zm-;y$?oHekd?EI4Q`H;M}3O!me`|*!2VL`V@VlWaLShxtAcqeiYm{oUA(xY zia5+HD=DgC_JV5mvZ5s=lV%YiR1P&1=PX(T(O9<9k`c)~C@d*Ew+w1IueQ7x*W3`{UN0Ml7mfNQ57@=b(eww@XU&eszs~| z1x%(coC`ycjruCfmlRf))WC$QN-GO9r!t}x<5^f*!Pkp~4HVMo5>;(+jjmL4@kR3r zYbpwh$|{!@F?I=9Vc09G3TswYmT1C~y2_&RMTJYMidGh87EUUhOtrFfjjDUo0dgnuQR2QAuUZ(kL18VvzcY zxwGV&WtD}C%MA*X!}6lC(nXq_8*=C##k$)Xcd1 z22OII4w#yaDquzFBJ`y2@{8uxWKLOFR9#w3bR|X07S=AFT?}Mq@zNrwx~izOrh0a< z<78DVFR5BwR_}f#BfUno>+N)LBloFFbHjOY59v+%PGC6*Z5>_z<0;A)Z)UUV2?^3C*_I znBb~^D4D#dWN}e#8O<^BT(e;rj+1xZ^5xX|c{S7EdX|-zLm8~@1J`zDCd4T&sw^rl ztyw7*KSa}?T320JTT|}9P|Bnthcsm-SFbED7UnUpVoG&M$+eYE8M-=&?R*~KMWs~- z!f;yU!lL497Z;V5sY!UC(?i=F6h5@+E7Z*6=xJ)GAVOX^-82_%LwZclPtf+<)n7kO@voRkFmo0>U zpkU_V{Y!b$Eb*vwaDrp@!4FiFi+p1?=CtE1pI ztSMS@knNm_K!k{lb`a?bmm@)=UY=>jKgPnYClwx$0nR+=i8!^iyc$`ov+UvvVf+Wz zu``i!mQ*drbUwKHu$=Gd#)oD+)2f$NtXNi5zEXM9s0Ye2_35362uC4}76U!4@ZyC? zZb~S}BaIYN4p~*Qu&Au4ytrf`O`a@GQjTn-5bc!An^<@;$7pbvqEd;4NM08sk*+Dl za;dnetW3;{(#aIS5qddf97KAMc?M-`;8#&hmZ+xNflkz^MV0WP5JY7;nG=y|mIybZ zm`$!Ms#!Xva1NG%(TS}J2jpxXZn$!eY$Qf`rRB&)mtoQ2p;KW*S*6JQNX3(B!KWRW z4i$qYnK7Z_AaX`@`65^(JVRmi^1>BWq_HaGt@F;1Q^Zu+RB~Ch)tuHADiyK*4l+wq z1N2`^$ymwJoxvRqGR#b5!br%A7M3p0%%onF6;)UB!ipC?BIlw4kuH;svn7&kTh%G4 zt}czk0Sy!tE~GUjrbSILuQm>19-KkedqA;fYW2d}WtGZT$kYYu#b~ljl~~Zj^;Xr; zI$Y%L<_O0Os+bfM)|XY5$=tN6xXj7B2wr;%W97KVrtGt%k3NVFXXz{zy)Il*S62vg ztga}>>ak{J;qr+z1W2Cat`SP3Q%A}(`I?GSkwhKD%qW_t{f1nm@IV|yr>5}wlE)Fw zGb_fhs%Y(AUZPfI2R7Dq)o_!@%~fiyMsKKIOHDuc$Ab)LI;biaTQOxt6%q(@z2N0@ zGbhs`sS+bwSiG#Vrdk>L!S-U=fvohfe#L&2YI10ycuFaD2ujE~)=Z_yL_38Cwfdb> zg8r^ljUQ-#VVK%qn$~zg>A;k#McC~)SP|f6Ap(bp`Lp40hn#Gi^4i-W9-&pQs4%&T8 zD$JoJcHz=e?7qRZ=M{^nj#yAp<*UA~s%EjTxT4xujay@d`QbfIh7$UsjgX6e99@BaGAbZQVh{_tzv;cf9A}>GbT**oj+&xxibqVO_;2&GNYGg zL@y^znBvR3=={v7g|bdT0qRelkeTV5Sx}I3?(D+M31>{0Cb-D5Nz;t7m=e{7BAjZL zn%5I1O^N|Cr^JF*gPC-%JP}XHh=0TJJPutS;v0eghT&f_{w3mRnFRbDfxjd9dL*vJ zf5Vt+l$WFk39I+9qlbQn{UEXg=~B6^bOJCf-Wfuowqpcc{+L|PM)P#@?&(R=Eh&?Amx z@P}j@`AzYhQ#vqtC$GfGsUv;kfTK?Wo?`o&EYAacRB0(rkc{z-m$=(+`x#Drr1;K| z@CJrQ`_7SY?J=b|(Q>G7HemWtd;4%E1IJc;>G&tglVi$v8ReC8zJrq*!+iO={-M4; zS&r(yqW+)r36iU1dn>m;0r^vXWeVox>9X*xl0IVL*FGrDpY&Py`7>|;37>uof0M-fej)0qcJDn$(5G7XcF8}@ z!v9M0Nw@Ib5}#q=+g=j=nrPt@w+Vcfg@1ppz-L?d&m`V&;Saf3(C1nB?B5DK7g+e_ z?+f~+7QRFFx6;DTDi-H|>MVSf#5Y*@V;&a#n=SkViEpv+3nYH6g|CtLRtx`##J5@a z#gB@1+b#S*Ul90>7XIwt3Vg`I=Sh60h3}U7>9X*3MM6K_7XCqrk68GN9~ABOS@{37 z3B2$ASo`dk?Iv6J)DqEds)avC;?pdA-OmL7bPL}s@fj9=<61#K(ZUbEL*TP4{8Wk0 zw(xZl@3-*&I|ZLS3!iqkz!zBftM3)~1r~nmeFDGK!k^kI@Rb(+PDx*9;eRCQ8!Y@a z>ja->3*Rp3*IM{g^}Mu&|M&HRPn(4wCFf(ig+E5(H(K~f65nCr(`CDz7Jg^D@B>{I ze#?^r-)-T4CH+jq!f%uKJ`1n>RlkK-{>mrM8!CUR{8h4rSNSe_#I=nUzDwdm7GB$rh3}K}T^3&TH)7#a=|MJp`YgQa zuTS<**~3IhpKRf?C7fpA{gOVz!Y`2cEDPTt@qP>6BJl+lzE$FvTKIN}ue0!-65nFs z`y@VDo|9GmRdBPkAMy%F@AM~2>dnTt81xruYM(FLpf~Yr4f?wbdcQ$$;#&=RY5H6% z&!9K)Z3exZ5=>uU(3|*ngMOPK&jN$q#BVg{FE{9y8uTVUWY8BI^pys^iSIDzWva@x z>I`}l-)YcOF0Vcf2EB>zGU&?`F}|A(dK2Gm(4TG4w;1#$K3Vz`vXd7L`cw~mng>4J z1E1l6pXhN)LRU2fo1r-|T^J@xZV3z_)tf+dS~?9{7zO_>c#_!vo*x zf$#FbcYENI4ZDi59}oOm1AimzP1$<82fo7t-|d0#_rR<8Om=Rz+bsR9jc+sf;1LG> zNj2!RfmQOWxJP_Uyozr&Ud1IFpXb4Ufd{_9z(0$&)n}~-e&Z7&ufXPm?^}u(-ysA4 zx`FR7@Uskjr-A>Kf$uW#Ap_rS;I|t1h=G62z^BT1NBVixz-LSUhRq0mPmC&3&yf)R zO@+jF@*|?Zv~NSa)F;)zixrS4Ni*=n6fwTj4ZPqe3Ns8mwXHrA4Ls>ueXCXZKkBun(S!&?%h>-qN8hAXiq(5~A-h3{s z!N8l(c{Lk&Jc6n}Ee0Ns!s*Xi1CK|~^rzLp7_X*2M65{{RaL-1Mhn@ zYUd{z_+$frvVl)E@ZUD@X$C&Sz^5Dd@diG_z<iXyDTg{2T+HVc>HO z{6qu)Jp-R*;PVW8wt=5(;Qa>vVgsLN;O80m0s}waz%MZH`38QefxpDSR~q82L3Vw-)!KoFz_t~{z?PC*1%t7;9Cv+)ds%Jz!w_$b_2h_z;870MFu`(;1?SB z4g5(D35;1?VCZUeu>z()-HQUl*-;7bjBzk$ET!22GL+W)l%KH0!8Gw`Vf zzTCj48TbkVpKjnQ4Sa@yzs|rBK8T3mH{7M5~Y2dFn@O1|M`v$(jz~5ltn+<%ufp0PJKQQoX4Sa)vZ#D3%41Ak` zzsbP28~8>8ztO<2Ht-<>-(=uB4E)UozSF?}(7<;Y_+|s&ZQuh2K4Rd1WZ?S@ylde5 z4g4(z-uKI>{Ra(vvVs4xfloE?w;K2~1K(oc(+&J>20p{U|HQygH1Iz)@L2}_b_1Vn z;MW*BhsAp;*W@NEXZ!@xgm;5!ZcBL=?9 zz&~c-yAAx~20mioe`(BfW%fLTl;Ij?e20N=G4L-N__YT96$9UD;D2Y}+YJ1x2EN_E|K7m= z-{t>l;Quu6e;WAzn+E=#^38wy8}}sp-K3YlH{9oM3e_a;>-0BnO@5KPx^K!GvxoaW z+jr8N_)0n6NA$ej`mATyzJ2>z7)}7`XFc0BoXGHI4G&}ZNexrV`B~384JR>tn}!cz zc$J1JIDgi2oraScF4FKwhUaQ{6vH_hK9u214IjqvI1P_xc#MV*XE;H_DGY!5AJyMf zhIeUr48z+sd<4UrHGCw)PipumhSzC$EW@{H_-KY#Y4~dlU#H&emZHyF;;@UaY!)9`T&kJ0cq8BWmf@eF_ZmFj;w!@D#*j^XVZK7rxQ8vYi;Pipu? zhSzEMB!+L(@W~9X((tz#zD~m#3>Rs5Ji~J}{2hjKG<*ufnHoNo;c*&1jo~pGp1^Q| zhEHes(=S#3Co;TC!crWKEu~(*w1j0hG#Q8SHl-DoTK3j8P3%39EQhfIG5ou z8or3(1PyO-=^V97+$5}OBudS z!vzc%Y4|dR=W6(JhI2H01;d#dzLMc_8or9*F&e&_;RFpAGW_YkRR0$+yi3DH3~$%) zLWVbMxR~K5HN1%7bs8>V_%;nMW_Xo`moR*thA9Aj)>EY6QikVh_!@?DG<+??nHnx* zc$|ipF+4`Ylogy;VOnVYq*->CpBEd@H!3GGJKncmovOd z!z&oRPQ!H!7ioAU!*exE8*iBZ8vZ`RnHs)<;c*(SXLyW;Z)7+@!#`m7)6Z4^8yMcD z;Z+Q8*YHgYZ`N=l!%u2>HN)#P+{Ey08ors~RT};w!`ErJnc*T02N<5K;U6)aqhXif zOby?{@Hha-AYb-cu!A%xiZ^5+| zyv%}2Echx5&bQ!P3wA7ch6PWt;L|MlBnv*yg2!6$XbVoV;IGcM#@~WJw%`vf_&p1L z%Yt9C;Fm1;c?*8Zf*-fw2Q2s=3tnTvK?`oO;Cc(LwcuqITw=jjS#Z7u=UT91!80s) ziUpr$!6#YpaTYw*f=63$k_CS?-5P%j{@8*)wBYwF_$>>5&4ORD;O8y)DGPqwf*-Kp zdn|a31qUs-$%5-GxYmM~S#XI3UuD7h7MyFrjs?%K;3*b-ngyR^!N*zfSPLF)!ATbU z)mhf~Tkyvg{GkQEXTfh-@M{+Qk_A6+!B1K6;}-mY1>a-A`0>7&_#Cw0CJU~&;93h_ zX2B&Ee3b>~TX3!gI~F{{f~Q#UX%>8v1s`X@V=Z{J1t(eXSNL56GoSf~1%GV8A6oEx z7W|e4zh=QNS@81~{FDVhZov;&@I4m1#)5+u++@M^7F=t=%PhFWg0HgRd<)LCV8?=I zSnw1JKFxwpvf$$^c&r7Fw%{ZS{wm8Fe+&NDfbO>TOA6 zW(ilJu5O>|_pDGm+4-pG57hVjU8g@xV(z1>ZD2@W*$MJHQGO5G*K*a1#6i^ybj%8b z1D`hM?rqNRYj*Yo`kS3dlv&Et`Fm5IcKSAVCy(5Mi#?mWQ~4sYxjSuSXJ8AF22#P4 z?;B{YwR@vH=*rFCJCMpBsO{szRQ&C8ebct2+#a$x#i3afL({ltBKqI;_mnZtjzxZV z_(>U1f@(2cbT(;NOud&7^mAG%-`(~;ydsMe;^z= z>I$eVu;uS5sY!RepNPfU7^owNf1>zg6r+ssdj$XNgac~e*O%)C&Wh8(ODI+ikaEz0 z?8F0V;NoZlZ@)LN2aAjbXppFZVJT1d|2^eY2SeRK-+d`hchdKsl&AZs^ErWj(#=+E zpSxro`TdLhryRwau1Sha$F9oD#`quLh+Ms&5xMCyt+0`C`g1#qmBQkW$o?7_7j2*o zKMrdT`58t7@keBT4TK7G56+L%z#Ax5JvhJ-nS>4fI3w~f_BsBG5s`DGPtB1%m?LBd zd*meOyHxjQ#k)dbdr`bs7ANZdoEl;W(bY^7oD41QRPk)5IoeK2oObR+vCx}tCof^J zDdXAD@zI7Jd}m;XGUGIqNDbLDArt#O|ApSA9eIjbcZt^fzPAU8`6Cot`iQUh{k4-C zZRh84+BsohJ05!9UqcV&>kdtb)6g?0wmOs-SMSe!7FX|x9i3hwp%dfRDS|^nXTa$T zJ9{X4T9&?42pD$u?&CsjuYv;qJ}LwvW*YIa;z8249W7Ns0k~U6aWTcxn(}9 zaiElMp@5|9l`P56r#zk8i>Url%G0$yG-;)8kHCq$=$_@<3qSoYG}{sA9N*!o+rzx1 zK3+;4U$=uZ{mncNRlI@%Qbm024$gF;7t>nFbO`!w#@2fr{qFnQf1%&OY~q)Aiw*La zfi|%j1+H(PUskq8E_x7KyTprcsma&$2a552jQA_O4kNC>sWFV>&;L*U3oQ;l_KUs5 zT8V=F=y7n)hk0>6-sJ3|$-!AandhO)ttik!$M>bnK8~wPPd~bmJmYw87kZMHELki| ztP9;Av##%XbgUNz_VnH#GuMmRy^`4wruCDEJaMLV#vW3lUS~D#NvEuG5B}sv@F#ya z{#?~<=@g+Zw;O+wPI!ZY`(>Mvf`>RWPt8Sz)TB`{cx39S#4R=Hv+X85GWXP6G?kk4 zP7IzhIOXmC4YwXg?CW|hv1w_ArB5xy8sqF7C~r~)H*ux2b8*Uzv=)pkPAT6)YlSPJ zn1Hj_&EE+uR=@ %CU%J|&EOGGUyB+tPO6`O8O^D5){4TwczGv$1d%D_wtMD4CiK z41t@FL@-Y-27WCpdsY8HNxA~HI|BJJX-h<`WVKjbQL(y| zShpo|zfq+6-R0)*2&AHYTyzHVhSQZln1^G5z=*%u!&9Ev>|g5-Z1V^H79 zk}){DUw{Jlk+9$DBVkcXaHCpVP>a^hRP%#KDmrHNSW9(0u@pT%8g2zlB3L+zB{~+4 zi9NO!FdTZERo51h+AyRFeq~OA4?`Glzkzlc^3u&64#iLGH1xTwhd%CqzZfn$X=QT5 zTxCl`jX*vvD0QWYZZ5&R6n-rsrdG~;vLu=<9f8`df&7p;RmAvpxY_AVq1qp~&eo<- z-S@z#!%YP+ZOe`4x=skV>5R)84hYE82$BQV;U)u_@id&tj%?H_k2MxwVR#Z*q=~4U$o#S-k5$JZq@KRKL1Yun>wGr zlj5ha-GNx~$CM}f(JT{8ik8pblF%;&c|FpDA%P%tC_(Uq1#vsW8zTt;vOCO@AX*Wd zDCb;2W`W#fCrcP~cFKZ(%?2N`GBlj>L}yN5hqz`gv{QqB5Jhn52QhQmExEuPc?;*) z#Kku&p3xgFzrg$&w@$nw=SpKwOpbq`c5fiRFOa*Zv3{?QhpQhO zRL0BM zn>}&r|4i107vJmVG8ZvtlCTkEJM1YR#^en&9}3W=Hi{hUsY|T`=R{jk!4u*d3&RN}`IkyIg03S%7v$!A=UTD_&tK9Ud`6$JC?}i9s>87sJjD zVU_tiFd!SGWq$p93`kno>6B%iC~KExU!8|Cnl^2LcZ8Pj!)e8UvoSua-xGNn>qnSH zCpyv?!fbmLRzQqjm5e(FFg}kNZ<5MotJs0wa&6RPkvnDWpKv>2=T#{(xNDIw7{GZC z4j2Z=oA9s=&N<8O!K{U1$4LYOMO$J(3H(Nyx8LzL z=~=sm62}jN@kjF=xQBEVXOVcpbtS!+j@v0{?SpJD;-4_pOa12dUp>9`_=t}bf-Ox#aZ% zQ31(z0wv;|lrrp2*o@0A;ZE|f!-Ke#-wnKAn2h7FjUWOI8ky$ozTxRcRA-PvTlWq3 z;5ts{9)=QR=g#>vcTNP+XijE~ZlI$%e-GVM3XTno!3c8(ML`}(dNOoN>g@N2vs0;4 zd>zb7BPW04ONl-=X*B*~`4FNMb=}z_6;u@|6xWn<9kP%>UMkFJ&o_k`{b`mmqr5rp zC~S8{up6kG!1h^zHv+h!2Q%9qAT{;}IAbQui>xH2 zCm5>-Wy8o7gb9yrOFfVdPTEQxJQaUc2Sd{#b7OtPS92I*u53SEn@D8rTV1e#>{Q&c zOj-T3P$VNb!m8+&6nc@u*43NUYK^Lh_^L_nktCS;eQ+{r<#sYjMl(VvIt_|$JWeQj z5ma*yCe&1vIRmEFk?~r4N+ozm1s3MHhg%@ z0K3`Ajs5#-CP9Q+$@^N(d#TWdDFVcnwi~m8F?$6=TP-KW3+XhFKumkr)I$co z#3fgTtX5 zs2j&3i2@^a6a-Qk2*sWL!w*!VE^RL>|#TB2=B6A5mHIcv8P%>@Xgxkb4L4T#+ zZ6dm7X3Wf}ujzvcGbATKO}G#Q>^erU=+3LOtO6{xPTpiPy`nX&#rQ=Nbq8E9GnDBLtl zR|wLjKge}tzu6)l#FkCCDNoY%jK{EsoBYvgbQz~w1?}_%o0d{3EIeh zv+ZwxHl4>T9C`N?nzy>f3-C1Irunj=-qTS;A``RkgEuLT3Zasaj3Qs64N@GTp*Rw3 z8Ut1E0722@fbNCAjLI4G50BH62Z%zM22Xu7$T`cU--X1k=nQ>_v=(T`!YDq+(JM?OcIZ2O5DdTXuO|L}IwKo=?DKd?t#siHyfz z!{`T^W-)zGd<|%RrCqIQ0nxkS3j+0>Lkesvh{g5`YzweGzo+RD6cnhCOEwp1Y8Dmv z3)rI9NJCBSgbfD8*8pMmbaK;NN;@5b32MLjJE{#&CiV1=^kvV93fxH0pB_gocf?Sf z7>8m7QAEXGNIeU#p`K|h8_Ks(usW2|G=}H29TDiSfL9+? z5Cv{_I%#qZ7X`(Lh|qc{cU{-n$#sIVk#4v&({3w;;ZP}YgaebTaHo>^h|wY+8&IS5 zIM4DX;zTn2xPz4tkHh&<4%`97;djI#+Knw}RH&LqXqu2ehJRG5N2xo}alW2iTbyy8 zjshCzAe*zgyyhrL3j)}*q9c^4bXe)pdC*s7~Y`8-D4mFO5TbrZKBW|Ym( zjiDeTvnfi6B5Hf7BztfTbxUJ;ytZQ%O`1MR%qSsYFoWsDz#x4f2wfdX8F_Rt@0>~# zOS0?+P3&#KUz1{jL9<*WUYsyAy-J~;3S-Rno8TGG$y@?*G)=9LBqK24pX1aqD$cu; z*|u6F$*TfXHA+y!ikvB{=`rJ;JMc$`LOt<{p_kqzQM@wf=U+nTjHcUqzL6e#&y3Qu zA|a^*F-&?T-f0$I$@x5WRK)IcG0`G3NQ)d~^?Hm}Kp9r3?dJ6c?=^7J@h9k!@88%3 zFH-^ErbbC6Qer0(c$-xE%|2>~Vzzm1AZABQW~3iM#OQ8-T909AQ`>^TkA+w6fOVPc zG*BBp3&uI3pw2Kmsob_iITG$C;WGF8HORV^L)gO+W$g>bP zi6p#0z#DsO{e0^v97%-pz|qTQ=Lz&BZNtS%xdOk%Biv$84_t&p-7Cw)<(X zmdaV4hcPd~V3PAhv-3-2?)XP?aoKY`k?0;(I8w{k`PjVB_pD9;il$Q~Pbb_ci}vz# z>J&2yM^2vvtN5n27SZctfRsajK3M?mt ze;65Mm)Ha8LN<|1g3K4g(}i0Nbd!cj!r2qmCEdea_ZV%DkIA0Q)f+=u&-S7C1s_>H zICN$}W5gI+2XuqtCXl;H4fw0}?|nFnA?x}%*1aG+D)JOnw=t9#TXKE0q#)KoFOVhS z>;+0nbY@X3(e!Bbve=Swx_XUKy)KsM%Wo@*BMq@7e^Mn$&8Qw*woR68YiQA|gDtn? zn)Gt{qokMl_^b31IYG3ttwHU-bZu*(cT}*pwl&b}Dfp5`;Tx!kQD|tDINXH~5xic) z;f6L`(T7yMt>H0T(M>B{(rX{Y03@~36$Y*G1BD4m%s0AKP1>Sh@^AwmMUl|922iMi z-E@f$H4tuy;EKZ*dwraKnv!xvil= z6DBp|lDgiZ6iB`(IQB=a)OYrs z&QM4;Le<9Easd2VSF^Mw@GyXF>kG0q`cY+;to(h^Q$LR53Czu~Mqxk7B6~D zWy9Y)QxU7V@E&H8s2(CEqS1G!C>k{<$157?nn#jYA_ROw(ZC9GP2#nH4%k$hCFiIa+zsKSOqx7*{bP*V(k5NL4z$i8NDJ!N; zR_zl0@UG&c)Ubo{U8YfLI6=`!H6%hBbMGW3w}T@8LyIkNc%fCA)i?}{vFi%@JPL{O zzF_XFvyhg}3cT#*vdJRJ{;-~di?bWNBG}-@p+R2m z{Dk-s`yjT+#nkMHX@a90adNY9ll{hV;&Kiyj~16%xI9!`p4}As!3cITNWX+bkKHa$ zh6P14bc|az#;s%E(NPSKn;&v>w?=hzA#4tE@4|+hhQt!I&A2=Y0ovBG-SwMV@ z0m=JD46|%zoD?FR1)Od`-86wGkRs?9r-ZK&a9{5{uw!?17|krYc}nY(#!woB>u@hV zJMc$wkrntu4(s6=aP|doFd7*)9~yEZ+p@TYc|Oklu0+F;U2vbo=~8{$(@N^rV~y(SdB4?+n8I`j80L|21UgLVjqVMwy(xahFc zmhv>GY_n3Hrs2iS)XF+&KlJQ3A@~dQhm`c!<7gb5&@8%#`k9E*Is4wS+k2He>$JIf zHxMt^PqVO1%%^f>cf8JkK(G8;2-N*LDTmfAbU!(1MJu)Dgs_n804ExGpmRfL8LKyO zAB+5IFln6Z(R1u(Ne6zV13?^)EGM5FO!{0lb(^MyEs9~*(vRS~M!iYT@ywKK(p#`W z%i9lh&sQE1KFM4G@`7?PsAQmW&|oC{S+v@?HJk4V=Ui>v71q9+PM-5QrNhznemQG1 zdZ!Y!t)YuMP6a&cw1xkRQSJAL)3g^;enN(F6_*8DLTDCu^27oDKSo@mwL3oG*5Cxs%>XyD68K5G z#$~9{Trh?T;dOU6w@^o5>lCD8_Ce5t`pDoCPB$eS=iTOq$RnByQteit>2x&GkGC-3 z9APs(!{|id^RkE2g_{id_#~BZdz*z@LH0ur5tzEn0+X&lA06LC3>%aCYIE!h1+ch; zY&*cTE2`ZeP;R5Ndp25iUPT0jQFa?%_M@>NqH0==IE=%)Ku2(Dgk$ZvBTLen+X=gW zLjz%DJ^$bJbPc61s;9_6icWsUaGIUP0wV+X=4u21oQHEjK#p-2b9$s z*(fOFOpiWsKzGR2!W?mjj5bir?2Pv_#8wq%@~|Bs!u=2sx!Gq2=pKf+&O)2wjxV_k zBhu3R5A#VHac42OHbf0cS`=PQ8WfAL5*{EyCo=%77qH_METD~D-Y-Lq??4FllVq#X zPiFLvQ!A1vVFF1ja{6PQfvX(*)POP88ga8TqWUng^ahs0SZN?})q@k>w4*g`_Hd+L z=&aB5$#Mi-UhNO$g4lTXfEtlN?QWcy_0uur;!b~{)1MIXC*W^~KWPEhk}1E*OI{qv z{|aw67}f0T<2F-%1Nb*@EC%2oiIdy;*sXlSzxhw8{*!QravyN9Qc)Y!fJ={5nGIAT z(MJP`vVq|T(txsZrB64zAhuPuMmHM|zvx+~XkkYn9>`%q99lO!D=zlzI9Rw1-o_co z8!gtw#ror7^WtL546Jd2Mm)!b?ovoaB1FzO-cw!i&X(svB^v{^8v^<5f!xRJ$w}L? zuuPa~?I<4kp087C3bdO&5y)@SB~iG!kGc8nZY`b!2}fG6L)+6w$(kTUW>R}H!toPl z@AIf}HPxVWO4mqCY2xLm;t%Gz8${By1RKeC41^fQ9Pv*b`!W(|#3h^nrrq?76eK6( z=s!Pe!wb4r5a2l%@kSP`r<}(O4qWrH+#NuJc}Q%_iFfMo;}sM+ccSodqtK}Hnt@i& z8*s}=TAW`Rg+`s<8|auim@cH)f(&|14MGF@bSh;(h}@@=5Lm}KojLl!g;9v5Zf++H zfwMvQyF;GCj(SIgWENn(FAgLV;$cO zsUov*R-3;0L|4>;XSrC$Qe?reKDhPWmy(T5B1hl2l@5Bf@aYc~eO&Q`T|Z{S_ANN> zLiK?2HFo1QQ+mcX?x}>~w*_j~2l873xpzZ?wX*`bx0@4y7N^eLZvJ|D`WufQt#$KT z>5_I2Qjyqq19o%Q1BwX4Y#BobIM=$lcLOJv{5KE|1kQRFFQ0N+F*5^T>g+{>jrE-Glj zHVtqW28+5)YgtzN$^&X&bbI4;r$cm`CaJkJc=^$oJp|KZAl3zPBeMe7=-Z7&J|!+@ ztgDa6hnStfX8Gn9x>gQebw9r}d zs{4kCXay@qJUfM_tO8q*FOBb<=Ip#7P2n$;_-@A6kKo-P=@nOpQkp7w^6m7c{P6 z+%72uCcLON9Mv+dYV=st-r(7ZNWq}&;AC8IJ=`y)?J8Y26?H{#qfJuXeyZ#2J_U|z zn@81bR6WINmZaUw3L~kv#RyAJ9;d9vVP_~}?{ud>W%Vvxi!LO3^qhE6&(VW;Jx})N zIZIDHhsL~mj;Gh-_MEjwJtz6&DQ$IVWxQ_FASs2JW7JsxrEev?+TLJZLLh(d=I+GC z&>;!Vm%Ybn95zFO7wik<_K)x6;!!9@pQqymcELZv!StSJsW}%fLFjfV$!0UfX!1i< z(Y!Y{Tf__kWi-ILdfLc2vj793Bdd7uKRRE~E0`&r6Gmfhc5*ZXMKRc9c1Pjm7;f#C z?nq%@jn0?w9A8d%x^H}`#n_v`?9Rh{>=3)df!s})j~lHxOs<(G<|dk$TWMnQRfs2M z2Tja->CGEZ_*R;Ln|NX}{#OzoV*J2~iI*?H;cmjj#NIq6CRTN1kDEAJZ89h3PWZZw z6!y_euy!InWUhF(z$p~fu!jw|7(5$Y+=`e$D^{mlPOivPh@l)iPNLmzX9o_nyZJ<) zKoVc@|mbSGQL4z`dT%0hPbR--PWW6ed}YL{$vhiY|4Y^&b|D{ik#wYNjICkd&& zV~7Y6kuC0kd9&ShMePn@x@)?#BPDnw!V}wFM|8a1+>O*g?N*@@@*;7bar@!{Pj5~; zTFC#%Zrr-z`~`C|EE8i+rTtNNZtZNT*r4BM0P8_`fRDg4{mvfroLIQ^eYlr7e8m_zDB7Tp>{OI;+}EqL`7?T!oR5kM(Vycl40a``2| z5M2h{p@q`IPB+;;xgmP9MDVC40#P52RKQGk1QO}tSrkr3XneP&Wx(DYa$@XK;=K}h zxDD%*q?xiNI2accD|IN zb-TW%5OTEKFB5vdhHBkth6JXBAMY3$j)Fu+XCo#u-SFP8GlEZ`F(IA=7L>{GPUf0k z`X#g&f}Pqf30!a&B$P#}!@7quAqdC!bn-A&0(j{M<>FE7S&Z#QxIfhC<^?qzj<4rk z`~~h?gsYK@>@ee?=$$tvp;NuXI8&fodVMrviC?FM!S2Rx3U~7%NXVs-y^%W~!sFE2 z$K#nmr^C%Vn+xlP<3)A-^e!Aox zv$&NkQ*ycq6>`rqu8|!!bR*sMl<3m1}IqsSE;)xOL zOx#1aT~ZV_4OqCC#`Q> z{Z9-+5rjDSihv2ioB)ClPFoQ1laknoLZF-YKh{NYR2SwmK6>rOWBNbLMOW?N;}v+i zGf=xaklzpp-T=2fOFWe}D-f&!w8%J-N#{#~PMS;$n<*GJP3F&$T%XGDtj2XGMZ*h&?#0me}>cQbi{fwN6Gs1NPxq-#-%cB!EF*L-fpAR6#@X z0Xk7@zFDh@8xt{VI#qO52OMnVwFjKXaQK*ZS(wDlUrj5~yRplHs5u5-pJSKiZrsL3 z<2dY!3kqGLcaWBdTiqrSO;%lxO&L*jJyl(geHK;qRj%qF+v0j{w26idqb)an4clL> z=-S;<6?fq526UN#N20+^63}j2n}(~J>pV8yX|F#V-@KCqO>FATW{qH93AjzYlNynxWA`b+>EsQV@lIz${hhv= z6A>|azY3dOo%MeqT$+iE@9-9+!`K^3aCT7L_2Zo#4fS{VYL0dBIs<1b zb<5d7b?@@k9tvCnPCj>vI@la}?8ZZsT!7jMf_#u`6UypE*(QeC2_o6CNABita%(9X z@xBZ_H;CABr+AtSR6;&%-C*b2xl5y|X)HzXmv+LW-8vg;Xzsd4{0#+^1HTjj8>Lv};s-aDLTQgjS)rVe9>S0xQ)4B_8|u6PWw zUZnBEP|#rAfML2@4dZw*j4eEV$1DASY-j!OM#Ms{vh#))sPCCAy%XY$=C|XF=Fws_ z#iW?#wAY4}G5n7P_7gl_a}Wayzv688!4c7(rTqApG_nKVOyaEF->Qu_;ks!Pt{dt$ zcM}jbc!$AQw%NOIv|=ehu#oqj5H{No)7JAoTq_Qgv|(%LotUu(fRX2uG;-TE>~snw zFWMnTtJpF-65_PUx5M)i&`}y5 z-W~F7em%Fc7~S~m>#b35C@KU`B!iqw_}w(Tn$pefpmxNKfv0{6lgAorC*Ga6p5C3; zo`65Q6FLH6EYB2j10lPC+(z8wYHuGhkKM3BWTxw@?$9f+^;N6I7ji6!QqQ!~a5MYaoIZYEUdziypv&hVx)Mlp>rk2}@#=7Kv zqAr&f3COPsXcd8Pr0a$qDkIF9iyKHga^`Y|^|nm1aO0VhtaNkzNjAAOo=xVqx=uzj zB9pMo_YTx9?-q8Mif-^b;|!xj5g8?l(zqyUkm)c;8Y4AcB8I)h7(@?kko1^=TD#w< z46+oOunZC|5W7?z2s-@MENPm@JQg)gJQ6z4G#R-+(@d|rRhwpd%1z7J2+4VpEx_)l zJ83DmTpDAV7{=*NYRWCAlU?3o^i$YnRjggox^YPTx!}I zS(~QLzEyrt)NMo4JxgIjI1$r9OT@raA^uvkk-viN z_?i=FYU8h9D95VD$q|8iHu9MdKuoh zSAcmRU$QaYl1Xl~O{3Ul50AqyM)J)oL{CZbw{R^a?~0ZDY#}+R3dgCMF-GWJH$fsZ ze&bL|%$rhSm&9i!__$-M@mF>Ii%+;pG6;~sbIo9o)FTgd&ciKVFh~>S#K#{DGPQ^X zYeadvC}%R!^82D7!jdV*OeQL(9T6khFqZ7?r`my$h$$9D#k>}3M1KpM9s z3!)Ic@au3UqK*?6gDBM?>Ot^yn-q~~iaDhjL|Z{5`_n*eg`1l3CHun|cMW0e{v^x( zoQVEFH>}NIL7HKr|EVDp?Z;nbq6OSmj0w8zRwS<@QrHmsskvZKn4o&R{`SMY+W8r` z(_*yK*GqkBV&it#v=O_Qb{-*KgG_}tgkAZ>9Ep|VFw}L|kcg!DFIm)>@vBDTZj&My z&O8Q;-$hJB4G|YTRI{?iI1Ve-rF>xa<2gXFI47E~BBzCxrjfp?ME+!^Qa*m0LJ|?m(k@R3R z`un&Rk%er}a43I*&Os0)G#Vz!0pdg&PHhz+ag7m$10zE6D^!C`PS?O2=m%6GX;9@# zocL%WYgRM89Bt}R3r8yOL*h}-+ev~1l;axW^P3ck;lxv07QNfNm@mJ+VCa&!x-titVp6f z?<43Bbi>-jyIY}d;qZcFjdCttO*1HlhP|09OjX41vv`(qW`V?k%VQFQ8&K}9A>Ocw zro-5w#dy3`e<8go0`bO-0Uqy4CX(rke60o(nI7-V7$VWs0FQSBh)8ck^mvVj36D3u z-0*l4B)}eToCMh89VLMS@p!|TGG5o5?(aY`%ZFV26Y@+)}X zzzgQd#VPLDp=s4i9n%JH!c8?5+U_%<*0eFJ!@{cmsYG!oQzRJ_ zcSI@DxP_#@mU0VR|NF9Zh;C`m2;r92gN07Sel7uaOFxnTyQO*w9Ee+5M%oazZ@ML# z3ogG>K_p@XN8J)Z1KiRsyx?tMB6cflTU+#~`^lpBn~44S7a$XEsqSXDrRtl|{Hm?w za}2kn&xp};IvvOgu`5wPf(D+- zRh2F&2XK#!R#dFjIBb-&>%7H0yL^qTU)(_=txxAcNV(~U_s9I^P`{<^JCr* zNXnQ!>7mRC+#Eg|9rK8VH$$)0WDY&+0#EW#q;3s&0gfduVcgk-ZOG6%=5lRN038Jgl5Cz`9Dkr>R zpytH8lmN!e2}Pky|7*x2+%+_|-sb-dxS$V7=NHGCztcU9+fBLYp8uKczx1zowqKnX zWBZ@r3BMuOewfIF?O)jd+rRXNgR*@Y54>&vgS!T?{nlZF+5R!;m}TI~_V*VzlCD#SntR*CEwl6JzGpgHMdNA;F*%BT+J-iE%Wp<4%nF zx4b9D2paf-6T`6We4_B47~7z~!Gl5~-k;>I;oMRuQz~$%oM=6`&i#-neI-h%d{M;o~?1ITk&ziqd6K!oVVB=sI7UVZ~@N0Yl3sQZM!l!i&$D=275AePkszC605{!vWz zbX`5ot}e63JWVnG9kSM0oWeu}$kOHi{z6!4Gg`yim=r{b>0k=Fib?g|WJ&I=pwlEh zDJTcmaTV0IJyt;o&8(oQkk3OwZ(kNwP-#r{bn#vu==bw~Th+CKR0C(gD&i?fmA?%R zkUVJ#S|bJFTu>6pHl)SMQ8>*!Fgk7NC#;^EGk!|!_B%I^CNeQMAH}qcJXmGVP5Moo zReM@lbNr?atl$@LR9{02uRw4<{tlR(P6G{0sDSPuK8wF<6Yc0ul9GN#gnqyQ@5rF^ zfZtj4O+16SLD3TyARiCOVgLvOHnhpa!q7njY%6v|n>ly5WNq$z%@C|ZVw(N* z(rA~zB}*`5$zs>3R+NTl*D5Jmnkq^!HA??VFHXW6rBGMySWPoZUq-3Rzwr=oTGYku z>SC?Bpi_`7l&Bs3K|2i4B z>om}4=aXsC({A@Qkp{GM+1!xlTf(CR8(Wljl>=3 z`GTn&=tuIS1AUe(i4OEFqBKMUeQdN;4s^Lu`kz->RAZp?jM8qDiZh=)(4@A2gMlW+ z;emhLvBWuQsEbEAd~3`oHv=?Ac_oANJV@Thk84@{KKIvb3>{yRCFn&>VI;b`;%T|1wBJ#QVqUUZcKKovhyvJO|Hd zke!HWckKM=Xgu~iQ9`4Eu^s*8JlZh+H~zvG@re8=O2He(?=Zy(K*M!cyqIQS^t8!8aN8up8=* z^%*j?ygax}KOy%ph)f;5u`;Q!{ywW)+=BI#$&eJL__vR7hbw{rLkBUWuqs&gVBZ|r zRG}Ka4-})i-oFhl<~U1C%8SXgPxbbl68lY>IAAlq9P z>&b9%lT(~RiT;#0i_!wi>q5-?HJbVZ;lY-}=*Fdf85c8Z{3cnMYThPhqnRe^o<1G(--i61W2j zo6D$;IAQbKyI69Xn{mVDKEvQ-&tfvm+ddquXP^Jo?3wpCzYaQf6ZkXw?(?Ey(+-CB ziD1ZHJ?11NF9$@VpWjW5iY#ByhzZNfNSO^xBpT@ecDn4|N^%48L>+oi@eAb&l?nTqZD#mV4S52e%M)MYt%6PqB}#1Zn2Q z&`38%nW>!Tr93#BE)&sO3f=a4goWP4?6M?8 zM6uH^w$;tO?;H^MVN*kNW50S|Oq6S~X`)=snpDb-4hD^w8hYCJ0cZ_BFhd=BJ*tvZ z0Um`g2Oq3#>xdot%e0g`c}SIA4Q`-AlyIQk9iVinLGtos@uWw3Pz)^xNm91Y(>JI5 zJjCy0NLl@|kCop;l&T)y5bT=B3a_I336 zMXpP~@~$@sgqPvH%U|wfX#}@$I&6YqLm7Fbs1KCA6l@4dbNso?xhwv&r`pdLvdbIpWQKHhLZ76e- zwh9VPkLE`y$ZwBc&lL9>6qBM9GCgWAN{^DILkyNpf8;KREzfYIO{5QGvqpL(=aUg1 zC4o+lBuukCCOtCfC;%E{MtUT%I!qA^Mg!BMeiU%*n83T?Iz1wM%IYrI290D@77h5- zOhWOBN~3M7xNnGJ^$R>W?i&IC*eSaM&tgF zq!Z7Cs<{6pOPU&_04*YToVc%|_s3Jc<32GO822Un(Z7zZ$#I{DA|~!LM#S}5Z?OsU zZ7d!4Ne-D8d`WR#hiGxP%X|iMQzyz@afdL#xoDW9KRh?Z?Z+Hxc}lZ5@x?~|861^aX)h|;{K_*XnxgJ3hHsMZ(>9=zznN55Cz7)ifl z^GC-fbT)RU&BNccc=0&9%_h?b$ ze$%+bJz8u2pU*incYE(8*x&E>{r+vqyLaYu&YU@O=FFM7bLS4)1AOAeyZ9=6#WK3V z&!54w|B7WSs=pS+t#8lTjQg0;IZ|ZCqgovQ_SdUW@8{o`q{O*yb(8jS97j0*H&mXxTl?@NqkKa~yvP&akyQqTRzps9>rTui6 z258~-Q_hi)Q*3!n&73s@s>=YbM^{E=2a91uKEU0e1$)o{WT`dsK|2m6f-hXtzj{}8 z`Zkc%;6fbX#RBvkXIneoC#54?6Ly42lNfM>{H#wW3)~TW;>FwIuU#LnzSA?xBp~@Y zT2#Ohu4l%_yNk8bwj7@$90djtb(!Y~PyKCo_k`HGiHVBNiNM0W;S)~_FjOxUTw+ec zN!`^o6kuZPB`WLIx7B=5iYRB$GsSsMgkljI+?vK4)d@GGvd3u1K@TFCJ5S?32kJ#~u>2 z$J$S(!ZS6(GhwW*S{8Oh=sEIn>ecAzIqW5~?_zn8&S2`9Or^GCa(pZtSIT*@T#T{> z>2g1wei0t5U7|$BCjc#jtYVZe)XaHdKsC-){<=1qi*xGD>G2&~BfUS*Q|_!y_Qgr@ z9sT7JkWO?%{9`mO-S9lfz-2RZb>`u)LFR$Vn(hzE~R0LJvIwN|!wh6u%hyzCP#hN}d_Q1ipH;wC_ zA!q8kJ8IrTH9Poq&wSuP#RB+O~Wisrx9i9&P`(I+91iqe?5h z3ljsQ%EsC`4LDr&-%{x(S?TsRxlZD5PCK?ns%3f+^4XY?R&8ASULPKRpUizW_8LBw zV{V_s?TfK5wVj_kvAY|NUB1sZ#~d==ae+CuxZ@;h%p4Pp&veQW*sL4sk;Ey$4gAhZ zGwxIP=%7{&2~XOb^~k{&s4yVmTw(XKUA3S9)!0bG?{2Tf3Entqm)FJ;IDcM*28Vp z$7io-OSpa;vQ(=D0=Gb5zB|DW#?{W*fV<;x=Qj;)e``zAWX%?{WG@oh5=k6t69~L; zF0&|4I_`Fxp}XKV&?k@ib=cQcq7OC0*CpZW8bdt?)XhP!miV(6cF5OV##A~5x*D?> z@ZnhXZT7xuy!Ms$O;%8Mlgky%uG z2a>4s{m@usg(1m(*cWMzRrXEO`|H#OkpGW-;rJ@P<5c^}EHq}le%T=!OK_C|FE`DQ zp9E&Xfi;KLy_2cP0(Og3WHEmrU%hz7M{DOi+GobE3Rl!!1{&iW>s{ygG#s%w$9mT} zK8&QOnMmV<1mJ|>18bUNq|KA|{@8=%zPWvW;uE@@(fEL|fg5Y)+&G|mxDhHYFJ-wd zaxt59ar+nIB5&+pU84EMOD zEZ2#>sb8F(g+({xR|@AffM+T~mzC2oD-~HL7gL2DUOCgRALGR{j6`6?J;M{hsj=sXCn`abX!%Ea|3{^1C8HFY zr59@TFDo5;09wCx{VZA6_0A}T)8Kc_fbPPV*H#%vlThagDz^g-upJikxk4o(4uXgs{k%k}a{VaCs?QYO8l7Q5sG0eJ^-b5JXifPJ z969U1XpNXogKLe(?&OiTwZku8*XA$`pL0EE>D28C*NI zg*IT(HM~*e%Le%rCDkwS(GJRB;H@kO-?VvU`zuby-z(*7bXagg+ct=dTxvp~TeFee z78~22k|gW37Zmk1UOh3>la4(X zO~U5al1)>^5t4$5aY|DJ@;2L}a8Fda7EEoiA`3J%CgS|=PnJnf${Z}rQqJ8_9~>Hw zFSFL*eQhAgip5ry!N2-DEJB=UypE}H(_Q37U1#eYh_<+1g1EvJuhcKxpWb<`Q&jUo zcYPf^B_6v;COZ1O8Wy1ESFlS9c+Lqb zgV?BhWnakdMvAB3C>f+*S;P!kJwSaAXNIj#eZ4AI>X2CID{bxHggHwYD?SuSA=kW9fsg5FPWBPGo;;T7t929JjgdpmPp0XFs!vu9xZrp-r@= ztX5~OJtkdeg-_gA9acxWJH}c%*Mr?kHvttz_UMKg!cTS=u9ow7AS6k&kx%-!YcQVk zJHwFv0Bfr4>Xyn?--gj~+XclO9gB+BM?1HK+ z=u0e-jgK`EHNl`}yQp7T)CNI4P1KGCwT+AFZ&CLMYQ3#^qH?sc9b>=y@R6tgsF)Wp zU|Rb;{W@kEQ+P}a!!!||*Bu{;eG!S_>$vYyfj_N+ity|sCDzQQwLR8Qs3yO=g>UAL zgJub7?;uVA6BEnCvJG5YHZD-t)pL(JCIqN*{#1yG2t+!UTe-#LV4egeSO}3nY(XSX zijX5Av1T7H{s?KdlE06O2H2m{dhl!aOP=Wx(pM7x!$Tgx3SPW;@r4-t5`O(u#GKfR z6JzgDGpD`_`;dCTE&B|0jHDG_#|<{Y8kiP>@PlSUP<$`MC+>4tRWS_)aYbrADwQ6( zE5v1$50xqp*5f#>LPa?N3Gj&^qbxjhD$0NHm!c?n4!0AvVJK0DqY+hNS=5id6X?RS zccIxftXjVZk)o>b1Zk~uw^eD4=7{iK@|}$tifI(s3c>;E-{+5x#X@3y&^8xPxyJcF zCho{5*AuRo6BA3-GvYXm_`)(NNn-OU>MynIl=B=u5d?m6YtGF{3_5&(JDx0re~ii~ zQ`G=@rVAs{?w{&ZyB`kDW_mqo_fnw>n&Ih4vzq!!%FR5G9HWEGG*OYdY}@qO?pXAU z4|?@{Y6>G^$Q0XXg|EHMYU^;=yeqF|7RszA^wuiqz}*Ddz^vPl)y`S67jZ6U`qfDH zg`w`q`qK!|Y9x9*#@rB9Wr#|3n{q~n(yb)R$;}*ZG6WdI`Fln=2#EZ1tq%oycAMEl zFpm>Cw3Pm8>M5|ALP+_82~nh(&7beYM`#w-+$n z=f^{#upxZhgS8=m5P+wgt#HpdlJL|@etIb?iBJ#EKM~X+)Z?0~8|vYSJ3~EYGSpj2 zIqmsZi&gK!YxMzA1K6Nphhrd=-Nk-(v>~wL4?Z2dxfN?EWbYVg@0PPBaXGjuK3|&o z%xAfOj$ZQ}=NTHjilD84&)3@usZp4^e+`F(4u-WR>Ovayb|i6X`j$Fzx~J9q8o@v0 zp<2+xB_bm9fO}65s1*fgCIrW)wWZc7fPA~HJ{73}r*MgV;88t=8>3Z?jWPvaU<&5) zlQclJzu=i#Ff&nbMX+E#`l>fc(R^tRH1c<_e;P)x?svzsN^-D~Fihhj@%{%htK@tELSh*7QK09$W7F{fh=Q446!MUq6)Tlk_^g;OE zEIK{V)TG4J1Sc74P5$7o$>mxTWJ3q zD4@cYVHnj%8ty3>kWd3p0n+Od#oy;JSQ4ebEd!%^pF@n&KjWS%D7{{k9&?p`EtFnQ zwUja-Q>A;0d-J%Z(N@fin|M_NbN#Q2_kxEgitR7671 z$CY#fX65W<+6OrzJ>}VoK0igJ?HCpfcoz+rnQs3~eJeUjlEIzZ^4jIM6I9u8o)^#w=1O3EVW2Aad=hF5cnpLH zkxUGdxctSRDb-e>YQWGP*rVB3{JDQUigAz(4WZabR=Z6G3zsjw6U)&ggyo7l0w-_9 zlTjDa!hE?x8AXACN4B~^-5AG|8jqyn<toHkcVy zMGIf`qol78hi=dY;b3H!^A>wYgk>}loduI^Gp#oKuV^)9bBA@TvQnXw3^YK}YDhkl z**!QUurb@6Ce|qYF>vVxzCqA2Dy~L(x z^T9FEqMe2s@JTbGH8joe&Pdr|@5HJ z(Ui<0Uj2_+Y?psZavTZ^#`*TCPvs2kGRoCiN9Bxw{poBavoxrk(^PXSDu%Dv2L(GT zC>SE8h!d}&4n{;i(3^R3gSom|qNZvU6qy*96Kfg4UGeh8B&?IbM)A%)IF*m~H5$xT zn%{|4o@KeU2~64><+7-rxQ)xqZJIJc8%U8@hv%LkmX1{cUZFsRD6)K<2N~x=Ox{^_ z$=RHyIjQoqAWn{Qn&zcWV=65q&}g@A6Wp-@e}Gnt;n`Ft8odkc7F~?*Mt9aej3YPtxUJW8YSUt$>PehKd(EJ$M5uS?1R zj}D5#WrMxWuZI>9OtqpR(34Y6U(983-)!&8^}eNJKXk)pBHq&!v@)kFDY)#!wp6PL|U9`poL0;U*h*=ohPujw*g6zczK6@vl?ZtVEB~CzjKlb z_x_#&cvl3JDwSrc5(hs}U1+FkL515X=JqyavJ6S@o1{4lfRLsvvrQ?VvJUo_3rhn1 zh2vxiOvC}BTMVO7E~AkX;{(u(khzlHUN5h$F_(oa=HG_P!WA!JkF-;`Vg=Wo$_rO~ zJ0aEuU61d6(KRJASc&iL{slSdYR>EUUcFrBCqe6mHE%+Krt-mjD&|CkS!*P#NU~h~ zIj6}m;W#f0W++pO?--+q(;R1w-k0Fs{j7I`i7y~bHEaOywBX&S!w?6gB0oZioc3?< zCtmfVeO64j-xken;qIvnshE*4nOk~tTMAbc#VdHz8qICS&aKhw3g=qG6CKgE(Q039MoW_AwHEj^R z%a<&jj%-{;a{HlTm?k6G9Epe}J|Dr*!ElMo$DE__`AV-mEgJiR_h+PXMKUHwq+oem zk4jNu1a38VQB=J}_{OJ*Xo!J2vJE!ps^l-K(oQ<^j|9^%R@%;F|!{FfFHB9U4 zyIaoP0;t1JPz8=#5pAWMYY}d^1oa$;+`EPg{ddcM$2&_7R?tq$Im$7g9NR1j7+M>bu@{aEAraCsa#aWMOPZ>ZEFSC>F?x869 z_G(-+L4Sg#vfVn)f!JtzL3e7pEDM`&U0F{G-wkJ~-@igyVqssmwYy&&AhO`Q#Bdx7 zll5}vV_QwO_Mjb8n3%~hWN^eX>r4h<#)dr8b`X0Q!CiK z%vGSPm@>-iJZXrj20iQHjd#ydR}m=>Viq0O+w<-JGf2cUvFwYZx(A#`=1r%7@&P`?z(z0 zsmIvUqqU#aa1eeGWG5N23QP8{nu+ne_@I4}F2zbXKEAufh!S$@|H5N+_4md_BvFj( z-8V>XQEp271ajQ^YrV|UM#U9UgAK=O+pW9I)?j2>4{jiT=(NDKJ!d$y*M7W=)FwIQ zB=+IxR$jz4kP*SG-ysW)=6!18cF}&}ypP!B6ZwZ5DaKI>ZU^ZD^z@1GQnyr`^{PBD z{HB~X?3jp`7jRAPo$=k6V~QfshIl}zYg^a>V+Bq*U+pcD4Mu+suSo9igKYpzObaDk z!95xHosSJkKFGa*K=qV)KAtZSQ9sisbNR%0nGgPZ!0|xWmog106BE-5d`VR-7N1Fv zAGQUR03YjUE@o%Y zk5ZxnFCL>6NL3?Mj5n&rJKAk$TB}Pvh}4<7(xWI2U1^a|;<+P%6^XHH2Xr0V_%hGZ zOvKtLXP7W`ZD?0!89O!KHZ(^l=m_}k9ex>n2AOlG`m_t^{1f@#;E68TcfhM_-O#Ny zU^nbBo zV$hEsW;mvJ$f%SIKBo0QTt4Vr$rLISx$^*0qZC|kh3m+?4=~hvA}z$xlJovKV<0dM z#C+3Vnst{7ZPp|lt2S#nP1bJn;phgFRZXX)*{CWj6!_c^KJC{ajVospVo%$OAZtFd zbg@s;Rkk_)#ITT^zv%9+h<)OhvjX^u{JKcW*$2JOT^v-cAaxG_%3h`@bxS{GVJYX_LHISdiQ-HC z#b%XA;5KOn#U+ZDdKqw*kgCviChfV_gXkn6tVDf7o(EvCAM;cLLfG3Ha>XBy)vhsb-nW_O+lz z$iaK79Btj?=6p_ucNmhj&d6BZR08l?h2ZcA*xH9EYC#ZtRshSR=G2x1J1vNv7Qpri zV#fus7XXWo5#jIJwqIgTdL0W|+cvO2F3D$Y+h+Y+eo;plzN3S`sk&3~mz8f{k3Vs_ z)fj2*%kRZnb`(?{2X>;&(^R~s0;+(Dsmo9YqEC|gQTq_{NVN7HVYDIXUSf!zb*JGy+ z!a`|~zR|g>W+Am3-MR4Uy%;b0?SsO9vL}U4IXS!Gx3SO8TNd;yDCiqqSzKJuueje% z1sK?q;DC=2HS?#jU}%B-7hPFUuv777{k9wmM2jL?6`_CvAb=$th&DwyE<)0YD8~PY z=u||PAa*JlD*r`S4&8U?(0y?SM9ya-M*)8tncZ*b(30p%WcJ&)UjYoeu1FCj{7D$O z>*~odu0N>gSdJ&Lx|(3>p_jf@e-t^Qj6mG z?dM9U!~)vN7t%!-(yF%3i*XxH90@+N+B-*IA7^_>CsVDs-^ysE3-5*`Ln4!B~=w<-iPw9B+(JfcH(_FeLLwBOk%>uYU>3HeUbt&Bl zmu|SB+fL{%2e`k|@zSHiDi7=T&Bs3HeUNnnvE{sJ>JwU>6eYI2_6nR;0OaG}!i(xdCt@>aQY zj~Ti}Lbn)TgfG+!FFm@^uv*9&bLr+9x+y}p1mM9+$4if{M(L_tx(Y+Li_k3v_?Jq@ zOOLKu>4v*>TN%2~c4GbR0(c9h}I7%`tR`3*B;nw^TY_dUTG`9qZCfFm#&>-J<{xRXSdJ zbR##D`fcmd6&t$OcBEb!0WMKGUV3!1l&<{)tCx4>s>!)c=$-_4E2ZP5N0(H($6UJm z4c$3HmjrlgrQ@YXR{-yUc9`qZ%{Fv<3Eg^tw^2G?dUTaaSK-n{4BfXouzt+|4^ui` zdUT7GZY!5=u%TNmbQ=KPR_S=@(X}bvhwodxyd_sn&ecNK0x+g)><_&3=*r;bP``&< zx_b@X(L(nMz}qVwFFm^HN;k))t2T673SBF}rAo(3kFHVaCb)Ft4c*(rsh5oa}1E-`dJm9l=H031;|UV3z;@cyXZ_AXtqp=%PlPJjxm%Ug2QA6TQ#xLHbj?b4 ziA#5`q5BMOELFD?z{e{cFFm>0 zxoUEb7P`d%Pf4Rpx7RpK27=I^7%d6gK16c0{?sehC7Uj*q~5DJ8+o^@wkN!c^4%2FIx# zRl4GcX92yjs}Ob*WAFR69=fbQ!C|y!{qV(q|J3bl_zw4T*Dph?8hQ_(dlg1wuR=2( z+$a4{pLJhfvwcK|b5mldr5-HhOT9-@`D(5&F(QdPt}7`o7~r+s7o6j$CGK$vo@;U$ zlCesSz0%--?QRlakWO||-;CS_WCeCX{`4&s7uW^qe@7eku%Qeqz}VCjjTOn8E?Y+9 z`87BIBq}du%kmV|!Z)w|dn5f%=ebuXd=C|+?TI8#_F4w_B;g?pwtvLTUI?=>V}CYy zjBkQti-4TwN(p7(UI(h=lg51)JlrDYe5aQAv^eg*;YQ!p`=Mqqs55S~sVV0(7m;#g z6N`&3pdNtUX`zQ+zQDUqEV9?+{gUB5OHROKM1N;rimv##J=j5c-yRMVFav}CJh=-yHxjjGU{G9Q!96G+SFZ!u` zwR>eJu2$#dvecAQ%mWPYLZ&?XmIw7p%DnsW1nchrTXsylJDHn<{kPbjgH4P>WGBXq zvX0^23cVHMasW*aySNKI?0ojPG@Fdg`7u`ARXgX$+`_s8A>!OQU0mcVtlJYeP~X5& z+7=sW(TV&y`|uVEPx)W%%c~}s?F30nIro0~6E;79IOmBZ{;cGm)%*kWGlEVzC;i}J zYLK&%e^&DkF!vhFz8!03qgHgV`d4 zsX_K?gNYf;=V)=$7R*r{8q9eH^PGoaF)RILO)!|hc$gYu_{Wwt!eDAWOgS<9V=?^= z=5!AuWdY)1+V@ry8|z_ekge@SAoXJ~Lp@A+7;~?|e1)OJ)ZHtKSKt#fm=`=ujmw8u zz?^3=fAcWqE{0dYOfZ;hJdBjZKh`!z7|fX-2D!m{^*5M<1;cE8c)}~I!}gJ?pKX9a z(Ld~EyI`wi&<8GE#lD_$cEMRhxP9G)ta2eA8b}|7+-I0RFQZxh-7dwAE=4~>@#1H~ z^ePw9`77=EiTo}Dc>oaK;INu5WDOi#j?;iw@ghP0>8bt!iW^}uJo-Rp%Qc5u z4pEI%5&R`zB}78X`77LR2am^*yC*vW61hP{ta^RObK~f|wa)hSk#}n5ypuDdA8x;E z?dXFaL=xBnxEk}(9I#)@A8gW2MC1sMJ-+%0%k@?7#IfvxGo%nQyp5upu>`PkParAK zUt}CmcmfI{Tro1d{|g+jPl8*-BOfr9c)|Gw6rmyL(2jAgYgbl>usQS%jGN$bZLSoa zau%T8CU-V+of$+kFW~uOb2i4RTT>A{{KK;-oD}|G5=X<~0vqP#O^B~0G4gdRNnPV+ zOl0Gw`SHjn9f!vw8^=aEFD<|gUN!I7U+%v)_jw(=;2!6gbj?&ipAH<`xKa9Y^t1)tiN7&lR4y7EC~yt(e(fF~94zn5lxmHv4tYYB8r~ zF9yMCk<FoQ(Pl0oNjbUT=mnR#DO8r)e&DxGkc4IK+gdA+xX`Xa%P2-YH z)K%ONZvudP=Q@0`BN!_(7F1*rei&%_`j_;cfB;2|6BUTv-6$onH(l~@T#zurz6t)u z&2LQkr^+!y0-2Ai<(OhdAjiIeE&Ngr)*9HC^PR}Yfo@C1Fis6A2b~c5NI5t1!J{ez zogT5sfezPZv8|6`v|l|JqFZEAk5s8MI%XZ7b$WtpP=rmh5N~*k|GlF?6pviK*R2n! zFTv_raId~d4)GuwPW*~=Vj&xSty7#Mguem8qElhALCy-qwji|ZD;i4Dz%? zwI$dc+{A+p>!CKL-$a4Y_Q#oEVA7LSzN8|j+mX@k)xG5RroGp`@3~h#j78m^7!IG-tf5yr@*9~nFpJmfxWxjsoaFTcz#k5Nq!#t1cPWoG|E0C zo7~wK_^Jgs9QmV+RXBmQ3#DjDwF0Plz%1p zZ-@S3=tlDXTGZd%OZk0KeoyW3%QJh{SJN3i=l7&f%Coo^{u6u2?~%VRP&KZs%IFm8 zFJ>07@+Ci9SQzz5qs$wN1hA(O&Yy^4N3V%P;|H z#Wc7DuX{G*b5ku`LjN2G4k~5=)!;r~LAyk56+VAG7r3@5q{#D3=e( zu!^Qx7$Y!4eaD$S8cH?bEoh9?uy^Le&R%^C=(9IDjT6lr3g*s1APUV^py0r)dG066 z*>tg0)1*?5O~p^FxqjiP_^p?dcKkKq>-X3%jyyLKY?FS88MZlV{#NDButHX$q z7C{d~O)Lo`Mq76A>E2~|*%M^B690s%leM)c!ybYun@S+-Ip(q)MR^(J=t=Sr@WfV9 zX2T>F>T59q-3BfuzVLF?SigXPv-Q_W-PceIXV|OYl*&JL>pr~YlKQJ=E%}VskrT@w zi=1dYe-wY}lGS6e?|BlK=_{Li0>9GYHv*66QRen|z1?ZGu81GT+j#z`TM3&P;{t5< zni$W2nK{|2WK?FBJ=VO7y(Uf`bg()PkI>gt!YMfW;#S^z;984+$>|4V1blaqtcNgG zh?kcMXV(^fdW}*t$F8kW%BCGqLIo9Oik24k`fkj%ZqqC+?Eizs{3`=M8?qUmKMJJw9+qGHYHTAf~!f|3q zov=^j--O19_szrku!-V08z#5qpzbzbVb7zI9VZ~imcEJ!hbm$-igOAe8wm{}mZ&FIIV@P8QfUv?CsyU)n@el@p?GJ}-4wDNg; zGts@zuO7IDPTBe31yCPO>UjADwOzSL$Hw3sX9Lssa?|sW&h=pDQKn02g6~s3U=0^| zoh3+5MKSe!$$tCQ2T3_T} z*qENb3i)v&PJ7arXhoy6@Z0Dd243K?yI$E%C%bA}oGaLeEgUezXbZ+{guMk|PLVLtI98}NG@7R(U$bqZ z39R79n)F#`BiQ2PA>4`~-HYOD=4{NFvD?brUEo`DG2_%6mal0!ZrV{u>%g!;q?fuM zk&@<74@X+foL2J983A;j%cGy5nbyZm`wCKkZ*CCz7E(3MLq3DFc>Wp;Hq_qgoE_+D zcZ4k_nZYL&!2xb7^w10|m^?kpua)DJI*1hCOC($`nn!}lh4`F1_mwH=*2rNNyWw-$m}%q1*?N%LD9qKuTd54z)w4;CQemryR{X`#IWCXU>cP zYq%N7IS=>2`UOTvf4k71cE+d6;D=8srksipEvF-i;)DD4p^NO&XOCVl)7;@%aznGx zCPaN?;(jI}7p^gAP0|ipP|BG_e4_Zy9ItF2nsbav=;F)#{aJSsE<;>apkoCtAvl6% z3BfAa`1o2(*8B}W>EYBwKs7n?L^LZr7rj)>+=Yu3X2q{<`Kh};va%-$u5Tvf?=^G& zUR3={1*-fh=TK1B%vsqdoIVzYiu9c75>WNKEJU>@+S4nrz+)+2tT3~IC|KBSYa#ku zpAqhPGX}DZ8Jmd&!+dNQ>gh%qSkIrIS6KwtH!SI~uvkwPnjTc;Yo3QvZa$Td?`$E^ zT-(-%6Qsw$84DvVsYnt9=5$1QV&s4kKJ*wp6GxK`e3f{(yq0KU*kH0cUs(0w7Zwc@ zQ`(qF@^VPzN<|tqoNLf5KCGNLpmrprMy+Bpkh<`}wGdnf_da`L6LVxeWoUmKf3O{e z(X)PD4J>);lo)6PyEvK&>zIw$WNlj>KX=5H2IT8jSct|FN|4GYbA4oJ?=p9Ja@TC` z5D#G3Cw-e-aXslt^Xl-eXoYyilkqBi=dD>6CvN+Y<7;z39XVqn&4t(D5N71859E~N z^JnV|ufsVc2y8Zp$UDYy3}?n_rJQZhsrUg%2xDrXUr^g{InO#oU0d{F1AWlz&sl}}uM(7oneEa9JFpK`7eJ5>HS zIEeg-n&_cc(jcPoK{W+%fjV*D_F1w{Id#w~r*x*<7S@%JtacSEnN$4-OoX-8Dd&_X zQgEkdnTRJb=S*Q!7iFQftN4>sJ)jXS+TGsAD<5kMg3wNI2Ia+Z8l-^)m;QiXY*#n8 zL*OXduco{>ii2r!)q7m@Zls@$qn)ByU0{Bqu0=@2GapXgMpcP~WF(4*zRZ%u1iqz4 zy|TaK;=E-5`++iSv5@TEg}FI0)9yI1p`&kuoE#_ZY?rn5rkwFP)Eh8?8~4h|Y1DN8 z#@zCbF?Qw5D|Rl5^>2xnYN7lQDBmnXtp>z-qM4PWsRl<*Gh-D|{ktbHs-~s+$I^Hu zeE@oXd4cgy=b|$RiEp0Y6PHB(*=QLtW4V)1vhW;wF*t9~@%l+%uFb?%W$s0eOkK?oBZ`C=u0ALCj1 zXc&nAq?VE8_EsMM;AluUw-oLXvzf?pd5HoG2#n=#U`aPj<#p8!6|uGoE&=mx3gk6l zZ{I{ZJ}iV{x1oR{4Tr_PKa3|Gagi9zCqaVl7R>t!7w}tfph-D<|Hk8eJjig)$^qzB zTbqg%v0|8^;>;}^o(21-R-$pyk(x1f+=z0`p%569n!Sal13UJ0O-##Mi!yxnrImV< z&!KdM^Ec?k&Z+F)bJw$BWD+!f%J|cL3(Rvr!R~PotP=kfd#qCWoOJ70bA8=mxb-&` zH>{n!o`EI)bC$}0yiiGHeNisiBJlv>=$ay1NToJObQY&u-{S;J$H+FfM3IUpA7)(U}3x6t@>@3Oo=d^Ov! zVjcUUtx}mwD~q;T(S`_-&X&wWd?x%rPnJFvEEaBxOHDi$UFfMoFWb$AaZfZ0DNECm# z%q@B4Y6O9+XjwUh^X}8p#9E0AmpG;ApC0@u6;zz3SEdCfJ>U)soW&X?7LJ5Ph@*aj z7KC+_ucCiT$$?|Tea#myFM$#&fraGipQUT#5DcW10%;@-?h~WpltxO!$uGRa5jXe& zO-?iwsgYkRa3+1dB&OzRvG*<$umn@b39%L!Z;1_vQqJF3`I>sC45DEVLcPnHe*Hch1dCRF@H(bkw7Y!kW0G6M1BMdwk)dpRQ=|r!hS;4{aRoJ26kQ;_7Nw!Q16xk>u+G^hhb|B z?DLaZ(*Xwd+c0dgfi(;47Y24g70tXhhe`outNk^Y+y6Pu#TTpA=?OSO9Q(s3|ntt zUr%C9hZ@+dFzi+X`-i|v3~Y87Hq*fF5!hA+_WLmG7z4XfU|Sm)r~83kUuIyF1h%t* ztqH?&4Qy|L?P6fbFzn6$s6qw^Y*zzY8-_h%VDICt>86}A16vn{U1wlV3T%XdJr#zX zZD0!pwwr;i55o>Ju!{t?yMa9&h7C2aBLudGfi;C;oj+<#O9i&4fu+K*XAJCnyjR|o zvzLK2hhcXZ*h>QYm4Q7IhW)|7{wA=I26jRic7lP;6WBfmc5E27r-7X&uzd~e_%LiU z1KVFG2Lkw(C81_d4TN{8Cgkk3!*ewD(*l^n> z3_H}oE)`h0fei@5h8ftzK%rZNVc-0qHQiZYhZ=4}!m#HJ?57i0)58p`e;D=`1AARy z`x_XC@jySVF|g$VJJ7%?!?4K)78BT513M`U+sDAp5ZE{as|v$@VPIng_G<$>F%0|Q zdsWB~fgNOEXNF;`4D8e6sgN-S_HG!q$iSKec7TDs7lvJIV1Ew4-Vei~1~yAzqYbwY z!m!~6c5DDv8g{BbbZJd@6WD%+8|LA`#(LSn`Uva<1H%ec5O$w|ZNz&KOkl$dY@aY}ih*4(ux$-&-!SX|13On>+ZouXFs#VH$_2K)f$bNDwST7y*-Bug z2DX0~w$8x5JeD;bZeRz5VSh5P=LEKcfsGEsW*FFA0^8BR#)M%N1~x}vI~myWaMSN% zU?&PJVqg!2Vcp+qO@Ae@!wu}=Fs#+U3Iuk9fjtt2J!D|-R#G8R1A8_`J!5r&O3unPoslz}}Sh7}uFL|_vQj4zc3Jkn?1s6w_AScQQ-5r(A< z>^r=o(3Equf&DcMTWVk}0z1aQ{uYMKHn4jIcC3Lt7=|5ZU{?vO(!lNy!*(~YQv`OL zfjtn0^);|j0z2No#)j?ft*=!fg9J9oz}Ctbfkez z71&f++lz)Cu0Q#3#@%CP_Z4SCD<8n@J;ul7UC}sa?{;Zd)^RSl-(A{uS7f>0Nec6= z8uK`ddM>tLbrutN|DqnAing!9VmBuJvYf&7I~>!+B@JEKFL@N}0Ov>z<^J^&V?jWm0&Z zGF(VXGF65uW`#t2Z&*Ptm>a)KoW4>1gWz)xrr=Se4KH?bM z`tUAGeRk0L92G9>Stg)9yJRZsbGA<#?B2Fd-@>}}kOb!@qj_q(@(Ztdkb)(ByBI&k z^BKE~@m)TvkzI_i=9bU$^m7-kj?W_dx$IW){D<9Ks81w1oVjOe?gBR#_mW%2+=%AR zMDFTYCS&p+k+FwnoW_iLvFeml&WtUP(a=5!e{GfWt1q|J=`*9EwyV!&=itG_jDEFU zc{55oPKIbhSva_R#__lLVAae5z|aA3C;qo+nWg2VTH-2>#!?5MPwR)zO&DXS` zM8*gk%etZ?kL8Vm<9IrJZ52DXuyIh>f^;XcI4%#w4HfbE&leOITmHGEF1*X1+GGb?+6LcK0cn@ z^ZC_vEMtg_hV?}qn}ZbZ*J?@i{v&9p>(Oj_=o*@?YHo^roP&S4HFG}36X0v;lSIoY zXZt&;bUnfmZBWG9T+l&wgP8MYB#ntUGq*xYGaKr+Cgrk#d8!k`20ftM7-Bm65=j0E zFQYRm@D@k4{~c(-Jz;t5DiBf<2ea7#|3ezQAK(DOO#K_oP|s2hhhFEz_y)bZ<+o5& zFx<^$2&@#f0`>_gwg3fsxlHx&>S$Uo;Dos z?_JCoVe@_=d2C_nWn1$W5OvgTw}}>J**gU1S2VS-q1X4QI@25mF&`Wk;KI>7>WMRJ#8Z#t1x z-q(K)lz$f*YB3tE>yPNrTt(7BXxGROzt6kL`PN?5PACuAJqC~K+t;3Em#;!s+3fOl z$#d=UBi_1p`7gPH#b&n4P_4cN`u|6@zENtHxi>D9Jh#?&Nj;$>37eM5UDjHs^+wyW zUjKC4Q`$?MAsR{?UP76pLo*p4y36#&g*Z|+t~Y=la>B=8QgKrfQQdA< zgb`8%1N2TxZ3{NXPXLDZj%=eXijYJivmSdq~VLpmaQwQBLNJ& zmn$lOBI-HlAqY=``^Nq4peAL|qlxydu86m`TJ~!L#~@}fe%J@iq*KN4W3rZb3(isV z#hHE_YC8Yt#op2i!VM!Rg(0$}!-^-%H z8vQGK4?-np;emvwYu7)SwptG+8FEd(awmS5xng?pwXMJ5bUh#$IGNlnZxPw0&Q}8& z=*fOgY2G|$oUq@M&ott&5kJG(l3BSSe;%=xiM)OVSAZ1pyofnc5WeHPqluT9+a!M#1VM+@*hwbyQ3%rxz#OF!nLR=*?*?-ZwpafV>of925_V=oFq{NB z&85FGE*p}#EaFh6_l68J!0Ti@IyGbm(^Z8g_`8c30iQr^MCk&djpb>Is9Fvd$dW<% zW9craza*ucM`Wo7!?%8F1bPuzJ78GK+{eOls5V)VNI9K$b*N%Fl3Gny>k9;;m+&Ij zad2Oah$;+JnD-NASo@L>ZAj8~l2q{m#ArpCZ4+Y63_%jbf1J*TKhhS%h+)$iIi^t# zp5ldPP5y^w1Ljj{{^f%VQ4(ijT9m1AU$IMuYp9W3`M8XUvZz6~ERsHpB>#d2(y@qz z2Y9B*u3H!`J=nW7O3BR@p@7+aD8bi%{oWI~clDV2S}@l)o3QZ_d?QZES%{@?x0Crx zpzBOIyI#*q;K=4!9u(pmy#k&>v5!cR-nij*+0eRRk?6rV%B3Lc$xAeZ@;9Xge+wFQ z)4mq`4-yf>!$M}7YJRBI$jB$)v#$q{ugx>G zn;k!BKmN0-ZFE%z^`g%Nzf9E1)4pVNFUe*-$jF_DV6joGdE!1ItmHn+mr~^fGvwMH zP(1+v!Ka*K#VRyN8-$5~J+)3==@u3*V~oOUj1W<7xnTf@*I_1smlr9(3)&ix^N@lD zpzyS1_rCh+n85c1AOOyGOTY-#eI9@=?YU2o_6$VbT zzd!^-%o}=avl=q^+9ew=c!vZI%#uS7MX$rM@Vh9$k5*lRc5~2fZBPe(Q%)ZtWi;Hz z+pp>M*lC|arwzw~USC7`d>n!vYYfD-IS-R*#P`hvmWY4#Pd1t2@1MwC&07S_&*}rOmShtZ6!{Ze?HW6KdG7&d%^vZe}m~t?wy6 zZ4R-v3ukN?Rru9;oUg?7I7%{Wi$R}LiM33^^N1=RqwtE5>0d^bP*+M{%*SvDGiV~;t%P3_JNZi7x))9CLu@up?CN6_)T)Kh*2y^94lj=BE*(fohb9`4uQC0dbeJ$!#k zo2?yo_f@5*9*$b?<%1s5Q>}nKp{M+8wWf;*xG{<5nF@}d+!<=Ekd^E~>9W;wxKH$S zYb$jl>zEwg)fqRZXz>V}7BqCHNae(H3Lk7{yX&IBG)LW9z_+IH;vza|9J~w9T`FH; zmkLj?!k2%9vzv{rYk^!}LERtQg8b2l#=&OXjguTlFYeZYqp@BRk)^yOk}%q1!wPoe znKQ@p-nZ<07DKr)`-y85h3SzT@EVA12IwM4XE?TbZ{B|w+pJ_r$ThD}tTaEs&l@3p zvCZv5>c%$n#dtL(M=Enca=9~;Xikyi)yoObVM`<`a35)w z?XG6-Tt-TnN~fI7fN21LQ(6-mM>-B-i*vcRRAd3`Iw`D`T(`c)3yuwwwIZhE{&+Ql zW~wQYT)xy?Rb>b8eH8Mc6Ec^;D<-T3HgKXH|v zZl_69yTyc!Gbl87xOhq1i^7P&$rrbX`J;aYPygEnb;}lsfoz;wKa3~kR8qG+V9)3& zKb!Y!{QNWhFRRbr#wWtB|19p%*Mp11iy2$pjnG(@s7|zDfMK6+S|^})Q`b|r-@ZGPvrWTd14V;)ZLAxo_uHO^o>u zn(U4L6pf_I9_WjUkDx!&DCRy~`};|FZnL_l{A`|syaRDly|2Wxmh>DqEh09Xr-=I) zrN>QyT3+vi-hu0$YWZkS`59`N=L$$0g+a?hnJauHzWt}3Yk3{9*=jl0$0$@wTKH&m zqE>m3jgE@Z^I5yb@AniuaZ{H2@l6*gi>voAaIAL8tyvI$)5xCGBH{6ZYtD=QKu_rJ z3IB}**U7N$N^q{+h9r1RMoIeIpaetGB-q5hcxr~ecq&h74fn;XS~K~PpR46^iTB+) zZX>s$I$o<`e_9Gr@V@{ z{r&RTaZGgWtAqdfM@9_zhB3m!wyzg`_VKT4X5=4AAr1bT?m$8ZovURk< zr`gh;MrE9w{33?7U4Bufw}oGf85@fS((H{Oy%=Nqlp3 z6JW@uGKMge5NR^g=0!OUL7$9QZuFx95;Dw=aZ5t5UdA2`H|m)I5qkqwE#oZG+^-n; zGqpi{VW3YsI_gglj{17QrdU9RD8;TFV68yfFyqp}o~{ebR6Fx6ikO(W^6S#=eI9y~ zXG5JhQ2@@zKO6cJ`=NRG`6@yiC=bmMoDB`C&ORFwAA(U>9LED-7V>0iR)w&T!%Y0h zzBhmOy~Om`EGQ`l-uzv+?1<=!eW@Pxm#Z!+AEtmHl!QsVSpcx^Spq!dtOXFmE zMl`&K0eb=S{k`8>hR^#oBDBxo{kTszoA>(;s`tF#WtKrE@0TC+#*;zWtM>zq-y3Cn zrW9DinMEJYkt5XH+g+MrZj!zpl=gh8EDOKqf9K~Ho+n_Qg?B84v;@+$@is?pH)Rbc z3KCc!dx!5G3Gu1S2z`e-5TxD@WA$}$+swi**8^Q!5<&PdJp3LQ`$0w>q62x4 z_%3B{%+Mt9OZ+NyhYYDAi$ohjTlxszlgd~DzW3mdPGvCS|Xhq*>Nv})` z+j*yE=@)b|fTf%vdhehIbi(8v)A+}WctNGz4D$>66KR3Wi*jvb&J8pO`>-fy7U0fL z$l^^B?cv1o8dx*G1s{-Bg8DQQ#gpYIK5U8k?et_3wFf)xB;E3l!gM?ILT4w0;t{q0 z7_a^M`hq*Yz+O9yv=$w=7izLr_N+qu8@t7Wij1mfL8dfKr8J9zX;v#1?ETiqLm zS$0bFuvGU;p-9;)1C-Oj+1LKI*_6~dj`gffPR)SuS?`fV1+14T8B6t1RbOY8t-Iv) zG*=7Me_5vfF5j2&zF*lXLzO;W84F(Y*YY7syZh^jr;%trO?mvZw6{&uzhAi*KxQPW z1#{;L-v1x3i~Sf}GkQJKn$Zc=pgfBRu8aM6Zdig0V-CDq${9}|&Nk53NzF>pWk~hf zE~91dDLeID55Aq;iU(f+hsH72Z5fp9_gt`1yPnbWdevS!z5p-lRmS!E*Q=st_g-+l zY82=KR^JM}I^%&#bvyP5G%XU<^xBLuUjgj{BbalMmhjm5U z@#|_jVMefPg(R%2MM9CLt4#aakC645fPyv%{|pLxV!W%M14-uF*Pch33<}!kOsgQj zEtSBQoHYg57vTKn3;^_9qG>q03oQx1Cew!_bh?k(W563xzh%-3P0%-ixEN=d{1-eK z;V8*-Ul=^u%=>k3{*$(V6a;}Q2>N^!Q%W&=hzLIv?hN065_7v*S+>y_Ul0|goPQfs zBJ!OJFwjSN7>H|gkdwKCvgC&RS`!zCz{LF~ z+^186Wrgswh))#nI^Qi@L@*mL;X%`zb}97fTn)C;v~SB z1+fjjgR2U^4@+zBokgFS+k02ZbK84=;fO+IAMawz|G#bTwZh%s-qp%d+WT*&y$2pA+ApoW|1)J%+WUnDw`_lVo9iMprkG+A z_SrHmRXz;ODTb9Q=dp@%i5K3>EE3lveb=LTozZ&Ak>NxkB&{dahq!3!P`V5LYRRcX zqsk3T_oJtBq>DJd5^VXb zv6&DLG?ZYbF=Wc=Cqlb=qQ4uBy0&uNgTZBbV2NbBaaAert_?<+Ezz-&=Cski5NB5H zC?hs(Wio|Bpz0mObRv6&oPQH(i`I4(&iplY`LzMi>fhye$LZ{aG)2fGLtIi>3DP80 z(9xUGc=SX z7LT*1a8f8gx`wWfV`g-%!R6wiAeX$g!0lt?YKj@rYI~tG6wE_eY#@; z^d&B|gEP~Z-@$IsF+y_Rmt0tb8mx`&Bv};B4l}1Mr+RsVZ}@VxWZL9QaLKOof9V9v ztEW+#eY=HB>>Dn6UBqE7q`(X{g$={o#IT@N%dZm;6%)a%MOFyRL@!I(J`b;2^LV)v zba4R}DT8o>d+P!P?k?LrpSQN2tr;RFK9x35M2gfj@#22cpc ztcs+WY3gb=XZ42^v>z=3B9Db6ihmr-DUeJ^Kjt!}I#6(M$A%=Oh)H-VQsJxwQx0a6 z1M4JM;TWCE89#~?|9GJ;c?tn$@#;aTh3OECzfcm|5S8R{@tQ;jdt?#P@w-oS#24Hw zw)O|;2xmDmZGbFLh^Vc6t|5HLgfvt#4?=TKXcm#!BOrq;?_d@>#3!xID(X1c`sj0x z6(5bFQ298b2i41OQJ8Zr{I_-2x53k!^|c){`n6KeAyRK>jNQ@2Ptg=7US)a%S#)V?EI)lAgyRcql=1!PD$bICl0 zZ%!tvYJh1lLn$9JAUP>8Y9hwg$Ng9>?dmsRK$geYA89GvwoDldTpCIdpN;#3RCDdS zdC~9Qh~~Z!jloEooGaDX42^#9(>(|W!jY4g`dzE-C5d5qkQCZWIX4Om2*O?>vvo-^ zxz&;jGxNCkXisu!8x8F}NNIn-SFtnX)pT?aWw5*U6R9S9Dj$I25E$W81%VGinrd|f z2=!fF)NWFN754*KXN9=c*c1M)IV5P7F1`XNs!Mu@0jUm5WtLf5lp3!qf2mT5y)Ej7s($^Lq* zaLFoHWU=KkPEI7G8(HS-tIMN_ae1yisshpIGbnH>ve@_^R1qr^vi!wE!Dgl z)Ut1~tzbo17}ex%yio3JcbpaODQBPKU0?e+E*roi@L2thV<`M5 zHS$u87k!v0$?pp7Hjdyk_S7uXp3HbRvk%Va6+n>h}po^}CF8hwASMOONVbq7>p27$B#dbvCM(MYEP*G=Gg0 zrNEnejOHIETMf)1Z#bII5Xl#jof0@Qy4grxosiwW#fXND25WSae3AQo5sU)Lr46GA zILRRM2qX8FqrB1N0}ge%N9(+bOe+$!5l~ly3;iX6Ge+)sp*Iw@-Y>-u zi0nwao0RNnI=hT}lDn_2o{*5P2rS`P0#C5?4dx_@rf)Tq&m_!T1)s%C-;CEiA(Bc77S)zS;}9V$30 zJ(;wXN3Cy-mWQqwCSVF+9&Ph{iVhr|%E`8SR@w155)?_8SaZ@MmSoOoNg~1#{ipwVB>+bdJ#QcuL6y7=JbLonyqA1R@?Qhq?xP!Yfs% z8sRw>VB_ygS5leVsYcxAD)&s`xB;q^(+*vv$5p{71+d-R`2>0c%BXDl}$wDB-^Sa}<&vz=3L;9;TOoRwcNzRq?|!M_d-@za`9a-JrK znlDI;3G~Z>Tm~``F*(+Gn*Y9C(&)dj;fKAW=Gg2X6@+q!>thMeoX8@q7xWTfmwEpaZr$Z z{-`mXBjxfI&kJ-?k*GN$j7s=Y z!VzPPPk?!EeFzR-Ub8okzT-X6I3+wK5tx$SY{K&xho%q5y73g||E+fW4=g@Y*_&Lq&y<1NM?&ouKB0rw6c~-ncdKCUt-2E8bew=2bV_z2 z{>w45E%c9+^X4%0bIvNvBF$f=SF2;zbr(rd{kNJRL;5-i@MWXwJNT&1v(u2gO9woZ;ReaDI7F1@@SR!nn4`nS5Y zu?>5ihV)t2R*fFM!gS4aY3L@nOY}T3@jMH+j_pp^J>| zye~=%JYTJF1JAN??gC1>9};~+mttNrX*`ZPp_wO42vM6vs4Q7;dRZkxQJX9=Sx07R zFB2==rP*Hjra%5q_<{qnlPM7OI{P-4i zja`T`Hl>`}huEH>_|dgcbWncuA!wmC9t+eHO{uXsJGBXd{#RP%H(8>PfV8C|ZRVK= zr(kgLu7|cB!dRRMg8osZN|60-5PmzXl&#k;&NS+X$BL0w2V+E zFtMO9AKACdz4twd`yW2i}DsA)`Fq2N<$cjXDkV8L85i{e@@EbR)i z9~03J>=e%9Gf$iKIggi$F{ElUzGRlj!Y}w$C9(A7GWnS48m9@<$6U&0^9UEeK*$X3 zuguK4z@25g$_S4L!wY;dm+biC`(O`>mU$C$%rBqfzUZsbY2?DnE=uCyx4m61tPwRN z^68U#RvQe(yPowHKExeUK0q-*Rpkvb_4HmYd0sSRgp(5zE!(qg4wU}to1SJzw3sna zH243p_b$*`RM+G7fT&1OqD4ie8Yya2#5W|6fDyta5;Q=#iV70$K|>NDAX-!uR3f0L zRH>p;r4}u%)C)$X8kL%OsirN}v_?%8?W<5tm1=5%{GWZ!+|JCrNdUXn?^~Z)geNm| zuKVn>&wb`6>WNqrCl^FEzd}k%D}r9FScL;)tLb~GG8yD*pleI^NiUa564k4Dh_ofW zoTKy8=+nzWZhcpJ*-wIx*2`)C^`xf@C|#u`td|eTeV<;+UJ$L(NLOtwfVfcQexf{7 za+hh>;KIv`-|}YM@HVRZWW$B_6xK`o9Y-ze<$F&PN=tgFDxa#3j$Yo{o?Ry85qh~& z!o#Vjm$xVhMd{_q&J$E@ob6Alm8q9ss7cgFz0``3KreI7{tomqTau`@Beg;=PuKbJ z>t%OteOG$^|~t{qKV3|S^y_k`RXmH-fpN)OCO+sKK&{GN$*vJxB65T2h>8Ye6~7)0G;qu z>GUQiYVVd3Te8}>A}SyK6O8`SVb?Xw^zHE@K!U8`sT$+Fik*NehC zdo7fI^=|9t_gZXHJE)cGYA3g%+UT;kEv-~lbSV(_TD+wDF?volg@f|w+)Z%ft3iu@Z$GkkQg5<%QvX|95?pllxu7VeIn?1K zsc^)M5Nj#v77~l>jpoB!61(m46WJ)F>J2Vb7ecwz<@c^URDQ{*?m|FsccR^e;GS=H z;>DgPVnc(`GTG&)c4GSyEOiy}sjh9knHT3#(vB+~y{-GP(%WiFb#qnWoarVVW8w;{ zT5nXTwpFl;ml_n--3Rg%yO-kCXrH5*b!WJmIps`u8pF{_d)!Wb)F2_Ql;KS2tji}Z z7|fVExNj$PEmT}8p;GXwqLj-MH5}J<+CQ#z$6iV#jo$hk+r$H-u}o?RIs6XJ7|1p! zw}ud8ReX@#X}*T|&KTU_W)o zna8{FNX@v>tQkbfMA}>NU+ZZjm5omuqzvqqR8;{I=vIMRl{l#|`_KuQC_(DZF`D3L z_JN);XnV5QK$R4~hJNt{8hYw!o`&{IaW(V>{#+Fr(j=o4q${MMa;7Axp|2Bqw1&Pc zPog!{n+I?rsgiP#PeHf0>8$;c4rYRHw1W!FMBYI$F<$+yXCg1?D|xDwnD0k!wt&1b zq<=C(x{+%BMw#R4USuMACNe+Gebhzy@Ma?SvH{0me|@{Iv;e6N+D7p4mrw~z`#4bv zTp~{rmq4)Y?^KPCt|e1>i#l=D;2qUgtt0fcj*^mE^%N;>CIe>OS6^~(b=_aL-N^!x z7tX}e8}dg9xnc-W63 zD}EAD#m}O-bDE}s-Q9}+3V(+5TG|c0T7gcJG*r!8zrilhngn)5Bsq7bzDz=T#)#5* zI%Qv;dZ{5!HCSU=)Yp!BA*<>Bs`!v#=rPxhIos1nVV0C{xfZkTL+LwYRx<209A6!$ zq#Ad}CU!OT?0}cTR?EximHHl{c?RQeuqCWpp`+l(CiO-YC5T zOr>;&61ZBc#u=k7ea%W7h3b3poZJo^z)vQziM8p&w5BHYYBn>isucaR&PqS`U9S_h zf@*Xa_$WxoC=YVqI*0KwI&S^sNwiURDw3~{Mt?EMxh=x`BeT>#iG=E7xMfYW($g)+ zs|PxT*dx`oJYJrt6k>{H%bomftn_@s=k0wfJ*TOlv@Laa3QcuFA=R%aOKB*bopQ9= zms>COD$1&OV0~b_vgath9;Gu7Xziq|{a$}tgH>Do9D(^~hL`NjN77Wg)~pt+3al2~ zFDdh8zdo1FjNRyr{J?44>g_?>BT5|M*)P5LrfQ0+{YSe#X)cr!K?JW#se?RJt#eez z)0rIbeXs0~QO(i9_uWt$RC6TFJtZ!K3PZ>%JGappQu=@+o57WYZyZ51H#2sf>Z;pq zoGOe)yhYB~{!+p#_s`hg?)JE8qKug_S@m?J?rl;J&e*=dqJ`U^SGlNlRualo0J^ua zija(zgga>ud{xixr1@>QGI})Y-j>&kbc$#m%r|NdjS5RQzU3+a{;rDm)yavIO+t{? zSe1N?+o6Pbp6#mhq#E}iW5e$z?)rC`NR2B*?KhzI>k`k=t=VgNb9530oh{m`oDI5F zs?>jCE_`!z7d$F*4$aZYDk`^Yv=?`dZl%6WQOI17H%IqRkpbnu93S4;yw*7+s3p=6 zF)jC`d|AeumYc2SePa}aL{Vu^uBlDB5*yQUXg&Es>{pvU&sIKBEb4?eE!SD6ms=u) zas1lTa)kEeCK8Vj#G0MLEwgiJ8DebsUu#_wh)Hn*V_g;l0O{R7@-;TN}KFtkAeL`6p zO{2{5(%i0O$*z*R%ESc(W{xbAl z;5&tL_Yq=`RDB`$O4STjVJG`)Ur)b-qLeymAr4odr00@hlb>xFU2aYQX+Yu zc6dnMiwPz^@`s+hry+Y=-f{knf5iF6eUbMsNQ6(`&6G@F|Dx+sRg$Xs-2~*TNlp#4 zpRO^VCa$q7j~+I#t%yZxo<>!?TXvu&34~77miE?rU*ox*%yo9hy<-oe-_PV)T+fE7 znM(Gm+NYMk5X}&CrELFKT&-%xu3>dD&3rrQu%$BId>WRDZ381)_^>rUh2XxEvOLyI zS0L40%%|a0JMLE=ee{4(Szg9Vj7X#_>!2RFWw}l@mR0d9s0C5(540?uw{O_(c*|v3 zfs@1<{V?RP zp!$T38v$|pdN_6p@Y%pBf&WSPJQ1BHV>tMP$S09*X_x*t9KMG02-mi(8e4p7)OK5z z&R%t2jvNu$;r^l7a9h|BoM_G%i~NuoF6r?*c9OR|yNfSr-rXeMyp>zz;X<86BsA*{ zx&c!?r4`(YlmvU}&8Yn8X}K_qb-1pYIARPZ>Z)K09H+L#-&H4T%1=&O{nSFZXQ*|7pifVbc5C9pBbj+p8m3&IFt?%Lkjg-u8v&M)2xc$ z*G^Ru<@T52R`uxA^x`+G%-Siy?S6~2}6RJ8^ z>KrX4D7#2x(j_3>6r>wRy7hLypUN%Mc1d_4t5xxbYaH?5iqkcO;H-Fwfpm^^%3jbG zY~*Fis~$0lSEa@q#Gj!ZdTePYxDSuLr-6QyOej~yuadT4c^4&ME-zD;1frHS7NMMl}$s~2D z$M7n2**S2r+%(cnvXcYVCTNE5#FHsX(}&Ut8T=Z?`!;-e61JlWpTjpMBV?cQ6DB_*FYT+^Qp{{q#%i+;nEmYlM6$OWX^`l>(l(8 zizqFA}Q~k{5t3ajA$aU_%+xc_n5Op(l{;9otq%%}4 z$vaZqdUn38x(wO*UriE82krb{inzPqcK(4j`(fvQ^EJ)J{@VGonOpE{`~Rj_(xew# zsf1Dkl{xQ8#D=aIT^*-W5vk+y1{+;I%J`;KNO8%wTD!Jkd}(ahH7xGJ7QyW0==vAd zl7q1hZso%zV?J$hwf& zHxT5X*Fg_ix?t9Yv)?JeKC3B!kW~ZfX6A17zwPf!J&@JA(RA`Bcccb!VXW|b^)kugf zx+^b76w+s`iqEt|@k(^llS*y{$c@ruQ;VE5a^!ZpPi`U;zee@-MY&^=?(kDHJ)w0_ z3a!($gSVp?5<3UqJ5KWjPtb~3*d$^P(HY~XDP8a*_7r|wAni%zS9Y#PIRLNQ! zRX6Ecnmdc$?xa;P(B*ykKZeBJ&Rw|aDOW`(E+ zC|D`4I2D;1GOqTOoF;0H>_bpTU%0PY$;D>ycK6HIWeJTVwS9R_tB0N%DbPY`dwfbvO4M*==?3+mIzVhgTI^VvL*VI{(7Wa)DE(r+l8yTc; z(?AolceZPWIzvT|O!dlSrbZfOKKCiJIZ7(>$bRYQ23ypRMC=#os5#}bmk>}l6-zg>tkHrUXcO`M?bFB z(>UT1m61(KYTHUhb9+R-+T}D%?$`{Q7HKK$9_$D5K2fos?80<+x%QCc%Mnbqi-~hm zDm$hwEF&sU7o@J4efVUf`q2smlyeASw$6A=&@R0k?Q>jXTOHTr1CA@!>bUwv$7SRb zUGR@s61OyGHW#h5Ji{BFrOQXLDnoNpblgtWMrm3qzoh0OG#bI~2OL*&tK+i8;K|UD znB9t=NJ!>fl31}6ziFo5gs$rG-LdM-w_4t9t9(jDnLt(4lYyTw8nFi^CrA>_K4IiI z`E(VGHwX0tRVL0HRKj#0i8o(u3*_o3^J!GG;cKU;*U>B9Zs|t_pp1x&frDKRTU0No zCSJ^xn2vfHIgw(G7{~SMk!;hnAGgUZAJtxW7OUbn>8BWV|5IdXZoTZ%5VSYqBGfd} zlFqLWCz0EHJ5t3Y$!l;*kR?m(Z4#HbNOJ7mUG81-p^m{CyFgcd@qZ>MZ9_@fH!J*g zc5AJ02L={?dmW`C$)?R0nO4Q4`zAjcWCYjBzC`D3zP`k%1VLF=Qg|D*)>Z32v)gkU zebPpR`6K$CEAHfs0l6FWxXq! zGZ%7PyqsLbYD0O_k%9umu zFYNiAEag)=7^G05^ZLUI7xnfT!w|$R9N%nBzXc6JK?Dbqi@ki z?Gq<|m?fz(dr^ak$!FL1xo2}npjJ8!LwG=PpLXfBF#8-=ajWCH zDk3h*p-~R@`7AamFt4!P4Y*T#8033afu|}2wSSh7sN*%cr&pWKoW>jlG8?M!dz#}| zK5CPCd-viq`P+O?bMN6yis%=57r!OtSQS6@6VVD8W|&hN*M1t*7D+}XwooO5mXKOB zT+EbsrLHRdt^T{MGIslU|LtT`p zokV4^6!8mp;!f|qtdl>D`6@?HacM4PKuR2tZyXJf%(^RkJJltrlKzmap33^>r*hK> z&h9Vj&{RlL*xLTHVN0pKzE*iZCe-RA8!bolT4zUmnRSIAiDsX4>Y&!?l+T$Xs#fS! zd!J6-DMB)J>Y>@LP94u2sfhbeAGw8TOp~4QkEjxBNvF38qeUjMAyY4pgW55P-jb5*)!q>r@YX z9k5RQPwiBS2*$QkPxKGzRBw7Www-$AHLX+E{@pE1L#N)}9#vv3>D1OkO`Uq5xx7}{ zsVnQr}e*0$Qh2=ZGjgJJmy72JF=7a?=QIAMMn50@!z* z%EMP!r#!nO^`whC;uLG?4M4BKv>#ET z>e2D4N-1CY_M?3J(Me*ch#tBUSNZcwiUw1*$1E_D&5Pr`J1DjCRv*1s|gg8CL##3X|sZ{TCKm?0Y}@ z%e}JJnpggLUim-b58z_Qk0d$buKazucNi}6$}va05nCF~C0kQib{IIxw`)(zB9|De zX$LE52P^zTN8b*$BSc`{_JdR8^7Ou>}V$?Mj;y&{$o`G{mWEA)l;(h_9cnBiwc4C?ogLWQc^O) zk$2L(lxv=KR&~njN%}jTk-Oe{N)q>XH^fPJ_Ejf;Waiww*m7!XI9<|9AY(C}s{+Xg z1ek1eKFmN5SFyWcwpL#PI~tFZu75N7^7^};zWh^-C8P(Y)aQLYv>XQ?WqTY3C{(s0rWE|4Uv~G>w|PvNEL9=J$dTwS8~?n{Y2hbNzw>}QGIDN}#Vl<0r{>Xqm$HBIQ2=md2cvH)LX zMwRH9;&{JH^rO1{DAB5{h!REgW;Id$sqh}v3OxIy8!WM2m}K?72C>xRa?3wf%0+Q{ z&7&QYQXRiFc0MRXO@2Ym&!II3IFYxNY8PQzZ0%Y>R>WSMpt9s9I4B6yUd%T0zn|Lm z52?q^XxF{(c-r-xX)iWL+l$-#L}}M`2V^g9{M&wL*Zgx1K)Yt*IYEIfsmzwbx;n87 zo)#y5YJV2$Qsp9S|7@KYZ3ZBg(NJEeyelw#p`9A`4wq!YKy@qeiL)vy2Uy49okuN z4&Byd)XA7bAFh;4`lI~%=Fp#znV@o+E~hu+zGJc8e2>FAaa5IKOq*}GTRggx-=`?0 zj;S$?>Lsbk?~`?`YTBFu6e&V`i%p*@AM~=#Y4d;R+*LT9`YKPsY4hH4S7&sCJ8iDt z^iXY2EWdJ>>x7Ia*{-SNva*Gcy?1PqX^{hI48G~1)6%=ByPvM$KPgzr#94o&_NmG7 z?)X7=?yEYzVAP0jJ{j@m#OF$CMBMV087T=hCtmEO5&d@O#DBo;xagCP^yPRLsq)$c z+{d5sHQO8$;OHCb7DL@?!}_ZX$I($c3-6-H1r|4bLX);g0Fi$+6V16v;9*%vc8vPcQG0jM~#S%j9o!D)6!k+-aLm z&xlB>;#F^nfGOU`JA!v@ucRZ>Nk@YD<%hMT<0?pke?w_QXny&|Hc|lo`Q;bh zbkkAen_nI+3i1&}<0!~Cf5DwrkVS7hNpvLTldz~hl5X$L*Ec6&GM0?rxEeQzHC>+V zy!SBd4MhsRit>}6+Bz>@jdyOnFHcm6Os!BMee*g_vFccTli@$hl%jDb!*5ZSfk~#D z`$MPeuOMZcxUOZAZE6U9wu4%G+)$5 zy}+$1-cKE}Gu-C*-FEl6JtFM(IKSIQiPsMILv?({2=|{1_}nzS;d8rL+%B2Z*amya zk=fgLRRAA=GV-@wlCh-mpt3IVf2Z5Z#ru-RHf0y{f7`M%cl;!f;l(<#LnOmHraGz0 z3#aNrzr((c!ykto_VhbUavXlEjwj{Rhi#u%c2TOTtnI66NYsTiqHnLfz(Rl(|LT2C zuDIu$4C|4L0#u`7mCej~J>LhxDDB53-{p|YX2v9kzOL+sa{Gws^&WK@8u9KqT`EIx z&TEc1Hb%U??K|F_*N?dqJ?HhWKLkyYh^&jN?{+=BR2JgBUf)P{ndF=E3f5?zH8%_L zLGJ??)zskGUzh!JF6QX+JzP@67zbAB_`5l~k#NWBF746P2bD8rqxY?a4QnNu*iyTA zg5#LjoxXeiB!PrCGTF-^2uEEG3vZjiMR{RwjtH&14 z%iEkI&sUu)u7l^RPLrEhI}+MUg(e+|HIiQGn12vIQXe2nc)sc%N*0L@;GD^ejtGw) zTlD!Vu^gfERgc<1dZ%$#sM9!!&sX)L<&sQ<&Q~2L50pM{kQ{bUmx1$DSIO(GoHPc{ zSAFh@=Z7j;0r7mPPpP=)t1f(=!gkIHHFv)1NO{SV6KjI=RUi1J7jeGof%J&;RYly? zGcdkVyg(&0@%gHg#gk8f?)j?rDzrq!o5#D*-An|}S4rbzYgVo3Br4^V=(Fq;qpMWF ztqLiblYZxuTXW~D{@TflGV*-Yqdvig&R5MLA^o z1yMGSOdVz`vUk4fI3*L)?EU5U#vIXpov(WJY@`x8Up1vyGv})^*@e;i^HrN(P|aO6 zBcBw^c^k@&E>(UC6`Sc7>D{SO!^zTXjac*h*H*mm^Z6>Tccc0NGJa68d`c<$S~91n z;I;fM*zcD`TIHVozH_T#ztzTIWi*ui9>vp0`@LAST6%7g_PdKThVFn7Z8%%iF>jCc zh15x}AM8^poo%~6)dgQsy)gUnvFfqw*=@?4J5JBMR{B6B7IGR*rNkBiYuEPe&r~gC z{Bcd(Dm&4+J6?*kTMlXL!_?}({wda}O(`1!vXTC&cE2Ph>E!aTJslQfB zy=CP@{aokSvCt5}t|3??S$=Kkba!Q^&6U#M_-Q4!{NQ-s=aiWy8s1R;{2qCUonr}t z>FsT%mlT+L^o^=u3l1iiaM|6PGYsii`vIYtI3~sF^1Ca%q0;( zO{(~i?wE6g2;=lt{NLO*a~&uLbZcej8*MA(l*ey>7aV$EFB?pO#3;|utg=&RC)w?Z zvab)Rq`9j^SS;0S#s4g?>_EcYzk{uTa~e{xJe9(%=kA*#_SpY*9g)qD6MfS-8A*erOM#; zkV{E(XrO7&%e{*0T(+)x<4uW);0V$b{KPFrp$Avcqh~lDB`}|~Lk+I(dZ+}d#@~7I zEgHKXuX>nRFE!w&k}PkmeG`}J^gElGtKzq)hyIt#KO%RQY)v+gs0S0R%#0ytDR5KH zqR14C-U5^0$giZvm(4`|Ts4L1RHCV|XgTkCHXvtR(6%j`7mVru;!`Q+kU?+@IigaI z3g${2t1!ITb7uyf8FgD0iws|pcJqb#Qkh)4`8YmoyZH=%t_n_xX-ECk>uRD*Sws@w1wPLyXN1jhHHIw1%Nz2_KlN4H?(wCNbqaW(^Uce#5cFA z;$4fCrfqVr+sYak#rI0iV{LYf;_fE7LYLR*zj2yBYCP6}Ol%+aYg-R9AJXH_hL2ePiYPhY?o=3QToSWpo=ESN_N1LM7~6YKK$- z7Kr$D9sZF?Olr)@+{Qm|S@-tiyxvn8-Wr0g-EocfQx*SI4YXA9iL5Gqz1rs>6`$lg zRk1p8sPp}y2H#0j>5G1&ibzlL1?Nl}pNy=BS|7R?`jD*jK`d3d(^syFzkimmjNOv2 z_TM#1-st5@Kffyev91_sPFwPBjyogcyv!fEjx$orjJWI>>1Fs0SW#_z8;rar`E`G8 z*bnJU?7krEUbju*{+TzdaX(-vqOZzMk}jdyztw4y)JCpRv?tb<*C@jMkG(z2hQ;nm zNdF_E-RzVoZ@!^GpwL8Fj2=%9BMfP?QV_S><3R~(MK5|#^Ez$r!ezafYt~<__=PX+ zZCd6Wdv~MsT8;+S-UQzbDTxZ(Orw9KPj!d;q1%4z?;izQeP!xSQdP`OD{nhemlT-V z3?EpBv0H9Ke`2JLOq2Wg(LiJ$l8LH+rBM-dTWnq7?tXx_*Cr4|4V+bZPNB0qj!=>`L(U8+ug8SRw!~B)j|czxuh~?JB-;5d&x|ilmHPgADvsC zpR2BxROZ_b^KFMGnK@6`s^|fajL%TKS6(Q^*2LRvksU8;T)z`z8q_`sE0^kCje)Lg zX6H+$DnAJtmsRoiQb_vgFDHB-`QaO+>!scPR@|ylqw@Nox(w}nIp|OkMsVlLd~s~- ze0hu)4$2*G=gWQE+0jmAnjV0EKg2hW20MU(N{SsIGi^&HYKl?U*RM+C5-qe(d#U$X zRQTQkft@nW>A96nG%>ROk}OFKp7shCqwhpeSIMR~)IMBYM!ZfsTOK6dnIkH#>bUCf z55uupFHps1+GeQ|HI9`&UBBByT58HsEU3{^-z!6!h?aWZBVrut%kDU*=;~qC+x2Tr516GcXnM?Jrs>^AiYme`Ved zzdo6Nbc&(p+qm>({+W8{m-%6mq1MU#fb{%nV)oe}X9g|BY4`lIG_|Rf*wbpNSEaNK zN3@e1NuuAe8{>N=8}`$&JurIeoyk0PrZrv{2g-d_{N+=XRm5O&f(Q$#PHfAJqqH_E zf|0HS)#WZcGtoxFnpggb__|JHB=e5t>cfXQtO7Yld|T`T2tyeENo9}dZ1wF=7wLlyUwp2Ac<~lTRood7qxA?cGM-9o zzF30e@C1vDlY_o|y#g7OGH!lxulZfJP}^jWo8k$km!>rnMK~LGo37o+yq=VM%O{y+ zOj@G&x0oTeAjU)KEAG1(`viT(NKnFsYl@kEq}`*KDm__s4vIdjT2EDd*0Z0AY~8v( zV6<-gDvKWm)9EYja9Ioux)w1r9Yl9uy08gUYJur1oWy4CG+)X(vD7YimFNl;m**>} z_4Yi@LYzrI!F~C*q`sLsY)%@nXZIwEi0Y}%4nfQR*3?h`860Yxvay#--g{Y_L36V7 zzi4Ez=i!XztFqhMb7j6Bw<~t?!jwSf`M;qPI zaktaLqho)3y3dX1jM>-Lj-PKLtG+?-eRA2%`1ucOWaO&H&sUgUr>e`)`1$2;bUiV@ z=ha&r8{_9a(z+Pq=N;UM9zQ>QucRV4)|CQoVYZnbsU)AVr@1^glK?%V5;+*ZL`{%N z_o!a1Zl1bC;oVoSgwo?x-N>=`MUp*d?ColaDp|D0YgfX0;~S0-5~+E^an*&1dO=4I zao5`6csh+5+fQN&oJSViw^r@S&V$J}EG)^8E~jZ|dm_Qr!-x^4s^ z%i7FPJ&h22Lztsf#@u1!L0o!6nB&w#{}3iq?leDyky(@mXBNet>t5Meb}`$=#7%bK z6k>wyG4FOaMl)`2ODw1TcJfco5H*lF0Sa;hB~z* zvX5qHj;}z&?dL^ipuxr|LHoH(`SNtuNwI1nkf=(z>LdIfmwv5rw7W`L5Y@O)gGJ?Z%Lyo3;&hg2)Xa|3HRVUvRvHz_uyP_--+IX z(?h=IC~ZYx>UZ~Qr|I&EMk}eU@eiwNE2A4{sAm8GCoy{Dr+c9Z*A&4uQo_yc;)qT=!H*_^^)=c{_4` zt}LOqBZtu}DGBVzS(iA}pZh~?Zb#1X?-8&aeQ-z4%-ch?(kss{zEXB-81x=tb4*5zM;<>-BU_Kza4L!wMyY8e+qTQkv8MNWJcLhgYx9 zqsrO!`gQp;LB00mOR9^B#oyTai;Blr^VlcO$fw~p-L}bBMSHf(L&l<=q+274w9Ah| zgiU{HCMD;qz!JtKkDKv5J~}COLiN*xVULUUynp#PwfTI=<7V>FD>^B5KJ?Rs`ACiS zynp#h@DAaUIrY5ZBd4(){iJ$p{=4GH-`|&9 zIV*W58G;O?Scje9l+W>Q7z3C@XjpJ?d|o*-!ToO*Tf=x?79>g0o3|MIg`!4ZdT_05 zS^GA|2<$(WWXm0aeWos1`j-C9i+U^;f1OQfpCnHcZW6gg4O{w-LpLU=mjD41x3J?5h2Mg~c`Z~~vLi@AlE_(NxRkR_ktbR%_o zsB~fiCB@{OIiGeYiVz6GuByHQw=*a+mpjx>7U?=KKdKzFD%vBuCEM9PUgZ}-U}TCb zt&1r-yH)YsKaxU|-VjAfZ~2}0uT&A4TFLh_0-InB<*zVB;0W1P_XBylvDuK$f4{=CuA@c^=aGDBE2x1-nZ*cNP3&bt%IZ>gyX) zsDWA6s2peow1xTfLuxtCXI+iQZ(OfhFt4_R^g)(R_<&-iY+QCfdgRFSy(KNm(}?cp z4>y~d5;Ex34p%R1<1S)aJS@=q_)~5~HAo3dxANop+pN}x(^1jU+7b*rZBgQ{ni3Dp zxajHr6C`nzt7>1AW~w{-`*TVk{v&&%^nA=2lKMpRF9}s~u(F3=u z;aCF`oU-J=Jw=L<5$AL@#O}|1o)2dx)xQ~6-?d+s$SW(=wHJB=F?Nxdn-Ooe$}GJl zvXrQF!}U5nSSZQT9Qn7fsPzKW(>sM)DltVY3%re%8!<~*2!UP=-Gc=0*~?c627rnlHos$#sv&ctW{uNjcuVC(wARih(jme*92s?TBDK=GSzMO%{~yv^4{gx8XNC?c?MB3dqo**XzMIrJt0BG7#(hkO-`Q4aIC z6)J~|*Sh8K*5dsvhccbyP&r(?C_-+nD2MI|!s{6oUQ6ZB*meKP;otZR)`wsJWS`5S zP6cC>Lld_`<*@Tkw;YCEx1Z%;C6L?KSMOgrY|`}T?PiYH_J_Dfa~kgyA6Q0$^N;s zfzl5kZ7(XX_ACX>V~cZVoeo59_TunKw%jrnAh)(TvwibTyo z1jk~XRPLm=6FmcQf|6!%214}#{}*$GLs60VQS;pVs=2}k5rQwjElhvh`HeH|ub1Lm z%B4>`=Bvwqc3gXWw00OhyhJ?Q(ZV(e_YqvC2L>{v%U+fIDD@M+VxADT#go(BNt zw85**l`&o}?rIs{)u`cJ+*=y`qFAbpq)O%zRquW1VwOgarTREq9?rxp+1>O_@@H^U zUo5f_w=1c!`)83hvMdzPIC>kK$1jU)#0SrnE{j=WVSzi=`MYe59x1`1l+BU6s+Ano z8nvTxwcesUM!EVd$~rE67Ugzz8L-Z`%FU2P5v9_a?MWgu11H0n(8m|=Q3=g zcNsD1Mp#r__cCO1B{2W~E~jLD^Y3T=!fA+cSrz}8oS~JP8JvIbP60&BzuR4!&@7g8 zO_-7MX0oy#mE6gMoZ0v7h-`h53hPRPBNNFBVBc;?Ok(6{9+RFUA%8l+f{7HNm0x+EZ!AN+p>3i{j zpZaU55wY~Ev|y{^+nP_~Tg|58FqI9YeILbo@!35O!TVpkM0R)oQ^}brO^DG` zN#mO+@i{atPEX}}X|nZ%;vuSIW9(-LS;A0f>^x%jEJx(B%-l4e!0jU3a8>jM3ZuBI z;+Gns2QANN^J!Rl_V1372K)6&jSKfSy%IE%GRmW{?e&h8+Lo5TOKzo&!D8>gm)0%T zf25kImHS__wBw0Pg@Mv|+CnyC!N!vi>vPYP7Kd2mQ#{umtD0$E^v)yHK!v=X%BF8~ zt4)o+uVQ4XDt^Wc)tm5Yy7S!bNlWT0Q8royzZ)~hDoc8zCh9Fb$ILQXRgqPNa~j0R zZ6H)F$HenTw|UpvS*W(__tG4&XKyA9@aLg1c6-WHb&=E3I<=Q7R_K<$JY!x zBg+XCMg_&ZiDj(~^kmktlVFX%v!PCX>u*@7uM02FSts(_K2!8w&U@6@5iCTac`N7C zP-JVw#Wr!Vd=de_{{n~WtoZE<{A1H^j$y_#@D1)8abb^5$6qFS_MhoKI6g?K{F1E` z?C)|roefXPywZ%W98Q1sK;Gp%XNpP~V&#qPTG8BuIC`ecq+Pl0o!{EZ^+O~=mQ~2N z;wxOanpm`Ju5v}q5ZMjGW$fDajXw^NxIBye4FP*+c{-?v{8G zs3fOoL|8}g*w9tGjO=yjsDZbVV^#bkGuwe^-!Y$tOWtv>>IC&cg{tbrb9nXD36)yM zMmW7*Y3=YM3*1t773Es)%e8*)U*2}UZ|b=fzZ7edna3FMg(tha54%JD8I#?olFo?9 z?w+(W5tH2~Of!2WVTs6{3MZliu~s^*!<^af>kF_}d6$~jO3iljrl6|$pQt*92KY3Xa9h|bh(ktS@>xnXTv4q=KK^}Gq~$jL9p%BZ98Q{S-8<@2qYu||c%PXG z2nj#37jdvEV0$Z%{Xw>=SBMEvy`(XpvS}bR&tlZBi}y91>&^RMKEX7^UQ9FO_N*u*=oO)7CyM7m_TmE*XQ|h2s0n~?3RkibnEJ6;|+i+#0gTbIX z@jlaCKMd%OmT)+iFS;TL>rL48oj#e2YPC$Rx}kM4@m~MJ$6jL=9fQP)Hr@weuPh2ezj$IWc9v&S^sitG<> z%R2>pY8Etz_CVo$NtxZ3c?qxKzDyreOwA1o?C*=}p}|R_WZt(Tj3L7HlU3i$_t+jP zM2n#+Uixvsy8CSED|$uc-OII*Zk%LJQSNWK_IpOcb9Zf?sV)QM`jaDdxh6}wCP$U4 zXLlqW6_4tlwg=LdkJCQ~+ar&sCeW5I9`Ci~zWtn4@k1{*rLiw(ej9mDZbWaUpcKI{ z&k&ft98OGrn-Y?{>a{sN`|onr|A`Bcz_sJ-?!LVSeZ)mk8E>g&`7v2GDveLic*`QA zBi~b1qW^=jL*Jz`u_RqYIoBw+m%Nm0Kf04gp?x>^ zOTSuA8@+Xjh|8Q4OPo)p_9Y^f%;lrA%k#5am+POKxpv0pUEEf}o+Mv)ge)TQZGuTO z9`EZ<8JsiAKdP4bzu>6)Y+Tr*YD==SWNVk;2-?Y#-Gp60JOPE|iD6t~q|McOSpgy6 zdJ^ZwMtOOWJ&|*g5{J7MWmxM!QY_6wsXg>Tt0yu7zEzE(81G ze&3Ak(ckPb$9oR?ZLY}G#uHL)>!&fm(<6=yO zeL*-&8rnqkwCIz0mOt3c)=SF7uWNAa@xuCoL~9^s%i*;eYi?afitV`9cEZH(Fw00C`!+- zXh|!R`*#9$b7Lgess8o|Zl!z_VYz7^%oxKR!p#I8kh&H?gD>H(TRys=qE1mB!EDx`AUNTG+Tq#|(U9Z2Iz)I(mk={z@-TAoD zUDmFurx94`Y(=FzN#)u72&2(eOOfD3`0nFiSIyrW^y;+g3GVn2UTbv@qzcHOmi@#z zwfr}yk)!#n_+3{gZah&d184{z*&kpgrXjCpGtr9jJ(&{nmG9A?x#inc{Y9bPS@ zb7`(*U&no?e0A^Br{OJCuIKRVRjyW~^;|QpWgoa{J>&0QTEDW>8eDb1H+fztS!<+b=4qPe+g5vJV?z7tn>?3&f=LAGGXzERstxfOkH)eF$_W?>a&H{0}(0M(_KBry|DuGC=u zb8GNBB&ODv2iJ-x)SDr18+zk zc2p@Ot5l0BsqVY9+{V1wri0BfoY?Xd{1CBTpCS(FVyc+8;aF1e)a8;Odb}URFB_;D zvUrueGJ&T^pema;*AZ$UbwLXm?y}vf)8fjzWgm~>BTB7&K^SUy6OE0}y`!Y6rWr$! zX9-vhLEMmR;kG;ixl?8;oceEn-lyAGvB9eEb@<&hBc^CX=3QKw+d4E~WbJ+KW~RF2 z4WTddA#d&7vt?e|=r6X{Di`Q4I?5YLf1t2+L3fbYoIF&;|I2PbXS`F6DJCOt#T(_O zvEM#29|O#M#9Ez?=pBBM`EYKwTr*!*Igy6nUo)=^)XbI@Z&gb^q%Ew9Z^T*H5Nrr} z)=C;y!`HM+ucRG~wVUdt?1}d2bI1S$OQc>BwMHe%T;RWcl&heXue%HU;duRRlHCSN zXe6h(WVEJyLY8SN0S5xru{ShZ%ow_=_=PHqRq;70ajGgvXP;!g!VAl382vd*R>_mF zeKQ<6b_kaw3L$71e2EGbgO`6d-UvHO7lx$w{MIoa6qY@K@zKcEPGHn_+H5V$`G%aD zyBN!k>$ExNn@*dn?eTV8H%UFRq8CGK^(v*Z`idYt>PZ;gF`YIKCl&fdN;xyq`6VS>W!DdVhGu{kYR11HFZnKE3{x7UQsvGD_r%wvB_<)GCXdt zFr7=USGWMTVkXp(P4#SlDtAJg+?r7=H;s;k2-noV^D;_*pD?k@)~|eh^gjsm zSJmD67}=IRTF}&_I!UUzwOG9_@1nAVCGE_Wo3g6-vFcKFT2JE7pwL=w^GD&!mqI5K zwynI&eCnn@R55M$OZU`bRSM3=oD{<+G9 z64DU<43=DEs5gf~J&UDmf2cFOP>Wg{>SLi$zmTOcUs_jsp|18qjg68VUNJnRE4U|&xNn<~rx>9ORlx~4Ym9TqohGlNZ{qA=(hpXTJ zpJVqItu%FL<1=W8ukfSgg`#+UoyfYMcZpFG=8N9xaqg(IUBmBcfIl6iVTPH8z|)E5 zQ#*x5|1Lu90)g~seIN-!_H^6{dgakKPH&^eJocDEyP?KCIi~gn4<`$;$*gFF(cV znPaLXr)VUr<9`}>&zzH2iBCT1jGHXpE*#Y#Vy#Axi<;JprqNlE#*2k9-ZFl=^X$K; z8d|vF3NCS?XQ;M|NAXleYD0Ecv^s^vy79dAP~v#1x*PHAD)GnjqP)^K z)%g3n{-oGdA~`xab|vM=gg-0cf^hHPP%JQ-!6N6@_Ahh&{m=Ij_+A3vOW=D6d@q6T zCGfojzL&uF68K&M-%H?o3H*Os0&QdkB=9454sKF6n`AaXewb|0F2@y5pD}ZG@r>!} zwtHXPF_UEVw>ntKXCGGk83prK=7(ZE4DV`IJ2v!@KonVylFK5+KP=|vf-8B->o zJ2p3#nl>two{V0xAp|sj!1&%JgVIJ#&YeE4Bqw7^-|QjNhvcLWgmX&ZIk~eYl?+cG zcvs7AqM!bNKiH$Y;=yAq68dro>=wcJG10EXAQS#*geh zr6hassL=y@=a+=SnN}ca958a$?8`D@`6GJ`nKWQPEWfbVka5GaGiDd`8ZvfPc1BTQ zuc6~E8xR{iGB!9hGb1*ZqSHT4zA|E2NIkV=Kz>%nfYedsBRy^8IR%B;L&wIl`(@`1 z%AOwU-!HpxM0Q>*d%%>E5!qQ3{uGrS3Csy+0^s8$S5lA zl|F7v|5$z=OihpF7xO&UKV#G^aeqNZ>KIAQKwmhqn1mCHQ7-Z@BYViWF|#C})5jL) zW?WP_a^~2)ESNpGD3-zf?16=`G~6jq{O6=6D;7h6Qb*;*ViylgPo0w8C%1T5R_g5R z^K$csXQmF%&W_~`%}gCIvLIGGc)&#&0}61LaS=g@KQA1fXZGTHWFbTk`}EH(9&q8D zk^zJ_d}?ay@cdY2_TbdiT-;@!mmeeCSS(%s(?3pnVzGR=kvek9RAJi4X&2A;f$P`ohflB4xN&kHaa_V_^c_Z8NISI2jmYdk?UR= zqe|p?uUK}B^dlYlPnhq@N8KmwBd1gRvkJ1(#-E!X8(PpOBW+lItROqBe=2_Rv-^xJ z$XCym`=R+tHWHMRo&ouN^Ydc8h$pLHMB3a@jy1w6wnkbdR=!ngjk2z=Mq6{OG1fe5tW{=>v*ugltp(PF)|J)-YoT?K zb(M9ob+vW66|+vZQmt-QFYCuvn$_J(w@$HoTbEjWtW&KF>kKQ?O0oJ{XIp1lXIVY1 zbFD0^hc%h6J@vEBu?AS#)@jyHtV^s#)V{`f?4=$NsG(EYvq%3**?0HkB7R^W=KjxwlgL@>0{iK|peC3pR%I(aO`Nh*u z){;$DKSN52Pb*8lvSjYutCGtKOXkli>;Y#N&zhHfiy4;oKXa~F`MFcdW@z_QXCzOXQd&A=dU9Dw@>M1C=O)i7DV|+cGIw6`+!-_H z&X`xI9iNqa*^~vdr!VYlrS|O8GcCiKJJq^y_~aouc{yWqCSN#g#E_8}>U6fIeX%Bw z$Qe5s|6|4u9GhcaO+Ig2Uf!6&qjPdbSfgi5E1Ob03uz55DJq&hZ+1zsHFe6o+0!P^ zE1OHE`}LbVeDbuCVy@;-D@!h(Pu@kw2!Hl;YXQDX<|;R|kW=Otl}(nc78_4yBCpA_ zi|5UlTc*=LZA!5OF>US)5z@S}>HYfYfRc-*%$+r3F2bBroIGdB!pZYyUp*t`bbmDX zDJm+N<`3@z7Fw>;Av^ys9-#laPG-9YlrLL$j%6+87mdSW75v`E?{D~hf!`W_<^FFk+uL*s zzk~Um!tZtbuHjdQW&Te+O{IFL#nNNFY49_!j(t;Osi~>GQqxk?Q+ucOp{dJE?b|EX zE45dzUTMA3d-d+sr&mU=%wB!dVri*qz0%Us($jjU^-0S}%S`K=9!pP6@0FgGo}S)2 zy-#{ZdS-gx-m%`Py?gae>z&@acke#EGkRzC?%OBUC$&$nK52c@`}FS9r%y(o%szcH zVl?%=GSV{AGkRzA$;ima%;=jL%S_Gem6?{Ap4mIIPi97DW@g{MB(X2i_a*4Qc8Yuc$CRN2lV{Hz)_=ez|Aa`MNdPtH+41fMww-4F*}RO&D7KRv%_O4-bkxpOAx z%`Tq5FvoMGpJxu6Gi4SXw<+}6a56Q2#8BIf?SC|SJ!38pM-I(Pjqyy~PtPBkGr<=a z!D{!R^tutM=b27&a=10r1x-qVyq4Oy17rzsz-ZmWO${l9F?biuhtI-N_&!`E&rjOZv6FodiLDYlnkfC zY`JFTc{*GVm%;_9dz#k6vfg`|s^MKQ4(nihWOG*^(g({j_B2WHUd(E*=**jN4Sb{T zo~8;osNbHZTKFK`4gUn!VlmdT&twaHoV}xUa2wnWzl4j1T2?`S~Nr>V>Nmi0DA z=xD#KZM+pF1wJe9hk>8LLipf>Jx$Bt{k*hg16(k1Pt!J73wOb*ClSsFDQ6YQLioMAfbz?(A1!fvn(X2I2P z9efg6yc%yCJO=K9Jz(ci$Q`D`32-bdhGlRmTn+DsPr@hRHuxIc1?yqw(U$c!Ooy4% z@eeogrpkq|18)gk1Jhvz{E)X=*1{O?GHrz2c`I#~G3XV{gq3gt+*FExxEHR0?RfQl z1-uE?!Pnq!_&MxKr)2_r6|&%#`N$J)hl^mZ1?UgFmM^Dlk^8U?-m(z?umN@*NBy}9 z|8VBD_=lU}B3Qkc@_^@GPrZOsm+WcU4sU|(##`2-usi*T=lQDfSh(pn>I2MOjlAH* zyNM4@xCi&_T)6DMJxx7e(K_72_UlOp9P}{t>muZ_0X-CMLjG_<1@#Kv^&)bIx4ud^ z7o&%;J3Qhw+`}R`9X<<}!bARm{=v83LSJD0ANMrvgr(ciqf4l7e<2;P%iDXJ^5De3 zqW7@&Z+n_n!0zwvY1#;5n2Ds^@y-l5`VV__+Y@58dX)HVnmccXNYSIUb+at%s+0FNZ7j5`A>H;i)t}MEy6@DzO|i4uSev2rxZZ&4;0Lf0{tMQ_vrpUG zl$=F)DeM)1ufal?divg`WpEAL04rcM`~!@`6VKS&)c#yLbua~vfVr?37Q*##8GIgY zfSc2szw`mjX25aCT*g!it z6($X)eS|UiAnZ!JQUkN#UN{lYEES@0e>5k3nyz`wz2_$`dX<5Q6j z?My$oiFRZFwXgs-!o{#l z8vbD>tb`L_JzNMGds=H?GOU2vuof1;Mz|PuNyk6Tgq3gttcMF>589hGFb`J1B3KJo zz@@z@A6N~$V;AFa2yEYHZ_{*`0++&ExE>b5t#BFK4%=t!ZE7d>5-!3XUJcj66>tlD z6xP8laAE=VQ=Y?J@*L)6BIhaCAD9AHz+AWy7Q#BX4DN;-VAsBs6I=!x;2zkSdfTZV z^%Zu5W8qr32yTOGVd*)0o1UcJE{5CSrgQf;?Si{u=jrr|`r{v#!m%)W0QDUvF=kl} z#}6jG@Qxwm2X2A8VfP&L3OjHE%z_)>L^$<4;)nOcEpQ90gMWj&VTYmkn@M@WEO`C} zlqdXpIQfM~jUd19R9Fosz&I>|?PsCyFa=(kzqe@u{G6BcEQIO2#%K-9g%z-LHu{5I zT6Y=l;gc{IehUlXQN`#1d=PGf)o>T=Q-WM(lW&*~--Z+6OQqxs{^|BjnB;BwOGP`4~gURqUm<>BG z#6KJa7sC~B9ju0xFb?bCsl0iuT`_hTc8Ax&A#f9%4(s4j*#2tj5ln|$;aIpGJ`CHJ zP>!$%yq1qCiBofz_}9egcz9>2F67B7qA=cX%rt0yo0xuofafi_pT@1a3Rct>tGSw z3Rghu2J8$>hHt=0umLWBN%OEnOZGNp!u3nhGuZ7$;)R3YdRPFr!fWAnxE8i6qx@la z_&FQ`lWrp2@Km@Io(I>%nQ$xI4tK&HHxuuC@DUxCqwC zbGRFBhFurn4ralDx1c9*FKOo6Iv@t_d@IrjKO@E4@+SwdOZh+OW8g{t@J%JNo`=9U} zrocO3F5CnQ;j?$5-!Kk0!Q|EGH(Ui9;1-y)hVi73-Bhm5Uzo1;G3`lUVazp zhSxymOsxi(3`gHhy@pT10(it4^aPf|_3$-V4ZntQc+Ag8_qCK4>;X5yd{_rd;cmDJ zj<|>R1>OZ~VV8T+6L=3yz7BUV8(zB>{e*krQh4iq#0yWnAN_=b;C46@wp+||*d0C& zhrl?T4$pr8|8Oo`54XXsu-iKP!@02C^~eu)hfl&G(E2(4VNbXe-VfKqYPc01{~-S1 zblC0&>>cb5yFP?}m<^}PeYh0X!S(WdJ^tl6+z!XVc1tX49_$WpheO~dI33o&rSPO* z;2)j`x59OBJA4VYTS|R}-C^uu{KI8%I(+7r*cI4r19k=Whi|}Da3`#T9dD$bKSH^| zBA5qP!6NtzxB?#aDE?s?d;@NTJK=WN@h0rauka7kVII5)7QuyZ1>6cZ!pH!#0wvUjj$4SS&qGcned3GNjLl{TnIZ?U|--OSP45kLp_E4 zp|yf?fXVPFm<_980sI6mhV7okuEJwrC7cQC;aX^|q#y7c_61s7urII&EQII7Wv~!# zfLFt6_#}+Odf5I}>LpBpU4Msv*b^4QNpKk~g&W`!SPk!jarh`~e;f7#rog|!T=*F* zgkzt_Kb#9Uz&l_y+zR7x8*IM{dA)#tm<)5_2v`Ugz-4g7i`Yll0Jp)UD(nm_fL(9L z9n6BO;Y9d2Tm-GQ88GW;9NhW~{H@bFiWJ4}Y_;Q6o;mce>h z2ix6=y?c#%3j4qzFc(gTH^8NEJzNjphFf9M>(q1D4YpfNI|jSM^Zr0Rfe*t{xCO3) zb#N2h32WeP*Z^m~iN5}n{sWA`2Vp*Z9hSl`;VRhWE&RjtU=1vVyWkzL^Ii0FYLFW& z_#^p-ufS!n!=ETGm<+37DU8EP*#2(x4W_^^VJ>X*XUYpMgDc<$xDg()jq-vMVIy1z zyR4ynU?!}G6X4fyA#DE_>No5UD_{Yvg>zveTnfAV4E=tabi@8|B777sf?MEPSO>Sj zBi^C^0#Agy;RM+A9_F=R7F+`-!V0(ueg@aVZ{Zf0UW#K!^N=cTI%WF@DG#WL^v8Qf>YsIxE5}KzkzkI4(^7#Vb}Yp zpYP%y_JSO<^)JL!f!VAr2Bp8>Pr)o>!*2p7REa4q~AZh`GT#6Ro z;Y9f6cKpNba4r1N$M}b%VI7?L5B$SLu>nR5qgAM;eZgA|E_=87%g}mWhxD~z)|A%o8V{in_hku7<&}yW=3{Qkl!du`rxDoDxFTu{gL@qELehtUM z%l|`p!)xIhxE5Bx-@sZp{#)b)i(uCc)=HA zEL;S4!nN@Dj`)X}untauyWw2e^>NY-v*0E;5xxc&!8lwCzkyre;k-1p4t9sT;Z)f5 z3F;Nhf;Yp7@EN!WZij1Oha>P0GhrP};e8j@la@6ZCd0d6HrxfL!<3|^rls&axE_|n zt*{=(;g_)eQ;esNY-;KO=fOO<0T#hsa0Sde3cZ8Hum-Mz4e)W8wAr%$31jdFKWu8s zhiAZ2I0LSNOW-E>b65l4fDQ0VnDjL5QD^+aq@$af#=@bn3|I%pM|?% zBkWv3yKziYQ#!m9PJnaZLU<2c3%9~8@NHNJ<8U|p7Iu9GyM8QkgWXO*Zg2ry3JbbY zU*WZ|8pfd<&pz=-$n9C`2~2@^z+AW+7Q$0cq`tz5a07fCR>K`I4!fL$|K}Kg!W4KV z%!TE!5WWJJ!DEu~54W7$)bs}I)~%^&CrpJMw_wj;4|prggZIHA_!e9Nzl9s&VL!${ zJO}QCGhxTykx$qIE`xdSaaaUDgsb4c;3oKE4%gPe5wHPHf=SOa?+9b?S(p#&U@7c# z3grgp!YAP>xDEaW?t+!D^9z>s1x$x+PNm%89k2|phpXX_PQyPu8*YOe;4b(y>|AMC zpTczb!xa3(Q(zgK3RlCnXHxF45Z1vf;coZ{?D`_(0GI_of)nAq57y1TYg*)M)X~g?`>@(~E zN5DKdJso+$zrl6z)!x_#cw!&)6pn-KU$Ly^Fa>Ugxv&lv!VdqBwD*CFs>=WW?+i0G z87dhWxn!e}k&&@RM#dT`85tQF87XUMWYm(8kxRxJ85wKjQjxL78nxuI#ugPB85LVp ztdX%TxnyKiWK*$4MlD z)}YDgel#2HM~hMCLfVab(Pp$3?Lj|AU30`eezX+*2CYRS(r7n& zA=-;BMZ@1^JfZREhIHDE?nMjG=F4a|x*u&sJ&WltbTT@CZbhTp4dZ<@3GGL-(21AR zZZrn1Ld(!*v=!|}51__BnSU9y8%;)&(dB41nvWKvooEdjc?IJforU(GH=?fh@QcQv zJJ1xg3C%%cme6iA8?8mxqpfHa>O*&8nKu0iN>Ql(R8!}%|{QSJJ6ZS>31{@jp}5aqlxH}tI0dM9W6nd&>D0f+Ja8b zrr*$=E6CditSe|N8o83ZqseG4T8x&WkE69{Bif4YMSZ9b_3UHbUqg9xGMb8}p}FW? zXes(UT8n;)wxU0vK6H8x$(S>L&T8y@$ zkD|TkD`@zB)@L*hoqRpx2F*nC&<$uAT8Gx5MK>^R&{nh;ec?Cs`^T))d92Uqd^7{y ziWZ_RXeF9>Bl8`-5$!~a(E;>%G^!haXd-$L%|uw`eYU z@^2}RZboa-YP1!72lb&pqn^(>&K6Q0osXuXSD?9QE?SDNLu=7(Xe-)@`q1NUqx=ER zW6@Z2Et-mMMRU;uXesJhM|pGrZ9`LTr@zrsH2e$JaWoEn4^2Z)En=Rd8E85B3R;i; z3vEY}e#bh2u0kVx>{HQr^l3C5Jz+h^2Q&k%KsTWcXfxV@9zgriad)sE_>z4rnt-lB zGteru0NsODphhv}(d*F;^kK9gZ9^l!VqHQL&>ztZ^w~QZZ)iJOiF!6LUeN@!6PO*&;o?iL^jYYpfQ_=8^?M0i>Nt-B- zrlLk4{f>IkdNdhrMYGY<@1;Cif!3g%Xbbu^+JhcOUEdkTX=UUc%|=tuHE0g{XS4); z5v@VLL0ixZ?xQ?<4eB~*7eu$Q!|3hogrJE^_zJ~hH4%G8KaiFni z{2%BybUB)f?m^4YZnO@KEoZ)<3(#IP2MzCM-lK77J(`Aogyx~&qGf3M{gg)^McdJ5 z&_1*sjrf7Jo*c?22Dd-(3NNpdKe8i97b*pc|+HsY3KuJ z9{OLj3|;;N?MBz2?dV_7KJ-mA!r?IXqw(nc?c@zDK=aX!XgOMs)}#B;c68BS7%%8H zG}7rX4xkCBtCqZ>1!y7qC|ZfWj5eYlqMhjf&;c~+ujDO^deKC51)7Q8ffk~FLMzcG zv=RLR?L^%>D36|mM!6iu`Dh|~HJXXuj25DIp_S-EXd_yScA`z_0QwUe9BE-{?2=Y%~SE0?k3MLrc)x&>Hk!v<3YW+Jm;B zt|J^q_*0Zer=Th58Z-xe04+gxqBUqg+Jc5ZO?k8kb$J}db~FaPxt@MQOVE6D3tEmo zh1R1#q3!6@XXtnIdNkrlhjBX^kA8_}pq^(L?`SMqfzCr4&}_5=U61yowxSj2k7xrL_X6e7jc7mm7c_Dl^`Z&r*hb2u$!Gz( z6|F#DLL1ONv;!UgBIQvp8abYEg(jlnhsQI~|Dc8FS-Ti_XcgLs?m|1!1Ly!cfJRMl z7^`1mokTaHS!h36gf4Gl+@bks6IzOPq5nY#(P=NU&PEU~nuKPeS?F765jucYq0?TW zJer1fp=;4Wv>S~%n*6=WxI^cmIp}j}Ir>ku9{mh$M;A6TZqQsbVxq%nL=(_4tty+L_&8k&bDqh;tLXdT*$ zwxJ)Pz32&VQhpNS3ynjcK-16{&^*-h7Uj_yXdRk~wxO%gUi1Yt{5XfviN>Kvwo)EF z5zRwqp=Id#XdQYv+J>${d(pem@F<7zXEYAoiKe0Z&^+{ke~@?d2ebjbU^n9xy%Oz5 zccGETGcMm|yrQenO!QN<2sPRmFX%F~3B4ZeLU*Hs=*jOe-X^n7qe=f^i&mk@=rd?G`X96y^`q73tbbA-y%+64 zUq)S19L6_j3~Ic`d_kw6Ip{*P1ic!qLARqV=mE3`9s4iJpGZHWG3aVE6@3cLMc+e9 z(J#^SqCRv#>Y3&+&iFUw(M!-&^bs@{{R%Bb{b((k)Jb`C z5$Z#;QO`*ZV-FgKcB5(NK{OAI{(y0hEAvFItZN6|F~KLfg>~&_47hG-A5L zNdJiS4&9DsqCcZW=n4B7_vjgD6FLv=LU*9XDICu}X568tp~>h)Xf~RQ7NhIXYP1Y( zMhDPtbZj@}V_C0JFItKwqy1<$I_49~qp@f;ItOh=)6i~oGisdb;JFFPqr1^$^kXy| z{SGZgCwxkIGzM)(&q2GIKDHltfm<22SA)Qe8* zVZNZ7&|LH&T82*ioc=<^FGshdYtTNl5{-ysUx3D=#sT^Zor310NoYCxSF|2|8*N9w zMElU6(TLM2$G^zH|GC$-Zn5DgjBxxS{0L7uNW34-f8E`K{^`Od<%kF&G5nXnf4+YV z`ty(C#rTK?9|@l; z^IL5$-gc}@PYYz?h(~_lknoG4e28|7|Bj$MTMNT=k~pEaV!Yt4urP-s;7G)o0sjU& z(Vn1<^R6H-fG_1Ubk_~z%Y(cE{+Nb0z7Hc63<$Sed8UydVA%qvX7VVRUuyFN_yWqGD)VfcXTa0p=gEAn%?se- zGn1<%F>R|an_y&$|Djt0V*N{IM^j|fse0s2a5v&cf@ky$`Y#y9lY+bmzV7ou|HZ@jydbZFZ$B{TPZ`F~4e}=V?k@)YFUa|KJ{9Ny zk)JO33g4jr-eG)0kPpHaYS!s!4!k;^Gz!mx=TU#=u=>q~G=}#IsJ{ljiPx^Lxe+on z&&50`GRKjy6L}c(<)D8Id2qcu!fwDGZ*A1qgWZ{OeK**=7d`-2t%>1Ba%_PAQZAos zmyd&w`D)OAmdx`^?&ZBScr<*s++P=#h27-}+a)?p)SHjZYhMrgZR&=%(o;YsvM0(Q&48}xr9>@xo!yLW`0s4r(M^W@;5{{XLp@i;#U##;j4 z2N(N7oBt-rYv41#AM|qxS>pXc-U1KrAM~#s#?1|dm-o7eb0z$0Ub|lVLh@m5nna8q z_M+RcQ60}>;p@LV@~h0i8F)v+pd6rQW$vD8^ie5((u1&b+V;>^5QdYhib@WarEX_FFM^wO|u!+=;p>@zDXlP~_#8k`HmtagLpnR{Zq; zchH|p8RLBGyu^7;upS>gX<*QQ`7mzoiA6mIJRe@ky5c(6W49@ldhW5;miY0U6aGBt zzj>G+bNv#2Qt>luaM1rf$5z+7Bm8_iCJ>JoyF&aNcJlop!p~4&i~O0VL4kg*!e&9( zA>}&O1mEz5dXLrxFH>+4Yd^7i@PCKM?G;k2BDXi2xfOQN_{nx1@?RnBl1JEGX!k+d z1mdN>bL2MVo8v*OF?sN8c&NS>ybPWXSIx1yQFz-Zymu5H&WXCN{vG8gMzrD+qG|Jcln zQ2VHZuY&(&q>n$^^|WL26*f5|ZLI4i#wxawM9GyGFHV*8h_8FvFZHQYFkHBZC6@D>eEhVO!(FV|ma_Meycvf*9uLax~b z*BA4gFxQEh=%W&B;v*0Fui|yk#%T@$!E4|*z-I_Pw70v~9wROA0{GwLbHcEiz@|-< z?}0m`4*4$;<%iZdXG*|<;4UV4Jp6k(#;~tMc!4n<19$yGeLXV;9s|!3{)ggo+G8*W zo(=zY*m`~-xZanD`cF9IUnI&0Gp$^M<@1yd294{n?^;`rnI zQ!vLKCfG)}s(dWG4X*0rRJbRGb&S13uzg_>flY&`KNs#i^^pHkUI)3;ylLR&ty1_b z_|bCy#6CPMkitM8)?wp4?NH#n%l2VTHsPZUz5yOJ(#KfaM;|t8Pgh?zj`#(86Ah1t zSMmLcsy%x8C_H}@UOo!1ABDG%!uu?K2l=vdXx^L<{S?9f25H~r!?<~qO&kMbDZf6B z?{hqYHDTkwZ6E1>2;1zs&s{d=t|P*JHqY~feJ1uR`DxLEF6>t+>_2b?#-Xq;#XctS zkpE#}zd&KX-n18UwGsQWB<=~a_PM4@_Ts$NX`Z(VyKd|X&tp%?*l>*-VP`INfoob~ zK~9^;Jw)N}hrM?Dgg=MvFCM$ueDADN*nKp@?mKh5iFz}!i%8{rOoiPWBkb-q{b6F1 zp1^Tx{~_h|ms8+{E_+^Ex;`?g+RG ztU>Yc9q_YdzS`#L@YmqWW$rXDqzV7|a3eqTvDgbLhtGtk$^9V4-nr(O5;hIkELn5N z|L#beSL}7J6B{SrqkE)m6J`VqL>vR~-nEDPe;CF$26+?}W!-kjzfA7yu*JdSO(MK< z9dY6-m?!7_Kq7?yOn3`?KI1;f!{!7Ayx@g!-|r6jZy)A=P0)WOJmrqi{sZ$Ko)4ev z!r9RAbm&@PoVh-Tdb+U_-}ftC2koT$pvF}C8-A?JrTwWFUIcq}|muE)AQNEL=>8hc3iib#J;Oi{me-+d;?q+XEuDDrhGAcwT4&2SHh2z+hJa| zHN3pn3||WWK=j+tJP`f%wmmj_u!-2pcbv;UoaO@)qKz&RBEIALWSL9%abw`i;eU|p z2-_H_K$K5`?|I~q|1`P$N_$Prf$!7s68HglsJ;^ZYv4W&Z-IXWSM9BP;0NJ!xt*g- zJ3bc;@7I)%fe(uEPN{rYRA8<2@>UA`@SoUQ@LKKz=YQ-vaqKw7J zz~*}TMEI%3PdvOz_{ksPXR|rZL_fD;*Y}r0{+V1Kb6p|XWm@;q#zh8^gbidet0+hmva5NcKyllgK$-T zvf+{Y_}vbU zKeK}ChY$YL$LeR!JX~CAgy#;6(>_MTii=PDK7eXpn+l%`XBo2lNjiq)!c*Y8WbS+> zn1@pMet441rF~W{eCsZCo?GGjD9?7^uHU@P8pw|i-l^dp4qkiVsx>JVegJ;D?BBfn z6Ie@9;r;N5E~$QT9Cq2~syW2)DZiocBz@`H^A9^tvFF%o_hkjXR&gJYQ&!t%u|4J* z;49%=;{zXnF1uj1Y`p1V&Rik-7AZR1j;IGZRh`Hv%Cf9M;e}-t+@>biI=*Ovc zdlDD~`Od>3;}P*@z;ocC+T(>6z&F98!uVq-UQx$b`+7hnHtXGduN@{q8|OWNU`4F; z#QFy9d7Ia+H{P;sXt#5h-Phgtc@saXdC4!$8ExaY|$1keDavXA@>3zjzs(m zUd(IPrBWOsw`=S;a`CeUyI$<<_IwsF@bXqEeC`p4{a?yl`1sKFQHM>K=dkiRb{jk% zt~!_Ng-?esmg{hu7g0o8!VWyeApT)+8{lbnvcykjgNA8 zIeeoWqwrzfQxG{9c{qs8>%4Y-{f3=SVfVb9uP%JucVy^zMSBO~#qdz)U|x76yaF!v zfv$(7dZfKza=dtEE%f*hb!3mii!Dx*>~;ipUyME8s)ldFzbdD~rinN|9g6e27Tb>$ zrw>2-HF0`4!0ULd#Z_^p!h1Dwid^Jj6F*knA7$`q8eRvFgeT%FIESR?b=%-^a1Jvz zUty1jUifVILYXsW#8Mj={~R!~HU8t^g&O~9@C_ROdGI=Ts2qy=%iz04mUo^XXn^2# z@E-WNa{SU7(*`$=3OyFRpdQ*U_^%MxJFnR}lI{~loXL8IO)NIS97~@qh=)&u3s*rd z?FZB0XKHvpJO$43ZI{2s?xS*ex`x-ov)~+p?eZ(_^6l_kc&KwXFGYOhNbt*e?Rw|$ zwh!y`AHpUaA6u|V6*hm9Y^3X;i7XnsuqnqTmAzw{-lL&YiDCi1Wn zzLVFkiOqIBL+89TcFz0o)vKv1Vm8=b#(gvY>DW3Lb%2j|k9?O)m_RKnxosvuZ*arjiZymYNGA6^4LR_4G%`G~gR z+WV3n%h2_Uapw3C?J5<%j^TU%c^&L`>3&izJQbckj9(ZSgra;aybyk#%%ye92d{*y zjwK!@S`}Q?FR}0k;4B?>eAYR!m$y>kweTxsE^=;$7#KTw*nEJED#kMS09+Mg9efb3 zim?rT5FV;OM2>plhvA{dmS}^^;w#1Lxx4IkO3zD*JS0V0<7SFvBaNGkB<4Fd3-A-{ z%ir0@paS?t_^@`8E3sV{aaO=L!By>OfNz1T+9C2FcwmpS{zW?mX^+!~O$#=G@xNTM zk=hZ>;_1U?Se(-SBMIIMSH+nHAAqak6nPLl5a<5Jka1RGGyPaA&Q~NGDb5z^S%{4) zjvn|Dc&Ip{1AT3BRr#2?>@O%kS?*8i^SCMSEKU76@LWaxUVIi2pWuOUxBmqx5B77q z)!1yuCeUB6NH$U)#BWpX*2E!xvvRK{4wuCP{jprCLmD@+*tjNHam zwU!mYGvL?D`E;5aU(p{G@NMu#!}tZkwXOl)rt#kaAAqaY%6@q4aq4v_GMV)Zt{V3V z@HDt;9m;^ugHMs;w~o=mXCCz@!^eu_!r|xb{xHu?Zdz=TK(* zkj{s@;HmJNWbQO?GztHM@JhI9+()0!u^N7&T;95-;N?A$2f+j5c9GN$X}!$E=2L6} z<94QGBaN35>KSu<=<^5>XAOJ+A7}A8xSm?a9+A%$_;||yN#?<4;hn!9%4aWjcWLSg zpT~E6!*7xOIBy7!yEu3&T(mbBht*aQXBvF$*Ah>z_c+QShS_k5>Nx;TDen>V_zh=$nJOCS297XUP zO&k@}A$TB;!_S6{qY;}2hT_;S*;sLiy!TMgPHZ-e%)50iAZ$D-?DeA6Hllyyuvvr6 zO4*Mz*3;k(qxi_jX5tBl{dWxWu{Joy%0(UUP}dbi4E69&HM||Zc#3-M>Vq%Q@CYv2 zFNJe!)Xul`oNhdPrKWs3d=)&@Tov)>!;9c6WdBa{1VQj}_$%;OnO_}9Phd@~hrg*Q z-wyYjsJ=$t2cHdB9UCIpw5@_qk?WVvuj1i3@EDm(d!lrBF8pGdJLd)aHy>UB=eB{J zU+X?vp#AX2;i@&e9xi^DTXoH-9X<$OEc=(P@%O=JdPiIXm98;HT*#b-EBhaw4OflN zba*>lm7jcg2VAu`D2E$U)%{ZskB4&zweu&P=d{Dq;i~%k;2H2^~!hR4Gj;i~>ihxfoUW&hT-Jkck))E_;K>&FiEKC}KdtPjIZvd@Pr@pJM? z>Uq=%Plt1gZTpa}qjbV=fQw^C@K|ddzeFAf;8k!{JEFKr&;geSvQ{WAd1hO6R_qT(F5s(%vU zOW~)=?USzEXIkY|lJx}F@dAX1rIbFXmO2K9gHc8kA=bH5FN)CK8T-6sP@TcH+$mN~( zo~Q;s{uFgzwZJE8cn^FM{6yKmbsi&f>Ec9F#YL{7t@7+H4cDJ*?KX;BS@#yauoUd7 zsfQ)baFt4S(q1&5x>~T|YwU*k2|K}D`-GngY}#Vg*OLSNk4@{Tp>ryHbi#e`TwVwJ zM8qpyV;{sOZpLB%eX@Gl^UJ}-vU2FE^nTG2KI7A@a^zW zbHq!TD)_7LE9CN`4c0Y9VbhFFA2zyn_F$86n!24X7Wv2Fs&>Y}t2I0Yz72kc9H-RI z9Qaf4dt~lhAIP(4LkaxYIP;qIu)dP6SJz^bkBzRKZP+wmqw3FI__Wj2_iVyBSk8v4 z)|5DS8vJ~@9oBOhVoavNZ-h^jxphBK@H}`nT(#zv!Ix`z9Xw0pzYV?!PBZN>Zw(ev ze=mGJJXHI{Ja@q>;NttLT<_M{J{WUhXWM&|1UA>t&Q!M}16~ggwf2fQ3gAte@)hu1 z8r}eJgipj*aJ)!)?tt%r&yhK85SL6u{r&J3xGKKL%Q^OI>Q8|0(eMm-8(bA%0sIxX zYQ9v!-_ewBfH%XBk=rLf{|A3gQ@$U*T~j_XgLMzCI$k7Le1crRxkV4GYZ>rOlvnWr z_&WFva(U@9GZk>rKdO1s0DnyrUx&qK$^NeoEH{Dp`r#d#_##^}k@iSAd-+HDgF#fIbr^wv8#vtZl8Sx#U{3Ivy@TSM?F)rqz$9zuIi=D_rM?Al= z&Ff)yPV1fCx|B`MNFP0JohnMy>`UHli-uE4bC~~bM~q5 zS#VXK<-+4Nyc9kYK124u!tS$Lc%p)PDbos{2ft1(FOHScp0O93jo2i~Hqt&V{7R04 za8(=P;MH(dzo)?;fKQeEOXrq(@CP;J%iwowcpbbDuF6pxe1n2}iK!P}2G`{%f)m$v zY*aalhxfu&IZB6r1y|)LAKnF5tfnie2TTo|L9;oow?1P3u12i>^*Ecb_Wyroo31mvy;9Tp@_O>o}->qRqz#{;AtA31z!kPog)^(FNdqHy`&Prnd~rQjW!p&Wim{CS2sWX{o9OQ->R1b3Ao}|j$%naq z&K5l`>Pp2=#hej)T(SOGpOq25a_}{P&9%ICT`2hycG5F075JKc?%|N%B_!J20AC89 z#Oq+YrM0*No(@;-1N-5{8XlR&{td1=_fCL63s;S+4EW;;?j_^``2Fx<=L1r%qOqyO zrXQP7V_)R5o;C;`$mJ1_*m;n0DeCIPZdQ`@JlNM;Z9D6EFj1H5*X*mYN#S)cpSRn` zo)~yBT(xGTz}LV-tr^09j#XaOUnTIh8vix$T=-dXeAfLx;lBmG9v*583*KYZuZrKb z+>AdI_fkFvo`ZkR==sdHQlkAjC952B5& z7M~^im-c)<_zcRc#;oURp8JKLBbS%1r^dn)H1VavMf|EUkZYAcQT8uggDJJRD*jsd z0!{p_@Y$O9eeiTm{GM#~Pn!5+;iCMBiuhA4u8KbwzLN5){FlNP!&UjOh3COTjnP2= z!mX}T#KvnQwrZhc~$+j@K@lf z{ItS5;i~-j;Je|f{CK#jE6S_lkF~fe{#5u^lvl-{3*QG<#a{~VhpXbRg&%;c;%~Le zPnG*e+I#!pKWhAYu3`OxPm#+@_dR3bqJC9;sc>EXbKxTYC&>P-Yl-4`R!n@Y@Ws4# zU07j{G4q+2zztL{tR6oHvCHRma2%R8fvfLgE_7fscW&tO1TVaox>7Cf`uIWHkF_rn zHc>h3{jgD8Zxb;mP|pL{yv%FYBa$C+Y1KBKB5o8^37{v!cOF&HH-;gJN?EuY)m3*Xa_7`EhvQI-P&BZ6jimuG3{*I?Osf`!UQ4}%*@~a-aF#s7^@QX{s;d{91K5bZaNQ%>h`8NmjsorH z!oYj!>hp>ucn4gyZf3#XgsYCJMer64uY$j(;Z5*n4ex@#0-r`Mg8e654;-}0vo*50 z^ek5NZ}<+a%QWqWyWpYP@5N;n{735N$Y}c~$IiR$V`DKk*WyE!qiT4LhBw1k!d2&0 z-SDU2s(q0`!FsrA-Sfh?Ysx3XYvHOKWK#cD_^qVCHNVWxgY=shCHVMS;X{nW8u)&= zDn~8wE=74S<$K_J;I+ICjswvT()p|BM$SJLtMd>GPlYcS;X~a16EUX3SHqR-AG`#9 z26HYLLs;Oe^#b=KO5qP?sGs+!g}(~_m0Z8IK8pHV;av*uh5O(?!C!Rn$I$tUnD0-T z*DQsP@YSqSOVoRaIC!y!r@`~#sy#rS#b;1gFa~MQSwf8KD9>$3zHfGuZ6iImRF98Z zY*gd59li~&I!^V$Ux0_|ColCz+{C>*_;b7t#wfPSf9V;x4)_ftIc*raZrTsehpX0vNKP)+z(e(uXkP-n z2p($A2igx`4NsKglb*{jfNxaPFOK15#8(OrTys6H)b587*IY#&wr5(`cPB`8)|e8$ zI>&3?KX}%B#3%4eJAZj$EEGGjz{979P1&-M&=#L=De{N5Ch_{-t~NrEn+w z7@14=v}@r%5}#^Jw!#g{tGEw7RuiA+7RG>v$HK?JC(7|j@u$KEH2srnl~?7z6g~<6 zDqahp4p;S0D?C{fzYjiF!#xGu--WB%7Ym;WKSpl9)IX{4X>e8kbK!9sUJ9QLSIwVV zc!Gwv!e_x%@%!L0aMk?rut5>!Rr!yF&(rW!_+q%Ke{$h@a8>(C;Wub_Ej$;lYF{gS zDO}Y*KKLaX|DLsmaWPz#pICS%Ty+m46}|{QULGG8*vHIVxENoPWiEY&uoN!(f0oRp z>-V+rRJiJx+6rF+SLN3S&w#7;CZ6A#Toqp|T(nQ+KNX&)@tKUMMN!cT&$`nME*7F@M{ z)WXI3rrPtg!bSc2|&(vd#Sx7(43&A2Ree`dl@gr6n*knUL)!V}k zN_aM0*N!G^DzH)QW4qv6G<*u4PrgXT#HF?leF2;N`sr_$v4lBd=vW>aDns?c*%XjM{9KLyM)(4a|4#T> z8vg_E3pM_uHgG6%3qJ{D?XXo8;u zSM^aB{9;86qK^mROEmtY?;KxXTzU>-!GSU zmImi(F?`Z>>iVnU9ynW8yMF77tVAC+!&hj^cf&8&lsE2XEro|VHVgk=_#<%D7)yr# z4X#=Tvf(elRrxK3{}mo;EsUZ5O5%SLUhQO_T**b8q4iQ6S0AwN)wSYh?hWeo&Id1n ztJ>+=$mgozs&>Z0?}n?|nF=r0_|Jvkq48e|zhC3O7G4FPL{5S^Fb~OrJwYq{0l4b= zgAZN|SIrX-mxkBFL*483Qe`Y$v>&nS7jsp5zBh$BeAqmWiR)inaFzPXSrHiWh8I?V zla;^G?-?txxeuGU*a!WHlRW+uhhEpjx|NRqpC~VWkKT34@L0o+F!yP(*cD>u!_Td} zc125eL*HF$odbE{b<{cQCiVHIuxZEU5H=aS4$d1f?xbsF{n*URSD%wc-b+#VY}to6 z;PSVadkOGm@EI~+Ve<_5D)@AnOZVpr;Cb*+`)@C0D&Xtj-^%4h8$Pk?XvAj0&7s>M ze00Kp4cA>i8N{X>8&#aqWt@k=RdFW4Yv8&#v$4rv6FN?j%VPLFa8<6W;oIP$_E4gH zGyGAw>iF6XzaOsJKN$CMz6n?5zzgq#t8$PGKMdF9AP1Y>x2SVa0{;ZA%0UhMzwl|a zH#nxH=U7_c|AVX6o}N+4yEZddHRWUA1MpBe7IQrb|6jqs(E|DJ$GhMh4SOtbpc8q>g-7159($$mG4T1=2Fo*UhOX<^!e_w09l_;eLK`+eD13+* zdf`z;>UAjm0j_DlRqIe3e1V3i!L#71>rQ#_wQ$wAE`#U6RXMJMFNKHdBQNc1gJ;53 z<5uLL51S@z&gOM6&(iOAMm)&xdBIiVBp&XAhdTCqahVQ32)|VJFV?~H?fF@N&6MAT zUPnZX74Svy39=7q|Iz@T3lDWX@KQ$PAsH^(=sI(i-A>{Vw^T(P1NgWcn~A&*=D~WN z!OL4w74$WnONBNcalJPQo5!)yUGL4tX8N7#>%GPBrSPx*-+F@Aduy=qZV27)qAe}( zDezEzB6trx7OrZm>mfe-1y{9I_=v@3EjFs_FsbnS;i1N+D4z>2*YHyKX1FTGT6i)1 zI(!BDON=XNAJB$P3pPozjr95UUU;`AhVU&s!vt5gF%JGeP5CtVfQIM6e}t>rSO)jO zb#1K2ChjhE8{6Tja8*C_!56?)ZH#!B>q(mO@$hU7PlqputJ;_kPlxN;SburhfEVyc~oC&`Tu3D1|;djBWl>Ix+AGQ_ktAy`@ ztMc0je-|DqKVHgr!v79mE&Df*UxBMsqK-jqj=EcY%`^HD?g7D7F(kof!Bz7h3%(q# zT8oO{SsGpiU#j6v@JtQwf-lkVL3oCSM^|wV2!4#5L+P2nB=`kz)fmjO%B#j$5j>aj zs(DyJe4F5NsNZ$VukCpv&BJDVRAUoroO&tO4c`K9lG`ATn@`zu+4U#R>o%(UDFz+~ zKTY!uRvq^=_6Or~Mt=yM=W$JRe_QVLNPpXkDBVyc|CHp3v7+ zM?T8>p(&pL-wRjGiwyW~_$;}8=~>1Cco+O^nM>y&74Yvhc_c2Ye!26<@zq z{+Dw7(r=hWR&!5|@}cIX$WH=15w5x>mjRyxSFMW$@Kx~h@D&`RjKQJnG!^iTa8>^_ zzzgBm%H^Hrm#YT)AN~sbSeZ-rRQur#@VPRV?yW>V#xp(eU&&m0J|O|#2Ui^zGT`n_ z>iieL2PmH)`ptbm78UR8esdd)Lm(eOlgEnJnuO!%8{RUa0@d*G`5Q6>Bv4R3@Gz}w|9ApNd%C%pFF5#Jw8 z4#m&#h#U>TJK*2S<>5oulY~ul4cFGo^!HMdv1!L{tv92|U z`C5X_cn)?1A40SB*#46P#OX%E!PT(v(kum%>$jnFD_S z9%?=FiuS`x;JUqPEjD|wQ5{oT;rrmK9Qfeh!d2%Rp6#sZo7HWIh0lYl+K>uQfa}_j zhs}CyRBb4OZ-T4ZPzQe$u4+RY+y__nLofUwT-Ao~zc44^x;DgPGxHDXHl)Mnz(ch` z>=ko~Asrt0O|;h++jC;XZ=wm?3T#u#E!(H{w$0ck-f!7HqP7+Dt)Cd<9#G%&imc_l z2A+s*aP5>nzn1`C2%jl)>D)2{o(Wg&`wQT!;U~%ErQ=rxd<{HQKZ*Do;5l&Bc}EBQ zX1FT8e)t_4|B-*y*Pj5_)t>>^)n5P?^{etz0e=X7y__HE+CT$*zb5_;cspE`pMLm9 za8-UHckp>^xGKH`xZ^=}|75_w*Th!<|6Ic>;GG)Y0N)K)jh_zq9=JGf431yvoUtG7 zgAdz(nqRkWp8q^)7)Q`P)v-GP9s{2y`OJ@XJRLq?u7eoFHdvI8s^d8&c&OvL;EC`uc&It*g=fOo z!*$2~B5a<)W(M}bep+GgW2@jzaMe6$f{%Gfz0dE0|EwuL2v4EBs{ZJ|aUTY*%0UwR zTDU3)S@0|2y5mVPHjioQsD?MfLmf{cSvGgp8@f6o1;HSvkdX7u@ z_rkw}tJaESxF4=sE3)B}wy1Ml4EMrk$@NR;%GK~$8r}>~fvd(yH~c>MOxeG5tTLYF z`A)bhzg~DfT(x&hhS$I+$^NbDq+Z_3hHr&8$z1GzrRz^6*vxuZeLhqJkB1K%hxiye zA8LW8!cUXykgiX4k%J8Qez@!5h4x$vaeXSHo;g*izCINXpA8SyUNK(M;c*(C51$4P zb?)b-d^y|;*Il1#z^3w#>KHoUo8fWz3$82D^{0OLRt=AQhU;H&)%u z)y7yt%A@`&c%VN{y2y@kM1Od(t;BZGR_k2$7|B*Tmu;cGi?9h*pXiew_=OtodX{@) z8Xf~rfuDh|U_PbiNmAgKE6RH*l12TC;eqq<%TsM1QX5L~af!l*XhSXhVhwMFFVt`! zJQc2LgXcNcYDIa`1~288zyob~@WPO7NW;fv3Ll~kdGJd$ybLb-TGfwr@HDup4Q=pc zn)1EytKbufCD@PBd3bn(KF4uzG0w)z<)zPfr@_zG)Sm}GOT)|HXKHvId=^|4e;Ygo zt~#Fg!e_!~$?;3an(*hjE~cqJ4n7yoEVT3IOb%>VME|D2bKy}km+qV9!L#Al%G|nc z>E*pL_!IE!WNzNG3Vb`XsG}YmA2unn4aWkpeHFYN9{z~>c-05@Q(l$Bh@G4x!%vs} zOZSH3;iCLmGMDaqro$ttU*$g^J_i0vxx94kxg35Re3Hzi`;PVSad1`r?eJ;vVf#+$ zzG5HzD!6KnMZCZ|pyBcGD>OVEez}I{!!OhDa`>ehUJt(nzCzBA^_^v2-fM?%hU>;q zKQ`}Sqw1r`M(*FhRr4qTz6Y+FM;Y*Lc&I&>h_L|v4LqOM!F9+S=fUqVt;A+dl{&^o zcpRLgiS0wW2h|DBfUEj>0KQnmqh4e$qv470MetB<6!S0>o&{IUqeA%ga8(~v!k58S z{oDwzf?q1PU+gQT=ghjWc}r8rApAwRIHm>rK{_T!@8aA;!;>toI#14mzptsk2rlAN z)n5gF6RzsRCU^^6mE$h>d+<>EV$nx~@Q*b9qhB(N4>ddq{sCOohgtBq;i1~+rT!xL zZuk|v4(4CT?8*o()tKl!eRmY2Fcq@FV?B97=F#c}1bDMe$ z881^1u8PkK|6UVcGW>H5&xZe76Mr$>2Ui{Ms^R=QZj$%kh(eohr_HcriRw zK17`D@Oro^&OZ1Xit-{(k%u4QfqcF-N14yWX7L<=I?haZHe3~FA-o)}in9{_1YC7p zrxE@QTs3Yx;oa~kgMUL~LfVTDz&qhL$=qq4)OvX@>hFAq!DIS;R1!8fV52%8$bwhE zRWTI7%iy8T2Si_1!T$+YwXq5QI()^jHkcQRMfonc>v47cgYZE`{a(sPzs56!l-KPg zld&nmW{Mnx^sG%bd;?sSqhffSCdO)b4LnqiM2yYwFX6fvd$5W7v-%k5dY!!{JXD^= z^%{`}QT|q5yIwup9#11aqbPEoj<2=Ys;;-@!;9djU>h8l()HGI_+4<-@wy&d$!iCQbQt`0wDV`139Qs`$&T@+w{rzX|`U{Xjds z7#`|)8biu`v|sd36|Y@l^%+_l#PMQtVA~M52Nd-N*Tu0@tv`wIgc@}}WWuMzRdc@( zJ_UZQJPxF50hRD;;JR_$gw1wrRDIb6-wIcag+ch!a8+MNzsYmta8(hwv<%Ms6tIji% z;XB}=awuZRhS!b~LkTwh*r?7^YT$lF9U{*y@IxBj1OHjWU9EhsL&Ia>18`M*De$oE zp>rVO%Yi#IyacXmUkzN>z83hPCcYl{5t{m4|6q@);W2Ps`%>V#_T|7u`&8q+1pb|d z*T6s1@D}*{8r}nMg{#&H*KY2!!hc0y1=mCAdB+&|K6t1xF8VhG-l?fS2fiB~>VAxu z@+I)SaB+MM)-TrM>+JQo7Moc5NUY&O8}nyQ4e?$xZH$9A^4fJ?ylo@=?6Cdx;im~Z z)tVIXHs8YlPsUHM9(=69g=lL${6BD2F4Ey&Yj{5VOSoz*l*2#N@Orq2PgTAh{;G!e z!JmPjBDYW4e?+u#P6bbuxpZAE9^S3-pALTyez{!UX`b+l{N=;P{3Y}?5y8tXev({X zdN!yYK9=%xWiCCt+YXO~tBzTHa4$U6zF*WI@eVPiVnUW8kXs z+zdaA|C@&SH_u~5eBJOxls{eO()}Exo$JhS)gI9c&xfnl%VhWlxT=4%;l-Nr#qj&# zs{B{O?}A?_$Ip6vo3$P{!*^)v?}pcE>Noz$HE_5pKVJBo@Y!sd1qpBH{FToqq3 zyc({GFB|?8Toqq2yald`uNrxM@@sUDxkzxb>Se4^Zb>z=q*v>)z* z-zxJFdy^DwuD~WvwvnDU$$_uX@DliqaMe0o1Ahp9o$TLf9<)UtwZOaJsyWyL->0eH z)xqbX;Hn(Nz&qi(9He41qfWg}<-(`IR}X80`5=~PLn%BTu39H+;k)3WVie_D;dPq& zeefE%>Ri?HKKnAbY8=JF55rYCPK6I@>d%Gu!&U2aDSZCl)az_5JOi#e4zHGBJj^=TdiHckDN@ZB2T2yfN!PWYQ}Rr~`MSH&N-k8^6utKv_DKdY%f z6JD?3h47~|yb@jqUnb8z=in zeaL;$`osRUGIyFAKT&@o{A#!=Kbi2$G`tW#AAZ>||EazP5p`R8u%|||I%|@negY~lVmPE$5jY_60XW$C43uv zf?Qtu4x~o-eVX!}@H^nD^LWhGqbZ*Vze!VnCVVAab-gN|`d7gl zso(YA6YO}`2m|yV?p@Hfsc7s@1qo(<=9M->yhpw*21&k%VaLC2d(gj;i~cO zgWnBbF|53OuhjD~>!rqjEZp&&I=)o6pYltG`L~}#$%UUo`5T6D^TLwo$5QxRa8*Cm z!q;l*Z-o~q%6swegRg=Q+lP#}RukUMnvIQW&546Q3s5z*WcGJorWMB)L3miTJpSFtxq7R!#6+T1^5ucf3DAZaecsyK;VbvH+ zhd01gV;~;L3Ic5cX9$b}!2Kd=Ldc|AMRX(*S=%!#m(FXm~$-2RucN zk9A_`GZc|u@JuWGGMP)iL7V{h!&Uv80Y3y^G_1V2`4;1&0Dju@Bj%9P+}sLY0bdGN zj(_;2@Fdy4w2pSb^Wdud^~0AdxEG(1KCT16b$f+GY_=+Vh&E)x%QUle?#HlOPQ#zILC%Bm&=P|sq|ZhN!T33Mzv04 z!NXoq&yym!1FmXg75sbrtL9k~{5wS(L?3j)Khu;Sgnt5`D!0+P_u=Ke=&!j(1iwP& zA_vy<`NAd{n@HNAv&q3`fyPF}T8hnYv6&{fbnciH4`aH^5cLtvvW|;i~>EgI}-lUkATV@YCh|NcYO);EUmB$lScu9=MmE24ATup9jy^lrMve{#ET;>)_io<=fy-!B3Rq zlb)sMg=hVD(0`Q+gQ4@cuymI=>IC|~kIxQLKGZS8OOZJE18~(EB5cyJ**l6&0XCCg zRQnOJRARFnn^0{L^)y<15wC-75$l=xaoj+Ay09t5hibeH!q>s4%RZ#<#)$ro{?PCw z_(r&DK4rmc;i~ym1mCXVRqz`4jdFZWb3lq5HNn4wtJaGy`1czBgI0M}exeVuo>N}M zli=6>Z_t0d9G|s65O4YYj)>dcEe>O=!~Lnl_{hP);yv-7XPJAYafdtb_ffxhoVVLy zeBm(v93O?wazEfO>W1F&^3ELh^A6)(>mA`@!dbj~{qv4$r|}P`Su|V}UEsdNSZn?* z%ub%o>qYKuPGhON-(i$E#LIGr`}a=c<-qF~tin?-;~3uRa955ozIR;q?HFT&%e`}q z@w#j6o-xKVW3mFV&b$O~Sv$kt8fSF6-S37QcaL%V!;Qu<7k&|LJm%?iK0VHO{HPTV zk25|SXT~sLA^M}k{kqfWvT8c|QHT3Jr}3=AJ>W2&wcb3gc#Qj}vBtlSaQ7W)ynAHy z-D8c9k1}0E%=0<8I_Zr2OPJ9dX1+Ie)#>jzj4!Qxg#R$+tubBWi~+0C@DIaYb?-XT z*n6aD9X=TLy6a<)@w3PL`>5aTc75SC?iyp^dmOc{ciqOLF4MTi0e6QO$ zXz`;N#rB2%~hoyFJ2q zV}ko7XvEclUO9EA^Xa3wt})L2-w308y!+D#lDW3?Ick?HPR zj4~_TM_={b$v>T9{BOGZ%~Ommv8Kgw_d6I_#_M5jzsvY6Eb|c2Ht8n!HiyXn`wrt_hr7vPyx>SO zvv3;KUG82=P$B;ol}26wTjlAN9=mu+~nv8d)i~% zmoZyJe0~DnkayY7-?#GO$Vf@V|fz%&W>Tol;{}M<$M~ah3$XxdY#&XdOOvicdi;NZK>k+2%pMf!N z)pNgN?8th=JpI4?>;A22{luMS-&9+F5BaFvE;jv)tzU%&bc3ba=9+&8-sFGvmff=! z@3yq|4b!jlO;h(@X6ivp$1FDgPJY$&RC>Gl_mJOK%Wujx93%O+#?r}uHSK0vnr&%; zr8_KL^`vR{BgYBxFX>fN(=83W$^W{odJ0#V^=!9v@pjYxu-Fzz{{}4kh-*#zxt11g zG3`n%ZDjcr|6G4Gb+)C2mZoks-;b&?bvZ{=@vr6o$>hImDbErI*-4n@%OO#;>-9u`jY!!!c(fR zqZ9ObapGB?G%1L0BKA9$Nn!nqN|#eV8%h7S^_Glo=ZB=XlvIEH@Bg2P-u_4Q-H!AA z_Zt5<->5_W|2G={C*N)P|5HByC%)vjN&R2?H5K~=`iX8qf01AN|HZ$vu8HrTERSt) zYxlEBHG*_&X=~ds*;zZt%C|lNWHE{cbPd1+uWe zKMnr=I`}&;`1`Nm@6)422^-1XGYI^Bra6+Tf1JhNc+|F*mSPaqWry&&x`Z-ANc1k^P;01X|&eJzkR~jVtG%? zKlSRaKRriArLjGYlx$<=Itv)z<>r9?Fuxm9z|)0emOpO&$L`O+w8h)iQ}opn{GHiY zU;K-|t=xJ1y}qG7|AW8N$mhSE4JCilMAAB>ElHC}Q%N&Pvq-Z^b4Ztwt|iSS-9?&58XHFbq=}?;NL!L7 zlcthpl4g-+lje{vBV9|HOS+3Rk2H28`I9D+)*)?4noOEXnn{{PDwY4<|BEdv5+~N7 zO`E2P)q3?Gn=xi=V%@s6l4{kttZtLB(cg8eL*l*>|3@MBXOY-Xf5lU`r`~}=?vojX z^+_TIrCeH&y&qpsi6zA91lP0vd!DBTydFHf{_W&=A$M0O`9+n}`{xh0=I{8qF`KVM z(EaZ=_n+6d>uTdu-9HM&%Km5mWBe)ikAnR7mt#KPsX@N}GPzK!WGo?BfiQo*gr5`e zKj9?Y9%5%3)Wp9bF*@Okh(0bdH= z7w}aPFBz*Gw6_o8)dK!4yk@|Up`V9-F?as>ql9aaK5j<>UI`vst`=KI=CS(jPu~x$ zeN@1Pu_xix9ux0@y}x7T?Mvqf+yoy5FS%Zz2K+_%@OK2Rga7KdKku3a(7&o7e(9vQz%wC)_1Dv5&0-7sDCc$ z>|?P~v0~S{29xio66u^53$K@IMZElft}pE?Z5^-V;xMk3bXxGi6OOxHKKA@Q0!4|# za_svJa3{Dse0ltF3_kM>@vg$L^NLG9^*#w)1aA-T-%R$#dpI85x5v(sh1g%eE=Ztk!+tci3IVw>KHaO{vij{Ssv?xuJ@&vFYM zrm1h}|7nhUKljy9d0X+}d+_Joy;Fa5g#QGe*+TY1;Pu?SXRnhxL-w`dUEnKb#q;@& z`}Sk&zP4WRR>b*ozGU}FcW^p-xJ%n(fAAvdo6cN#yW{coiyzYzx&~r z|0JEdkyq~bFI^>iotiI+H-Ha=PrF{++UF$rF}Lr;AAPak2evUgj7 zGx&L~!Fc^4Eyb;j;~cktV;UvPvA@Aj*xcb{qoa~L5xmYGx2MM+)*foZCkO3t0{onb z(n*TG<@Wc&*9PtC2G{Vse(Y1(cg21te90DZYY&Ivx9kyjr(txoas!svS%v=%;JNU^ zO%#Vp@Fs2n)t_IfJyotM@L`U7o%VI5Z*k7S{^-Lp9HFnpzQw-QEwXQj&be;E*6Wbk&a{k(P$G{tZBzx-zUU1yUdF8rDCU%XI*l*Y; zKPY>PTUqxHpYNJoiccl%+dJ-c?0(<3@O12xzm>g{jgH6QnN!4Fw;vs!z{l4RuLvKP zSa2NPj(9?>;dB{}#r}Zf-p}?G6ld#KPj!N>i~h%PspHT!i>9}}jcsUn9-%j??POM&PI_~}X>~_V${J#c1<#*}afzE7r z=Yz8E1^)uR{96ofD8LFIJ=J7y z@kxQNsx5B$X2CnrUs=0-3|_Bk;Yio~-w9tyyK>!Kbe!ipC?B_-H%Z@g?uYL?PdbgU zKMbG2e89$$CRL@g__}yMuZP?HdFyX)fp5jWI{Gicd!M3wli|nVPm-^d>*h<8?|~u8 z*O^7fB6#lisxOQ43CFFBm_|ug*MayrG<{M!R^C_O<%)?{;!9f`_vf$epRhPIas??! zKRMzFvCUH?&=;L_$9=wK@Zb8$x8M~%Qoa_q(_91d_Aj5I_}IAC0KWGe*;{+-46k2O z_Wj8BR`|kovcCfUCH&awvbT1Ub*b`Q^@rkV<6sVa$sp;tVJkX{)RcWL^OiF3%iv3M zWN-C2)Nvor1*>IfahQ#L!WFW&_Wy5qc>U!c%q=MXR>)3w+U$#iR5xlT-*yp=%s(>3VqobzdUO($q+3$qcauw%! z9p+<}ZyJ2SS+Y-*yRmF|v10Q7-_EtDad=Bl(byLw%-*K<= zd2b0+gx>(KnTcqDv^08L+ zWq)Jec%3)lM_-PQ!^Q3d@#S6hh`6=?8SrXc$c{w+Y4{T6GZ(`*I$qI5!uCrPhi}Dx zHT$~S!GDHlGY_%x?rawjAI~v=%J4B&L#&_U-p|C}RNhJGEP!YJCI3spzk#2^^{@4- z!(4;+I?Xvx%V7VAeDU{xUkPsTVs>x5IDXdT=aa zWt%DA6O6ys{@cRWJScx`JiQHmY?!!f_R+Bde(PU~gXuJLf%0*FeU0p`UWUSpa9vpf zotNO7e~`{l_)hq$9nvxVGR>vG?iBGZ*r&tCEfjanH9F?PSDY&DnoV@P1&;;SkL6oP z|MAjl4{lhAj;r8}lEg2BzY9;wkiH9hbez>vI*a{{19w<^ZU-MiKW{oq9WSWfk6`~B z{qSo}%O7Q1N&m5W(zo_D5x(nVapzuid;<^H@&1IbI$idzTBD<1Yw2%3Pu%M3UieY> zM1(&EM*nxuB*XJ=7Pt8S4!;dQFT%c28|mjV|2KXcdWAnc3!uwbS91y z-;e&;u4Acil-@}*gf*0rdYY==Hd?)>n<@FQ1SP%JQ_1m|- z@?FgJW+po8;Jcra{Sq~=SceX>pI%Pf^p`m9`*Yi;`v*GPu&=_pgxR<1D4p;+s}KB* zjneOl&fAXLcR^9I1^a_;_q;#)ME`eN{o!M|j6@htG06 zA$ESOTyQ^o9`-*F|0L|+ho?7Ez%0(CJ1gJ)FREN!v2P7uP*?V@*+j<-rylZg>*;T7R2xrSv;}tNOBjU?RM_dydW@&7=RjcgDegnj~HW{-NUqmG?8p{q?x* z!!C#YVRWu@4-Wd{Qg}kQf_2hQ!ZRHA@u|r;YdRlA_6f03MmAtCwfsn}hbb6`svHe+B&f4W7t+#_I8^9@0NbJ1l|yX!v^e2M>Y22rqW7 z3TpBH6~6H^ahvaTavg||!?+RR715ak-+!+HVD0%Acn0gb=Fhdg3a+n*9ryZS{k{ZW z+fVtL{vLS8d&F&iRkC-%`n4SQ`s*6%{4I(9x4?@ruW-}X=y(yni*e*x_}lQB^glM9 zp6fbHum3dj1gn?Y@NO~rc?~*uz{C4Z55q_Qqy z$FCOf1lFr7qCWtB>NE-1ykaSQ|5|bDulB(!aK2bOsdv5djWN!aMgI%fcW~?%P}Asq%jTCf~zL+%InaS4@@tujk5tZ|I)O^+)yS|L&ce@QYaY=nDVB zai4E^|L8~TJ93}M;?`||?8mK^?#|RIGdl#9rx=t+5M!G zfX+tj@21}#4Ii|rP%P2Sdt<@8xBp=2U--E6-PfbzA;*3E{qK_c!{WIH`x&%n>;Lz| zD|4M*Szn8daRa^gzbE?^tUYgn7h~RHyyH;W?+?c7mGF|ZPwSVi9VYvg@5z6Q+l`KU z|F^L(#r+u_4`E;5JtFT9>t{ZOZ>Ha~`l@)NU*~auB`eNFqHnqP96IjHb%^~uW_8!F~?BJbqfgu?_w-nHjaEHSo`_habMmK zCdl66@GE=?^Aco)77`!0o4-n#H& zlb^2m(XkzVnDNwgo6*ti7TGuAy59X69pA%u1?OF#39{c)SNciVKkB%T!+z(NKN>5E zJ%|0{+~2V>egmIDKhO{R`nO8IUjyk|{Vs&(9#GtpvCoBfLf_hX(rwb2%)G_MwZ8E2 zKTF5*9q+jLhIEvCcoN?7cIhuYM}A(wmwLh94X$T1;Db_RZ~gFtj{CUPd`AK;(AkFl z+3YuP!*p~cPE@|(b$VO)JobCE=R6&8hwSrc57s{C!;^Q(pR(v5hL+El`wA{Y=a}R3T_lPyUSl3BJ4rg11p85w;q_^U zR(DT3?&G<_>G{L*eFOWf1*b&Z>|=Ln+!?~Yk*w%b_xm%B`+h7j=>I#zw{RX=`y2-! zlOz2G=$vr;KY77?a{grLhu4EG;o)6r0=RJI@0ehSpP-Gz5ks*RwO%P{~o;e z-QpL)e~NV6eefFMEceHg<_}5OzYNc$o!fcz5xi_LpJ{%NboQ5(|8|}(fah_a zHx2!D@EJ|x>#O)w>FoGk+|Hxrj{ACljCo@-bdEdj=M~xiDiV=8@Uz@J#>b}u_Yum& z+rm@1PPh7<3E%mK@@;|rA^4^&@!If;)1<$Td7zbd5InrE@eDkX>kEtL&+sD51Bapi z!gT2mqo1_#sMZYe-Sit3u^$UxRVhAhU&05yAZ~5-!uzBX?#Ji{pUr++E7u+Hr}jz5 z`1|llKZ#qps@*UB@V-@h$9+9!FrHSz|C_Pj$NEoe_~j2s=gm@uBVy;>)9~oy)=cUD@}c}OKSw!Ukp11*FYKxKn9XK*YZkz` z%!{?2CH+dx;_YX^`*WTS#;H|sdvD4O@E;xb^}gz1flBb=v+>HIZbWnRJMO>*4p{BlHomLuPr;Tz^F-wWW`@Wdk0x9goR;o^8TmH9zKMLQvTHN~8?dZ&@EdPzS$dS&NBC@w~O@+VC zI@o3CfA6@D^PRUz=RNpA?AMTQS@_)fvbXm&m4>f^U&wp2wo5Mdz2m-I7cw7C#=hDD z@mIOedl9@VyfWj9#d8{b{Q#BgLhQH0n{pj}IlSt>LUT5*VWU=%Yye)m}pKpZQ zd(Bo;?=Qeld?EYQ#G(FE(y9D`45z?n!-qAN{Sf#k@Fna&w0skmNM|+kOzYozz^|Dh zoqN!k1iva%ydnHWcrW%Zb%US%wDjL)ze+{;0C;LoU?6_|aHChyoJ{*F{RQPhv7yIrz z@Z#6Xf9od?z{BT~{(#S=o!I%g zIPT-w-_4u+F-A!&4f{geSF(7%0uP@X`X0WK^(8wm3O%oUf1@9;a&?C1@|>peryaK# z;!2dP!@f4_v1Z@+1?kUVzq);QqvO6@1L`Xd$4cI{AvU`?N>;@_(w9V z1)mDH_a$2YztC|%pS1V-@P&UKadaW z!>hj_9c#}|!H-nHpHuaPVs3up>v31`9CUlfeY;)k%Iy!9Dc$cN$o@K>JGS!9hW~V? z{IPuZzHG=uc5qRP}=~%yX)hgxt4C7H*w2HqbKeupR7;hT!d>Qi3-i~|!pBN_n z8{kjEyB}3MuLge&-siaN8^F(W1GLvE$2x<>c@%sC>oXOxe;2+jn7@^LT{;b!zu9@z z9KMV71S{8Ic=>naXEyrTj{Ewmd4_nsV*21X_Rn9Y`f7oFnv0kBe=hO2_WuHW+A}I| z5A4r*L-xh?Nxw4uGWbC5^PUHv4lgoW_7}l-!B5c6FNL4ImN+z2zScgkfZKZ~Ek4)4 zD^-tg=h^TnmrKX;T@HVOam3p5ZurNn<5@k{T_=C;=eehfF&&xk6U5E>1phpzk7qXf zMy&n+3ICP#9J6n_UiO*m|H{U-5zq)FMCqg0FK0h7O!JI@bP2!83WD_dRru!6&jlZ1%0(!0r8< z#(iX)CyazQEH3>!(3uZ^u88<>c#C(XlT@j2L`-L|agOV{6852W*YYx!^E%V_u-_HkL9zlHC4Q1+LhQ~yKhWV7IE=iRgL%|&IOjQyAJ zgkas~tX%0NFt4~2`;PEN!E*u2;S+hzcPRE}ek7ertRtDu)$nlr;R*O<)f68q@7M4X zoOjl5T((8}ZMUghR^Dlj`+EO$mK-^bKl8B<`}19qJx2MG>K{x0=e^P|$!CKd_xeXC z33O5Q$8Lu=b^iLJEBtwMZV1}vpYUlHsJyOOM#nu{mG7ph60mZ8?6}_#X74vX7yEyx$6{~rEnj^ePCa^3r>baI00%{Smn zzL8;9biRc5tuGxrUoQJhI&HcBHGd|+Zw&4e?S^O1l1^3h3vW|-+j4zp<7rdJeR*TF zPmAYFc-m3v3`S=yJiHH5WxI5`v#+}g_76L5K7*pn=Z{+!_P>sZ*WVAH`mG$V%$GWTseE@cZ#2zE z;Ae9kVSG3I%x%&!e(p}`yxddyE*-@}vZs9ZK)xBgc3xa$=e&c}X)|4Xfao^wc?}e{9F8lxV8}L&a%l}^JwEIr_cXRz~QUKJXMg zd{5f@j{Ek!k^!(A`la?sf5$_GBJvpgCitdjrGFuOIlLz0nzi$tj{AAKvKjj=>GHEFpPjS6;P!dtN%%a+ zeZH-j4>U*TEqM5Twy)sJxUbO~`_4Z|XB+cg^Zzc#&&lVv{c{EOEqS41$s+os{Q>Dr zLf>?zz_;yCc^e_*f3wy5zvoSH^ZzjX4d(fFzO?;c!Ovc!a@qM3a~+`9AILge680_O;dOB` zyd-hH4Es#@yR5f#gs*{r9NY�q+>BH+IjHKNDEjN<-&%$4j1aPRxGS0UWyv`@_Nf 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 z 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

FJ@qVpm`1g@sTX6MO} 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.vWJ2`ga;^YbJagR`uu2rOd3k8ZEoZ zt!)Lg1w=4Iu1?q&9^y+y=#%3v~6e>K{a{MW>An`9^+ZHP4&~ zc+K&acxzIp&qn9qtvPTL{geYg9)8^@m!uTG=fh$PLGHJzu+g{J*24rGuO5SpiN0i{ zLx)m^d{dy;*Mh->BILV&(FeWovRe!JZk~*FX~qcC5lLx$kUi6kR!JC>R7EGu0?$|UyxjvVED~)=&m#^ilrs2zVsD||g_!hW@wKTg1wV_`NhUp7 zq!hx^?4*?-5)620ZK?#3d5?^$GEe}jAT4$|_8??1q1t2<_8K5DHm0aS2($6&S)NC* zO12mKnm1~qLnYfh1@P>U-b@^hAorFvxV|9KjOnN6Ujwx?da+wC{AGlHS|GBC^xsUm zuiFqa#CxkCB_xZlYAO2ApI?8lMlpI3DU( z4_`n0B1OpZ>1X}Rv(DD=^jMZ9{vVz#6C69-!9Gu>ullI zt<#zQayc4wwjgit@dSUy*)tm4&erIxcQWsTnm!xiYW4}Y84P+#HsT$T_LHyLrW86!wetdb^*`k6ysEgFOouAL+GM~b~AJfa-c}0G);}`UMJJ7lQ z=U4IJ)A(oY_qDwp@A{MR`)9Jxd-&|zpJ{tL{#thak7u6`v-W?i?d|w#c7ERe>-hVB zee}`Hp8tJqZ^wOY|C4&mpTCdqe;WU+{l9B_JAU}{(vaSf`p(+lk*_;r9I1)5|KJzp z4|e>YU)B@#koW&L@%=~c`Tv1B6hC(SYi(f9Z}+j|AL88qb?qvZh+UHr|r|I=r3vK{R_yM%qV z-0_J2={*8RNdey3l*fX1|Tto?i6mFIr%+j4xO154|le(b*X zUVnq{{QZCMJ!$uYzm#KKnA|+Hx9j|R_WYgqwcUGiG&v!yg(p1OM#3?Y(Gv*|W=; i^8eXi$iRK`d+F8fzIHB&$6EV;c_?=}$QooHJO2l^2~WiU delta 158895 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 diff --git a/co2.png b/co2.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a48aaf3c48d221fe2ed8819183783a141a1057 GIT binary patch literal 5408 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 KMNUMnLSTZ5L3sH9 literal 0 HcmV?d00001 diff --git a/diff b/diff new file mode 100644 index 0000000..68dbe45 --- /dev/null +++ b/diff @@ -0,0 +1,14 @@ +1114d1113 +< void SetFPSDisplay(bool display); +1176,1177c1175 +< int nFrameCount = 0; +< bool showFPS = true; +--- +> int nFrameCount = 0; +3203,3204d3200 +< void PixelGameEngine::SetFPSDisplay(bool display) +< { showFPS=display; } +3543c3539 +< std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + ((showFPS)?" - FPS: " + std::to_string(nFrameCount):""); +--- +> std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount); diff --git a/main.cpp b/main.cpp index 05849a6..85d6ca7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,46 +1,103 @@ +//-------------------------------------------------------------------------------------------------- +// Collect the Balls Game +//-------------------------------------------------------------------------------------------------- +// Purpose of the game is to figure out how to program. +// The goal of the game, is to simply to collect balls until a score or time limit has been reached. +//-------------------------------------------------------------------------------------------------- +// created by Rune; with massive help from sigonasr2 on Javidx9's One Lone Coder Discord server +//-------------------------------------------------------------------------------------------------- #define OLC_PGE_APPLICATION -#include "pixelGameEngine.h" +#include +#include "pixelGameEngine.h" // use this for drawing stuff to screen -using namespace std; +using namespace olc; -class Example : public olc::PixelGameEngine +//-------------------------------------------------------------------------------------------------- +// class +//-------------------------------------------------------------------------------------------------- +class BallGame : public olc::PixelGameEngine { public: - Example() + BallGame() { - sAppName = "Example"; + sAppName = "Rectangle Collision"; } public: + struct Rectangle{ + vd2d pos; + vd2d size; + }; + + vd2d pos = {0,0}; + + Rectangle r1 = {{64,64},{16,16}}; + Rectangle r3 = {{128,64},{16,16}}; + Rectangle r2 = {pos,{8,8}}; + +//-------------------------------------------------------------------------------------------------- +// Create stuff for game +//-------------------------------------------------------------------------------------------------- bool OnUserCreate() override { - SetPixelMode(olc::Pixel::ALPHA); - ConsoleCaptureStdOut(true); - // Called once at the start, so create things here - for (int x = 0; x < ScreenWidth(); x++) - for (int y = 0; y < ScreenHeight(); y++) - Draw(x, y, olc::Pixel(rand() % 255, rand() % 255, rand()% 255)); - for (int x=0;x<50;x++) { - for (int y=0;y<50;y++) { - Draw(x, y, olc::Pixel(255, 0, 0, 128)); - } - } return true; } +//-------------------------------------------------------------------------------------------------- +// main game function +//-------------------------------------------------------------------------------------------------- bool OnUserUpdate(float fElapsedTime) override { - // called once per frame + + Clear(VERY_DARK_CYAN); + + if (GetKey(A).bHeld) { + r2.pos.x-=20*fElapsedTime; + } + if (GetKey(D).bHeld) { + r2.pos.x+=20*fElapsedTime; + } + if (GetKey(W).bHeld) { + r2.pos.y-=20*fElapsedTime; + } + if (GetKey(S).bHeld) { + r2.pos.y+=20*fElapsedTime; + } + + if (Collision(r1,r2)) { + FillRect(r1.pos,r1.size,YELLOW); + } else { + FillRect(r1.pos,r1.size,RED); + } + if (Collision(r3,r2)) { + FillRect(r3.pos,r1.size,YELLOW); + } else { + FillRect(r3.pos,r1.size,GREEN); + } + FillRect(r2.pos,r2.size,BLUE); + return true; } + + bool Collision(Rectangle r1,Rectangle r2) { + return abs(r1.pos.x-r2.pos.x) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#pragma endregion + +#define PGE_VER 219 + +// O------------------------------------------------------------------------------O +// | COMPILER CONFIGURATION ODDITIES | +// O------------------------------------------------------------------------------O +#pragma region compiler_config +#define USE_EXPERIMENTAL_FS +#if defined(_WIN32) + #if _MSC_VER >= 1920 && _MSVC_LANG >= 201703L + #undef USE_EXPERIMENTAL_FS + #endif +#endif +#if defined(__linux__) || defined(__MINGW32__) || defined(__EMSCRIPTEN__) || defined(__FreeBSD__) || defined(__APPLE__) + #if __cplusplus >= 201703L + #undef USE_EXPERIMENTAL_FS + #endif +#endif + +#if !defined(OLC_KEYBOARD_UK) + #define OLC_KEYBOARD_UK +#endif + + +#if defined(USE_EXPERIMENTAL_FS) || defined(FORCE_EXPERIMENTAL_FS) + // C++14 + #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING + #include + namespace _gfs = std::experimental::filesystem::v1; +#else + // C++17 + #include + namespace _gfs = std::filesystem; +#endif + +#if defined(UNICODE) || defined(_UNICODE) + #define olcT(s) L##s +#else + #define olcT(s) s +#endif + +#define UNUSED(x) (void)(x) + +// O------------------------------------------------------------------------------O +// | PLATFORM SELECTION CODE, Thanks slavka! | +// O------------------------------------------------------------------------------O + +// Platform +#if !defined(OLC_PLATFORM_WINAPI) && !defined(OLC_PLATFORM_X11) && !defined(OLC_PLATFORM_GLUT) && !defined(OLC_PLATFORM_EMSCRIPTEN) + #if !defined(OLC_PLATFORM_CUSTOM_EX) + #if defined(_WIN32) + #define OLC_PLATFORM_WINAPI + #endif + #if defined(__linux__) || defined(__FreeBSD__) + #define OLC_PLATFORM_X11 + #endif + #if defined(__APPLE__) + #define GL_SILENCE_DEPRECATION + #define OLC_PLATFORM_GLUT + #endif + #if defined(__EMSCRIPTEN__) + #define OLC_PLATFORM_EMSCRIPTEN + #endif + #endif +#endif + +// Start Situation +#if defined(OLC_PLATFORM_GLUT) || defined(OLC_PLATFORM_EMSCRIPTEN) + #define PGE_USE_CUSTOM_START +#endif + +// Renderer +#if !defined(OLC_GFX_OPENGL10) && !defined(OLC_GFX_OPENGL33) && !defined(OLC_GFX_DIRECTX10) + #if !defined(OLC_GFX_CUSTOM_EX) + #if defined(OLC_PLATFORM_EMSCRIPTEN) + #define OLC_GFX_OPENGL33 + #else + #define OLC_GFX_OPENGL10 + #endif + #endif +#endif + +// Image loader +#if !defined(OLC_IMAGE_STB) && !defined(OLC_IMAGE_GDI) && !defined(OLC_IMAGE_LIBPNG) + #if !defined(OLC_IMAGE_CUSTOM_EX) + #if defined(_WIN32) + #define OLC_IMAGE_GDI + #endif + #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) + #define OLC_IMAGE_LIBPNG + #endif + #endif +#endif + + +// O------------------------------------------------------------------------------O +// | PLATFORM-SPECIFIC DEPENDENCIES | +// O------------------------------------------------------------------------------O +#if !defined(OLC_PGE_HEADLESS) +#if defined(OLC_PLATFORM_WINAPI) + #define _WINSOCKAPI_ // Thanks Cornchipss + #if !defined(VC_EXTRALEAN) + #define VC_EXTRALEAN + #endif + #if !defined(NOMINMAX) + #define NOMINMAX + #endif + + // In Code::Blocks + #if !defined(_WIN32_WINNT) + #ifdef HAVE_MSMF + #define _WIN32_WINNT 0x0600 // Windows Vista + #else + #define _WIN32_WINNT 0x0500 // Windows 2000 + #endif + #endif + + #include + #undef _WINSOCKAPI_ +#endif + +#if defined(OLC_PLATFORM_X11) + namespace X11 + { + #include + #include + } +#endif + +#if defined(OLC_PLATFORM_GLUT) + #if defined(__linux__) + #include + #include + #endif + #if defined(__APPLE__) + #include + #include + #include + #endif +#endif +#endif +#pragma endregion + +// O------------------------------------------------------------------------------O +// | olcPixelGameEngine INTERFACE DECLARATION | +// O------------------------------------------------------------------------------O +#pragma region pge_declaration +namespace olc +{ + class PixelGameEngine; + class Sprite; + + // Pixel Game Engine Advanced Configuration + constexpr uint8_t nMouseButtons = 5; + constexpr uint8_t nDefaultAlpha = 0xFF; + constexpr uint32_t nDefaultPixel = (nDefaultAlpha << 24); + constexpr uint8_t nTabSizeInSpaces = 4; + enum rcode { FAIL = 0, OK = 1, NO_FILE = -1 }; + + // O------------------------------------------------------------------------------O + // | olc::Pixel - Represents a 32-Bit RGBA colour | + // O------------------------------------------------------------------------------O + struct Pixel + { + union + { + uint32_t n = nDefaultPixel; + struct { uint8_t r; uint8_t g; uint8_t b; uint8_t a; }; + }; + + enum Mode { NORMAL, MASK, ALPHA, CUSTOM }; + + Pixel(); + Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = nDefaultAlpha); + Pixel(uint32_t p); + Pixel& operator = (const Pixel& v) = default; + bool operator ==(const Pixel& p) const; + bool operator !=(const Pixel& p) const; + Pixel operator * (const float i) const; + Pixel operator / (const float i) const; + Pixel& operator *=(const float i); + Pixel& operator /=(const float i); + Pixel operator + (const Pixel& p) const; + Pixel operator - (const Pixel& p) const; + Pixel& operator +=(const Pixel& p); + Pixel& operator -=(const Pixel& p); + Pixel inv() const; + }; + + Pixel PixelF(float red, float green, float blue, float alpha = 1.0f); + Pixel PixelLerp(const olc::Pixel& p1, const olc::Pixel& p2, float t); + + + // O------------------------------------------------------------------------------O + // | USEFUL CONSTANTS | + // O------------------------------------------------------------------------------O + static const Pixel + GREY(192, 192, 192), DARK_GREY(128, 128, 128), VERY_DARK_GREY(64, 64, 64), + RED(255, 0, 0), DARK_RED(128, 0, 0), VERY_DARK_RED(64, 0, 0), + YELLOW(255, 255, 0), DARK_YELLOW(128, 128, 0), VERY_DARK_YELLOW(64, 64, 0), + GREEN(0, 255, 0), DARK_GREEN(0, 128, 0), VERY_DARK_GREEN(0, 64, 0), + CYAN(0, 255, 255), DARK_CYAN(0, 128, 128), VERY_DARK_CYAN(0, 64, 64), + BLUE(0, 0, 255), DARK_BLUE(0, 0, 128), VERY_DARK_BLUE(0, 0, 64), + MAGENTA(255, 0, 255), DARK_MAGENTA(128, 0, 128), VERY_DARK_MAGENTA(64, 0, 64), + WHITE(255, 255, 255), BLACK(0, 0, 0), BLANK(0, 0, 0, 0); + + // Thanks to scripticuk and others for updating the key maps + // NOTE: The GLUT platform will need updating, open to contributions ;) + enum Key + { + NONE, + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, + F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + UP, DOWN, LEFT, RIGHT, + SPACE, TAB, SHIFT, CTRL, INS, DEL, HOME, END, PGUP, PGDN, + BACK, ESCAPE, RETURN, ENTER, PAUSE, SCROLL, + NP0, NP1, NP2, NP3, NP4, NP5, NP6, NP7, NP8, NP9, + NP_MUL, NP_DIV, NP_ADD, NP_SUB, NP_DECIMAL, PERIOD, + EQUALS, COMMA, MINUS, + OEM_1, OEM_2, OEM_3, OEM_4, OEM_5, OEM_6, OEM_7, OEM_8, + CAPS_LOCK, ENUM_END + }; + + namespace Mouse + { + static constexpr int32_t LEFT = 0; + static constexpr int32_t RIGHT = 1; + static constexpr int32_t MIDDLE = 2; + }; + + // O------------------------------------------------------------------------------O + // | olc::HWButton - Represents the state of a hardware button (mouse/key/joy) | + // O------------------------------------------------------------------------------O + struct HWButton + { + bool bPressed = false; // Set once during the frame the event occurs + bool bReleased = false; // Set once during the frame the event occurs + bool bHeld = false; // Set true for all frames between pressed and released events + }; + + + + + // O------------------------------------------------------------------------------O + // | olc::vX2d - A generic 2D vector type | + // O------------------------------------------------------------------------------O +#if !defined(OLC_IGNORE_VEC2D) + template + struct v2d_generic + { + T x = 0; + T y = 0; + v2d_generic() : x(0), y(0) {} + v2d_generic(T _x, T _y) : x(_x), y(_y) {} + v2d_generic(const v2d_generic& v) : x(v.x), y(v.y) {} + v2d_generic& operator=(const v2d_generic& v) = default; + T mag() const { return T(std::sqrt(x * x + y * y)); } + T mag2() const { return x * x + y * y; } + v2d_generic norm() const { T r = 1 / mag(); return v2d_generic(x * r, y * r); } + v2d_generic perp() const { return v2d_generic(-y, x); } + v2d_generic floor() const { return v2d_generic(std::floor(x), std::floor(y)); } + v2d_generic ceil() const { return v2d_generic(std::ceil(x), std::ceil(y)); } + v2d_generic max(const v2d_generic& v) const { return v2d_generic(std::max(x, v.x), std::max(y, v.y)); } + v2d_generic min(const v2d_generic& v) const { return v2d_generic(std::min(x, v.x), std::min(y, v.y)); } + v2d_generic cart() { return { std::cos(y) * x, std::sin(y) * x }; } + v2d_generic polar() { return { mag(), std::atan2(y, x) }; } + T dot(const v2d_generic& rhs) const { return this->x * rhs.x + this->y * rhs.y; } + T cross(const v2d_generic& rhs) const { return this->x * rhs.y - this->y * rhs.x; } + v2d_generic operator + (const v2d_generic& rhs) const { return v2d_generic(this->x + rhs.x, this->y + rhs.y); } + v2d_generic operator - (const v2d_generic& rhs) const { return v2d_generic(this->x - rhs.x, this->y - rhs.y); } + v2d_generic operator * (const T& rhs) const { return v2d_generic(this->x * rhs, this->y * rhs); } + v2d_generic operator * (const v2d_generic& rhs) const { return v2d_generic(this->x * rhs.x, this->y * rhs.y); } + v2d_generic operator / (const T& rhs) const { return v2d_generic(this->x / rhs, this->y / rhs); } + v2d_generic operator / (const v2d_generic& rhs) const { return v2d_generic(this->x / rhs.x, this->y / rhs.y); } + v2d_generic& operator += (const v2d_generic& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; } + v2d_generic& operator -= (const v2d_generic& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; } + v2d_generic& operator *= (const T& rhs) { this->x *= rhs; this->y *= rhs; return *this; } + v2d_generic& operator /= (const T& rhs) { this->x /= rhs; this->y /= rhs; return *this; } + v2d_generic& operator *= (const v2d_generic& rhs) { this->x *= rhs.x; this->y *= rhs.y; return *this; } + v2d_generic& operator /= (const v2d_generic& rhs) { this->x /= rhs.x; this->y /= rhs.y; return *this; } + v2d_generic operator + () const { return { +x, +y }; } + v2d_generic operator - () const { return { -x, -y }; } + bool operator == (const v2d_generic& rhs) const { return (this->x == rhs.x && this->y == rhs.y); } + bool operator != (const v2d_generic& rhs) const { return (this->x != rhs.x || this->y != rhs.y); } + const std::string str() const { return std::string("(") + std::to_string(this->x) + "," + std::to_string(this->y) + ")"; } + friend std::ostream& operator << (std::ostream& os, const v2d_generic& rhs) { os << rhs.str(); return os; } + operator v2d_generic() const { return { static_cast(this->x), static_cast(this->y) }; } + operator v2d_generic() const { return { static_cast(this->x), static_cast(this->y) }; } + operator v2d_generic() const { return { static_cast(this->x), static_cast(this->y) }; } + }; + + // Note: joshinils has some good suggestions here, but they are complicated to implement at this moment, + // however they will appear in a future version of PGE + template inline v2d_generic operator * (const float& lhs, const v2d_generic& rhs) + { return v2d_generic((T)(lhs * (float)rhs.x), (T)(lhs * (float)rhs.y)); } + template inline v2d_generic operator * (const double& lhs, const v2d_generic& rhs) + { return v2d_generic((T)(lhs * (double)rhs.x), (T)(lhs * (double)rhs.y)); } + template inline v2d_generic operator * (const int& lhs, const v2d_generic& rhs) + { return v2d_generic((T)(lhs * (int)rhs.x), (T)(lhs * (int)rhs.y)); } + template inline v2d_generic operator / (const float& lhs, const v2d_generic& rhs) + { return v2d_generic((T)(lhs / (float)rhs.x), (T)(lhs / (float)rhs.y)); } + template inline v2d_generic operator / (const double& lhs, const v2d_generic& rhs) + { return v2d_generic((T)(lhs / (double)rhs.x), (T)(lhs / (double)rhs.y)); } + template inline v2d_generic operator / (const int& lhs, const v2d_generic& rhs) + { return v2d_generic((T)(lhs / (int)rhs.x), (T)(lhs / (int)rhs.y)); } + + // To stop dandistine crying... + template inline bool operator < (const v2d_generic& lhs, const v2d_generic& rhs) + { return lhs.y < rhs.y || (lhs.y == rhs.y && lhs.x < rhs.x); } + template inline bool operator > (const v2d_generic& lhs, const v2d_generic& rhs) + { return lhs.y > rhs.y || (lhs.y == rhs.y && lhs.x > rhs.x); } + + typedef v2d_generic vi2d; + typedef v2d_generic vu2d; + typedef v2d_generic vf2d; + typedef v2d_generic vd2d; +#endif + + + + + + + // O------------------------------------------------------------------------------O + // | olc::ResourcePack - A virtual scrambled filesystem to pack your assets into | + // O------------------------------------------------------------------------------O + struct ResourceBuffer : public std::streambuf + { + ResourceBuffer(std::ifstream& ifs, uint32_t offset, uint32_t size); + std::vector vMemory; + }; + + class ResourcePack : public std::streambuf + { + public: + ResourcePack(); + ~ResourcePack(); + bool AddFile(const std::string& sFile); + bool LoadPack(const std::string& sFile, const std::string& sKey); + bool SavePack(const std::string& sFile, const std::string& sKey); + ResourceBuffer GetFileBuffer(const std::string& sFile); + bool Loaded(); + private: + struct sResourceFile { uint32_t nSize; uint32_t nOffset; }; + std::map mapFiles; + std::ifstream baseFile; + std::vector scramble(const std::vector& data, const std::string& key); + std::string makeposix(const std::string& path); + }; + + + class ImageLoader + { + public: + ImageLoader() = default; + virtual ~ImageLoader() = default; + virtual olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) = 0; + virtual olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) = 0; + }; + + + // O------------------------------------------------------------------------------O + // | olc::Sprite - An image represented by a 2D array of olc::Pixel | + // O------------------------------------------------------------------------------O + class Sprite + { + public: + Sprite(); + Sprite(const std::string& sImageFile, olc::ResourcePack* pack = nullptr); + Sprite(int32_t w, int32_t h); + Sprite(const olc::Sprite&) = delete; + ~Sprite(); + + public: + olc::rcode LoadFromFile(const std::string& sImageFile, olc::ResourcePack* pack = nullptr); + + public: + int32_t width = 0; + int32_t height = 0; + enum Mode { NORMAL, PERIODIC, CLAMP }; + enum Flip { NONE = 0, HORIZ = 1, VERT = 2 }; + + public: + void SetSampleMode(olc::Sprite::Mode mode = olc::Sprite::Mode::NORMAL); + Pixel GetPixel(int32_t x, int32_t y) const; + bool SetPixel(int32_t x, int32_t y, Pixel p); + Pixel GetPixel(const olc::vi2d& a) const; + bool SetPixel(const olc::vi2d& a, Pixel p); + Pixel Sample(float x, float y) const; + Pixel SampleBL(float u, float v) const; + Pixel* GetData(); + olc::Sprite* Duplicate(); + olc::Sprite* Duplicate(const olc::vi2d& vPos, const olc::vi2d& vSize); + std::vector pColData; + Mode modeSample = Mode::NORMAL; + + static std::unique_ptr loader; + }; + + // O------------------------------------------------------------------------------O + // | olc::Decal - A GPU resident storage of an olc::Sprite | + // O------------------------------------------------------------------------------O + class Decal + { + public: + Decal(olc::Sprite* spr, bool filter = false, bool clamp = true); + Decal(const uint32_t nExistingTextureResource, olc::Sprite* spr); + virtual ~Decal(); + void Update(); + void UpdateSprite(); + + public: // But dont touch + int32_t id = -1; + olc::Sprite* sprite = nullptr; + olc::vf2d vUVScale = { 1.0f, 1.0f }; + }; + + enum class DecalMode + { + NORMAL, + ADDITIVE, + MULTIPLICATIVE, + STENCIL, + ILLUMINATE, + WIREFRAME, + MODEL3D, + }; + + enum class DecalStructure + { + LINE, + FAN, + STRIP, + LIST + }; + + // O------------------------------------------------------------------------------O + // | olc::Renderable - Convenience class to keep a sprite and decal together | + // O------------------------------------------------------------------------------O + class Renderable + { + public: + Renderable() = default; + Renderable(Renderable&& r) : pSprite(std::move(r.pSprite)), pDecal(std::move(r.pDecal)) {} + Renderable(const Renderable&) = delete; + olc::rcode Load(const std::string& sFile, ResourcePack* pack = nullptr, bool filter = false, bool clamp = true); + void Create(uint32_t width, uint32_t height, bool filter = false, bool clamp = true); + olc::Decal* Decal() const; + olc::Sprite* Sprite() const; + + private: + std::unique_ptr pSprite = nullptr; + std::unique_ptr pDecal = nullptr; + }; + + + // O------------------------------------------------------------------------------O + // | Auxilliary components internal to engine | + // O------------------------------------------------------------------------------O + + struct DecalInstance + { + olc::Decal* decal = nullptr; + std::vector pos; + std::vector uv; + std::vector w; + std::vector tint; + olc::DecalMode mode = olc::DecalMode::NORMAL; + olc::DecalStructure structure = olc::DecalStructure::FAN; + uint32_t points = 0; + }; + + struct LayerDesc + { + olc::vf2d vOffset = { 0, 0 }; + olc::vf2d vScale = { 1, 1 }; + bool bShow = false; + bool bUpdate = false; + olc::Renderable pDrawTarget; + uint32_t nResID = 0; + std::vector vecDecalInstance; + olc::Pixel tint = olc::WHITE; + std::function funcHook = nullptr; + }; + + class Renderer + { + public: + virtual ~Renderer() = default; + virtual void PrepareDevice() = 0; + virtual olc::rcode CreateDevice(std::vector params, bool bFullScreen, bool bVSYNC) = 0; + virtual olc::rcode DestroyDevice() = 0; + virtual void DisplayFrame() = 0; + virtual void PrepareDrawing() = 0; + virtual void SetDecalMode(const olc::DecalMode& mode) = 0; + virtual void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) = 0; + virtual void DrawDecal(const olc::DecalInstance& decal) = 0; + virtual uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered = false, const bool clamp = true) = 0; + virtual void UpdateTexture(uint32_t id, olc::Sprite* spr) = 0; + virtual void ReadTexture(uint32_t id, olc::Sprite* spr) = 0; + virtual uint32_t DeleteTexture(const uint32_t id) = 0; + virtual void ApplyTexture(uint32_t id) = 0; + virtual void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) = 0; + virtual void ClearBuffer(olc::Pixel p, bool bDepth) = 0; + static olc::PixelGameEngine* ptrPGE; + }; + + class Platform + { + public: + virtual ~Platform() = default; + virtual olc::rcode ApplicationStartUp() = 0; + virtual olc::rcode ApplicationCleanUp() = 0; + virtual olc::rcode ThreadStartUp() = 0; + virtual olc::rcode ThreadCleanUp() = 0; + virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) = 0; + virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) = 0; + virtual olc::rcode SetWindowTitle(const std::string& s) = 0; + virtual olc::rcode StartSystemEventLoop() = 0; + virtual olc::rcode HandleSystemEvent() = 0; + static olc::PixelGameEngine* ptrPGE; + }; + + class PGEX; + + // The Static Twins (plus one) + static std::unique_ptr renderer; + static std::unique_ptr platform; + static std::map mapKeys; + + // O------------------------------------------------------------------------------O + // | olc::PixelGameEngine - The main BASE class for your application | + // O------------------------------------------------------------------------------O + class PixelGameEngine + { + public: + PixelGameEngine(); + virtual ~PixelGameEngine(); + public: + olc::rcode Construct(int32_t screen_w, int32_t screen_h, int32_t pixel_w, int32_t pixel_h, + bool full_screen = false, bool vsync = false, bool cohesion = false); + olc::rcode Start(); + + public: // User Override Interfaces + // Called once on application startup, use to load your resources + virtual bool OnUserCreate(); + // Called every frame, and provides you with a time per frame value + virtual bool OnUserUpdate(float fElapsedTime); + // Called once on application termination, so you can be one clean coder + virtual bool OnUserDestroy(); + + // Called when a text entry is confirmed with "enter" key + virtual void OnTextEntryComplete(const std::string& sText); + // Called when a console command is executed + virtual bool OnConsoleCommand(const std::string& sCommand); + + public: // Hardware Interfaces + // Returns true if window is currently in focus + bool IsFocused() const; + // Get the state of a specific keyboard button + HWButton GetKey(Key k) const; + // Get the state of a specific mouse button + HWButton GetMouse(uint32_t b) const; + // Get Mouse X coordinate in "pixel" space + int32_t GetMouseX() const; + // Get Mouse Y coordinate in "pixel" space + int32_t GetMouseY() const; + // Get Mouse Wheel Delta + int32_t GetMouseWheel() const; + // Get the mouse in window space + const olc::vi2d& GetWindowMouse() const; + // Gets the mouse as a vector to keep Tarriest happy + const olc::vi2d& GetMousePos() const; + + static const std::map& GetKeyMap() { return mapKeys; } + + public: // Utility + // Returns the width of the screen in "pixels" + int32_t ScreenWidth() const; + // Returns the height of the screen in "pixels" + int32_t ScreenHeight() const; + // Returns the width of the currently selected drawing target in "pixels" + int32_t GetDrawTargetWidth() const; + // Returns the height of the currently selected drawing target in "pixels" + int32_t GetDrawTargetHeight() const; + // Returns the currently active draw target + olc::Sprite* GetDrawTarget() const; + // Resize the primary screen sprite + void SetScreenSize(int w, int h); + // Specify which Sprite should be the target of drawing functions, use nullptr + // to specify the primary screen + void SetDrawTarget(Sprite* target); + // Gets the current Frames Per Second + uint32_t GetFPS() const; + // Gets last update of elapsed time + float GetElapsedTime() const; + // Gets Actual Window size + const olc::vi2d& GetWindowSize() const; + // Gets pixel scale + const olc::vi2d& GetPixelSize() const; + // Gets actual pixel scale + const olc::vi2d& GetScreenPixelSize() const; + + public: // CONFIGURATION ROUTINES + // Layer targeting functions + void SetDrawTarget(uint8_t layer, bool bDirty = true); + void EnableLayer(uint8_t layer, bool b); + void SetLayerOffset(uint8_t layer, const olc::vf2d& offset); + void SetLayerOffset(uint8_t layer, float x, float y); + void SetLayerScale(uint8_t layer, const olc::vf2d& scale); + void SetLayerScale(uint8_t layer, float x, float y); + void SetLayerTint(uint8_t layer, const olc::Pixel& tint); + void SetLayerCustomRenderFunction(uint8_t layer, std::function f); + + std::vector& GetLayers(); + uint32_t CreateLayer(); + + // Change the pixel mode for different optimisations + // olc::Pixel::NORMAL = No transparency + // olc::Pixel::MASK = Transparent if alpha is < 255 + // olc::Pixel::ALPHA = Full transparency + void SetPixelMode(Pixel::Mode m); + Pixel::Mode GetPixelMode(); + // Use a custom blend function + void SetPixelMode(std::function pixelMode); + // Change the blend factor from between 0.0f to 1.0f; + void SetPixelBlend(float fBlend); + + + + public: // DRAWING ROUTINES + // Draws a single Pixel + virtual bool Draw(int32_t x, int32_t y, Pixel p = olc::WHITE); + bool Draw(const olc::vi2d& pos, Pixel p = olc::WHITE); + // Draws a line from (x1,y1) to (x2,y2) + void DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); + void DrawLine(const olc::vi2d& pos1, const olc::vi2d& pos2, Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF); + // Draws a circle located at (x,y) with radius + void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF); + void DrawCircle(const olc::vi2d& pos, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF); + // Fills a circle located at (x,y) with radius + void FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE); + void FillCircle(const olc::vi2d& pos, int32_t radius, Pixel p = olc::WHITE); + // Draws a rectangle at (x,y) to (x+w,y+h) + void DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); + void DrawRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p = olc::WHITE); + // Fills a rectangle at (x,y) to (x+w,y+h) + void FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p = olc::WHITE); + void FillRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p = olc::WHITE); + // Draws a triangle between points (x1,y1), (x2,y2) and (x3,y3) + void DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); + void DrawTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p = olc::WHITE); + // Flat fills a triangle between points (x1,y1), (x2,y2) and (x3,y3) + void FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p = olc::WHITE); + void FillTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p = olc::WHITE); + // Draws an entire sprite at location (x,y) + void DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); + void DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); + // Draws an area of a sprite at location (x,y), where the + // selected area is (ox,oy) to (ox+w,oy+h) + void DrawPartialSprite(int32_t x, int32_t y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); + void DrawPartialSprite(const olc::vi2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale = 1, uint8_t flip = olc::Sprite::NONE); + // Draws a single line of text - traditional monospaced + void DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + void DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + olc::vi2d GetTextSize(const std::string& s); + // Draws a single line of text - non-monospaced + void DrawStringProp(int32_t x, int32_t y, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + void DrawStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col = olc::WHITE, uint32_t scale = 1); + olc::vi2d GetTextSizeProp(const std::string& s); + + // Decal Quad functions + void SetDecalMode(const olc::DecalMode& mode); + void SetDecalStructure(const olc::DecalStructure& structure); + // Draws a whole decal, with optional scale and tinting + void DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + // Draws a region of a decal, with optional scale and tinting + void DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + void DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + // Draws fully user controlled 4 vertices, pos(pixels), uv(pixels), colours + void DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements = 4); + // Draws a decal with 4 arbitrary points, warping the texture to look "correct" + void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint = olc::WHITE); + void DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint = olc::WHITE); + void DrawWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::Pixel& tint = olc::WHITE); + // As above, but you can specify a region of a decal source sprite + void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + void DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + void DrawPartialWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint = olc::WHITE); + // Draws a decal rotated to specified angle, wit point of rotation offset + void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); + void DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale = { 1.0f, 1.0f }, const olc::Pixel& tint = olc::WHITE); + // Draws a multiline string as a decal, with tiniting and scaling + void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); + void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); + // Draws a single shaded filled rectangle as a decal + void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); + // Draws a corner shaded rectangle as a decal + void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR); + // Draws an arbitrary convex textured polygon using GPU + void DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const olc::Pixel tint = olc::WHITE); + void DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& depth, const std::vector& uv, const olc::Pixel tint = olc::WHITE); + void DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const std::vector& tint); + + // Draws a line in Decal Space + void DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p = olc::WHITE); + void DrawRotatedStringDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); + void DrawRotatedStringPropDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); + // Clears entire draw target to Pixel + void Clear(Pixel p); + // Clears the rendering back buffer + void ClearBuffer(Pixel p, bool bDepth = true); + // Returns the font image + olc::Sprite* GetFontSprite(); + + // Clip a line segment to visible area + bool ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2); + + // Dont allow PGE to mark layers as dirty, so pixel graphics don't update + void EnablePixelTransfer(const bool bEnable = true); + + // Command Console Routines + void ConsoleShow(const olc::Key &keyExit, bool bSuspendTime = true); + bool IsConsoleShowing() const; + void ConsoleClear(); + std::stringstream& ConsoleOut(); + void ConsoleCaptureStdOut(const bool bCapture); + + // Text Entry Routines + void TextEntryEnable(const bool bEnable, const std::string& sText = ""); + std::string TextEntryGetString() const; + int32_t TextEntryGetCursor() const; + bool IsTextEntryEnabled() const; + + + + private: + void UpdateTextEntry(); + void UpdateConsole(); + + public: + + // Experimental Lightweight 3D Routines ================ +#ifdef OLC_ENABLE_EXPERIMENTAL + // Set Manual View Matrix + void LW3D_View(const std::array& m); + // Set Manual World Matrix + void LW3D_World(const std::array& m); + // Set Manual Projection Matrix + void LW3D_Projection(const std::array& m); + + // Draws a vector of vertices, interprted as individual triangles + void LW3D_DrawTriangles(olc::Decal* decal, const std::vector>& pos, const std::vector& tex, const std::vector& col); + + void LW3D_ModelTranslate(const float x, const float y, const float z); + + // Camera convenience functions + void LW3D_SetCameraAtTarget(const float fEyeX, const float fEyeY, const float fEyeZ, + const float fTargetX, const float fTargetY, const float fTargetZ, + const float fUpX = 0.0f, const float fUpY = 1.0f, const float fUpZ = 0.0f); + void LW3D_SetCameraAlongDirection(const float fEyeX, const float fEyeY, const float fEyeZ, + const float fDirX, const float fDirY, const float fDirZ, + const float fUpX = 0.0f, const float fUpY = 1.0f, const float fUpZ = 0.0f); + + // 3D Rendering Flags + void LW3D_EnableDepthTest(const bool bEnableDepth); + void LW3D_EnableBackfaceCulling(const bool bEnableCull); +#endif + public: // Branding + std::string sAppName; + + private: // Inner mysterious workings + olc::Sprite* pDrawTarget = nullptr; + Pixel::Mode nPixelMode = Pixel::NORMAL; + float fBlendFactor = 1.0f; + olc::vi2d vScreenSize = { 256, 240 }; + olc::vf2d vInvScreenSize = { 1.0f / 256.0f, 1.0f / 240.0f }; + olc::vi2d vPixelSize = { 4, 4 }; + olc::vi2d vScreenPixelSize = { 4, 4 }; + olc::vi2d vMousePos = { 0, 0 }; + int32_t nMouseWheelDelta = 0; + olc::vi2d vMousePosCache = { 0, 0 }; + olc::vi2d vMouseWindowPos = { 0, 0 }; + int32_t nMouseWheelDeltaCache = 0; + olc::vi2d vWindowSize = { 0, 0 }; + olc::vi2d vViewPos = { 0, 0 }; + olc::vi2d vViewSize = { 0,0 }; + bool bFullScreen = false; + olc::vf2d vPixel = { 1.0f, 1.0f }; + bool bHasInputFocus = false; + bool bHasMouseFocus = false; + bool bEnableVSYNC = false; + float fFrameTimer = 1.0f; + float fLastElapsed = 0.0f; + int nFrameCount = 0; + bool bSuspendTextureTransfer = false; + Renderable fontRenderable; + std::vector vLayers; + uint8_t nTargetLayer = 0; + uint32_t nLastFPS = 0; + bool bPixelCohesion = false; + DecalMode nDecalMode = DecalMode::NORMAL; + DecalStructure nDecalStructure = DecalStructure::FAN; + std::function funcPixelMode; + std::chrono::time_point m_tp1, m_tp2; + std::vector vFontSpacing; + + // Command Console Specific + bool bConsoleShow = false; + bool bConsoleSuspendTime = false; + olc::Key keyConsoleExit = olc::Key::F1; + std::stringstream ssConsoleOutput; + std::streambuf* sbufOldCout = nullptr; + olc::vi2d vConsoleSize; + olc::vi2d vConsoleCursor = { 0,0 }; + olc::vf2d vConsoleCharacterScale = { 1.0f, 2.0f }; + std::vector sConsoleLines; + std::list sCommandHistory; + std::list::iterator sCommandHistoryIt; + + // Text Entry Specific + bool bTextEntryEnable = false; + std::string sTextEntryString = ""; + int32_t nTextEntryCursor = 0; + std::vector> vKeyboardMap; + + + + // State of keyboard + bool pKeyNewState[256] = { 0 }; + bool pKeyOldState[256] = { 0 }; + HWButton pKeyboardState[256] = { 0 }; + + // State of mouse + bool pMouseNewState[nMouseButtons] = { 0 }; + bool pMouseOldState[nMouseButtons] = { 0 }; + HWButton pMouseState[nMouseButtons] = { 0 }; + + // The main engine thread + void EngineThread(); + + + // If anything sets this flag to false, the engine + // "should" shut down gracefully + static std::atomic bAtomActive; + + public: + // "Break In" Functions + void olc_UpdateMouse(int32_t x, int32_t y); + void olc_UpdateMouseWheel(int32_t delta); + void olc_UpdateWindowSize(int32_t x, int32_t y); + void olc_UpdateViewport(); + void olc_ConstructFontSheet(); + void olc_CoreUpdate(); + void olc_PrepareEngine(); + void olc_UpdateMouseState(int32_t button, bool state); + void olc_UpdateKeyState(int32_t key, bool state); + void olc_UpdateMouseFocus(bool state); + void olc_UpdateKeyFocus(bool state); + void olc_Terminate(); + void olc_Reanimate(); + bool olc_IsRunning(); + + // At the very end of this file, chooses which + // components to compile + virtual void olc_ConfigureSystem(); + + // NOTE: Items Here are to be deprecated, I have left them in for now + // in case you are using them, but they will be removed. + // olc::vf2d vSubPixelOffset = { 0.0f, 0.0f }; + + public: // PGEX Stuff + friend class PGEX; + void pgex_Register(olc::PGEX* pgex); + + private: + std::vector vExtensions; + }; + + + + // O------------------------------------------------------------------------------O + // | PGE EXTENSION BASE CLASS - Permits access to PGE functions from extension | + // O------------------------------------------------------------------------------O + class PGEX + { + friend class olc::PixelGameEngine; + public: + PGEX(bool bHook = false); + + protected: + virtual void OnBeforeUserCreate(); + virtual void OnAfterUserCreate(); + virtual bool OnBeforeUserUpdate(float &fElapsedTime); + virtual void OnAfterUserUpdate(float fElapsedTime); + + protected: + static PixelGameEngine* pge; + }; +} + +#pragma endregion + +#endif // OLC_PGE_DEF + + +// O------------------------------------------------------------------------------O +// | START OF OLC_PGE_APPLICATION | +// O------------------------------------------------------------------------------O +#ifdef OLC_PGE_APPLICATION +#undef OLC_PGE_APPLICATION + +// O------------------------------------------------------------------------------O +// | olcPixelGameEngine INTERFACE IMPLEMENTATION (CORE) | +// | Note: The core implementation is platform independent | +// O------------------------------------------------------------------------------O +#pragma region pge_implementation +namespace olc +{ + // O------------------------------------------------------------------------------O + // | olc::Pixel IMPLEMENTATION | + // O------------------------------------------------------------------------------O + Pixel::Pixel() + { r = 0; g = 0; b = 0; a = nDefaultAlpha; } + + Pixel::Pixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) + { n = red | (green << 8) | (blue << 16) | (alpha << 24); } // Thanks jarekpelczar + + Pixel::Pixel(uint32_t p) + { n = p; } + + bool Pixel::operator==(const Pixel& p) const + { return n == p.n; } + + bool Pixel::operator!=(const Pixel& p) const + { return n != p.n; } + + Pixel Pixel::operator * (const float i) const + { + float fR = std::min(255.0f, std::max(0.0f, float(r) * i)); + float fG = std::min(255.0f, std::max(0.0f, float(g) * i)); + float fB = std::min(255.0f, std::max(0.0f, float(b) * i)); + return Pixel(uint8_t(fR), uint8_t(fG), uint8_t(fB), a); + } + + Pixel Pixel::operator / (const float i) const + { + float fR = std::min(255.0f, std::max(0.0f, float(r) / i)); + float fG = std::min(255.0f, std::max(0.0f, float(g) / i)); + float fB = std::min(255.0f, std::max(0.0f, float(b) / i)); + return Pixel(uint8_t(fR), uint8_t(fG), uint8_t(fB), a); + } + + Pixel& Pixel::operator *=(const float i) + { + this->r = uint8_t(std::min(255.0f, std::max(0.0f, float(r) * i))); + this->g = uint8_t(std::min(255.0f, std::max(0.0f, float(g) * i))); + this->b = uint8_t(std::min(255.0f, std::max(0.0f, float(b) * i))); + return *this; + } + + Pixel& Pixel::operator /=(const float i) + { + this->r = uint8_t(std::min(255.0f, std::max(0.0f, float(r) / i))); + this->g = uint8_t(std::min(255.0f, std::max(0.0f, float(g) / i))); + this->b = uint8_t(std::min(255.0f, std::max(0.0f, float(b) / i))); + return *this; + } + + Pixel Pixel::operator + (const Pixel& p) const + { + uint8_t nR = uint8_t(std::min(255, std::max(0, int(r) + int(p.r)))); + uint8_t nG = uint8_t(std::min(255, std::max(0, int(g) + int(p.g)))); + uint8_t nB = uint8_t(std::min(255, std::max(0, int(b) + int(p.b)))); + return Pixel(nR, nG, nB, a); + } + + Pixel Pixel::operator - (const Pixel& p) const + { + uint8_t nR = uint8_t(std::min(255, std::max(0, int(r) - int(p.r)))); + uint8_t nG = uint8_t(std::min(255, std::max(0, int(g) - int(p.g)))); + uint8_t nB = uint8_t(std::min(255, std::max(0, int(b) - int(p.b)))); + return Pixel(nR, nG, nB, a); + } + + Pixel& Pixel::operator += (const Pixel& p) + { + this->r = uint8_t(std::min(255, std::max(0, int(r) + int(p.r)))); + this->g = uint8_t(std::min(255, std::max(0, int(g) + int(p.g)))); + this->b = uint8_t(std::min(255, std::max(0, int(b) + int(p.b)))); + return *this; + } + + Pixel& Pixel::operator -= (const Pixel& p) // Thanks Au Lit + { + this->r = uint8_t(std::min(255, std::max(0, int(r) - int(p.r)))); + this->g = uint8_t(std::min(255, std::max(0, int(g) - int(p.g)))); + this->b = uint8_t(std::min(255, std::max(0, int(b) - int(p.b)))); + return *this; + } + + Pixel Pixel::inv() const + { + uint8_t nR = uint8_t(std::min(255, std::max(0, 255 - int(r)))); + uint8_t nG = uint8_t(std::min(255, std::max(0, 255 - int(g)))); + uint8_t nB = uint8_t(std::min(255, std::max(0, 255 - int(b)))); + return Pixel(nR, nG, nB, a); + } + + Pixel PixelF(float red, float green, float blue, float alpha) + { return Pixel(uint8_t(red * 255.0f), uint8_t(green * 255.0f), uint8_t(blue * 255.0f), uint8_t(alpha * 255.0f)); } + + Pixel PixelLerp(const olc::Pixel& p1, const olc::Pixel& p2, float t) + { return (p2 * t) + p1 * (1.0f - t); } + + // O------------------------------------------------------------------------------O + // | olc::Sprite IMPLEMENTATION | + // O------------------------------------------------------------------------------O + Sprite::Sprite() + { width = 0; height = 0; } + + Sprite::Sprite(const std::string& sImageFile, olc::ResourcePack* pack) + { LoadFromFile(sImageFile, pack); } + + Sprite::Sprite(int32_t w, int32_t h) + { + width = w; height = h; + pColData.resize(width * height); + pColData.resize(width * height, nDefaultPixel); + } + + Sprite::~Sprite() + { pColData.clear(); } + + void Sprite::SetSampleMode(olc::Sprite::Mode mode) + { modeSample = mode; } + + Pixel Sprite::GetPixel(const olc::vi2d& a) const + { return GetPixel(a.x, a.y); } + + bool Sprite::SetPixel(const olc::vi2d& a, Pixel p) + { return SetPixel(a.x, a.y, p); } + + Pixel Sprite::GetPixel(int32_t x, int32_t y) const + { + if (modeSample == olc::Sprite::Mode::NORMAL) + { + if (x >= 0 && x < width && y >= 0 && y < height) + return pColData[y * width + x]; + else + return Pixel(0, 0, 0, 0); + } + else + { + if (modeSample == olc::Sprite::Mode::PERIODIC) + return pColData[abs(y % height) * width + abs(x % width)]; + else + return pColData[std::max(0, std::min(y, height-1)) * width + std::max(0, std::min(x, width-1))]; + } + } + + bool Sprite::SetPixel(int32_t x, int32_t y, Pixel p) + { + if (x >= 0 && x < width && y >= 0 && y < height) + { + pColData[y * width + x] = p; + return true; + } + else + return false; + } + + Pixel Sprite::Sample(float x, float y) const + { + int32_t sx = std::min((int32_t)((x * (float)width)), width - 1); + int32_t sy = std::min((int32_t)((y * (float)height)), height - 1); + return GetPixel(sx, sy); + } + + Pixel Sprite::SampleBL(float u, float v) const + { + u = u * width - 0.5f; + v = v * height - 0.5f; + int x = (int)floor(u); // cast to int rounds toward zero, not downward + int y = (int)floor(v); // Thanks @joshinils + float u_ratio = u - x; + float v_ratio = v - y; + float u_opposite = 1 - u_ratio; + float v_opposite = 1 - v_ratio; + + olc::Pixel p1 = GetPixel(std::max(x, 0), std::max(y, 0)); + olc::Pixel p2 = GetPixel(std::min(x + 1, (int)width - 1), std::max(y, 0)); + olc::Pixel p3 = GetPixel(std::max(x, 0), std::min(y + 1, (int)height - 1)); + olc::Pixel p4 = GetPixel(std::min(x + 1, (int)width - 1), std::min(y + 1, (int)height - 1)); + + return olc::Pixel( + (uint8_t)((p1.r * u_opposite + p2.r * u_ratio) * v_opposite + (p3.r * u_opposite + p4.r * u_ratio) * v_ratio), + (uint8_t)((p1.g * u_opposite + p2.g * u_ratio) * v_opposite + (p3.g * u_opposite + p4.g * u_ratio) * v_ratio), + (uint8_t)((p1.b * u_opposite + p2.b * u_ratio) * v_opposite + (p3.b * u_opposite + p4.b * u_ratio) * v_ratio)); + } + + Pixel* Sprite::GetData() + { return pColData.data(); } + + + olc::rcode Sprite::LoadFromFile(const std::string& sImageFile, olc::ResourcePack* pack) + { + UNUSED(pack); + return loader->LoadImageResource(this, sImageFile, pack); + } + + olc::Sprite* Sprite::Duplicate() + { + olc::Sprite* spr = new olc::Sprite(width, height); + std::memcpy(spr->GetData(), GetData(), width * height * sizeof(olc::Pixel)); + spr->modeSample = modeSample; + return spr; + } + + olc::Sprite* Sprite::Duplicate(const olc::vi2d& vPos, const olc::vi2d& vSize) + { + olc::Sprite* spr = new olc::Sprite(vSize.x, vSize.y); + for (int y = 0; y < vSize.y; y++) + for (int x = 0; x < vSize.x; x++) + spr->SetPixel(x, y, GetPixel(vPos.x + x, vPos.y + y)); + return spr; + } + + // O------------------------------------------------------------------------------O + // | olc::Decal IMPLEMENTATION | + // O------------------------------------------------------------------------------O + Decal::Decal(olc::Sprite* spr, bool filter, bool clamp) + { + id = -1; + if (spr == nullptr) return; + sprite = spr; + id = renderer->CreateTexture(sprite->width, sprite->height, filter, clamp); + Update(); + } + + Decal::Decal(const uint32_t nExistingTextureResource, olc::Sprite* spr) + { + if (spr == nullptr) return; + id = nExistingTextureResource; + } + + void Decal::Update() + { + if (sprite == nullptr) return; + vUVScale = { 1.0f / float(sprite->width), 1.0f / float(sprite->height) }; + renderer->ApplyTexture(id); + renderer->UpdateTexture(id, sprite); + } + + void Decal::UpdateSprite() + { + if (sprite == nullptr) return; + renderer->ApplyTexture(id); + renderer->ReadTexture(id, sprite); + } + + Decal::~Decal() + { + if (id != -1) + { + renderer->DeleteTexture(id); + id = -1; + } + } + + void Renderable::Create(uint32_t width, uint32_t height, bool filter, bool clamp) + { + pSprite = std::make_unique(width, height); + pDecal = std::make_unique(pSprite.get(), filter, clamp); + } + + olc::rcode Renderable::Load(const std::string& sFile, ResourcePack* pack, bool filter, bool clamp) + { + pSprite = std::make_unique(); + if (pSprite->LoadFromFile(sFile, pack) == olc::rcode::OK) + { + pDecal = std::make_unique(pSprite.get(), filter, clamp); + return olc::rcode::OK; + } + else + { + pSprite.release(); + pSprite = nullptr; + return olc::rcode::NO_FILE; + } + } + + olc::Decal* Renderable::Decal() const + { return pDecal.get(); } + + olc::Sprite* Renderable::Sprite() const + { return pSprite.get(); } + + // O------------------------------------------------------------------------------O + // | olc::ResourcePack IMPLEMENTATION | + // O------------------------------------------------------------------------------O + + + //============================================================= + // Resource Packs - Allows you to store files in one large + // scrambled file - Thanks MaGetzUb for debugging a null char in std::stringstream bug + ResourceBuffer::ResourceBuffer(std::ifstream& ifs, uint32_t offset, uint32_t size) + { + vMemory.resize(size); + ifs.seekg(offset); ifs.read(vMemory.data(), vMemory.size()); + setg(vMemory.data(), vMemory.data(), vMemory.data() + size); + } + + ResourcePack::ResourcePack() { } + ResourcePack::~ResourcePack() { baseFile.close(); } + + bool ResourcePack::AddFile(const std::string& sFile) + { + const std::string file = makeposix(sFile); + + if (_gfs::exists(file)) + { + sResourceFile e; + e.nSize = (uint32_t)_gfs::file_size(file); + e.nOffset = 0; // Unknown at this stage + mapFiles[file] = e; + return true; + } + return false; + } + + bool ResourcePack::LoadPack(const std::string& sFile, const std::string& sKey) + { + // Open the resource file + baseFile.open(sFile, std::ifstream::binary); + if (!baseFile.is_open()) return false; + + // 1) Read Scrambled index + uint32_t nIndexSize = 0; + baseFile.read((char*)&nIndexSize, sizeof(uint32_t)); + + std::vector buffer(nIndexSize); + for (uint32_t j = 0; j < nIndexSize; j++) + buffer[j] = baseFile.get(); + + std::vector decoded = scramble(buffer, sKey); + size_t pos = 0; + auto read = [&decoded, &pos](char* dst, size_t size) { + memcpy((void*)dst, (const void*)(decoded.data() + pos), size); + pos += size; + }; + + auto get = [&read]() -> int { char c; read(&c, 1); return c; }; + + // 2) Read Map + uint32_t nMapEntries = 0; + read((char*)&nMapEntries, sizeof(uint32_t)); + for (uint32_t i = 0; i < nMapEntries; i++) + { + uint32_t nFilePathSize = 0; + read((char*)&nFilePathSize, sizeof(uint32_t)); + + std::string sFileName(nFilePathSize, ' '); + for (uint32_t j = 0; j < nFilePathSize; j++) + sFileName[j] = get(); + + sResourceFile e; + read((char*)&e.nSize, sizeof(uint32_t)); + read((char*)&e.nOffset, sizeof(uint32_t)); + mapFiles[sFileName] = e; + } + + // Don't close base file! we will provide a stream + // pointer when the file is requested + return true; + } + + bool ResourcePack::SavePack(const std::string& sFile, const std::string& sKey) + { + // Create/Overwrite the resource file + std::ofstream ofs(sFile, std::ofstream::binary); + if (!ofs.is_open()) return false; + + // Iterate through map + uint32_t nIndexSize = 0; // Unknown for now + ofs.write((char*)&nIndexSize, sizeof(uint32_t)); + uint32_t nMapSize = uint32_t(mapFiles.size()); + ofs.write((char*)&nMapSize, sizeof(uint32_t)); + for (auto& e : mapFiles) + { + // Write the path of the file + size_t nPathSize = e.first.size(); + ofs.write((char*)&nPathSize, sizeof(uint32_t)); + ofs.write(e.first.c_str(), nPathSize); + + // Write the file entry properties + ofs.write((char*)&e.second.nSize, sizeof(uint32_t)); + ofs.write((char*)&e.second.nOffset, sizeof(uint32_t)); + } + + // 2) Write the individual Data + std::streampos offset = ofs.tellp(); + nIndexSize = (uint32_t)offset; + for (auto& e : mapFiles) + { + // Store beginning of file offset within resource pack file + e.second.nOffset = (uint32_t)offset; + + // Load the file to be added + std::vector vBuffer(e.second.nSize); + std::ifstream i(e.first, std::ifstream::binary); + i.read((char*)vBuffer.data(), e.second.nSize); + i.close(); + + // Write the loaded file into resource pack file + ofs.write((char*)vBuffer.data(), e.second.nSize); + offset += e.second.nSize; + } + + // 3) Scramble Index + std::vector stream; + auto write = [&stream](const char* data, size_t size) { + size_t sizeNow = stream.size(); + stream.resize(sizeNow + size); + memcpy(stream.data() + sizeNow, data, size); + }; + + // Iterate through map + write((char*)&nMapSize, sizeof(uint32_t)); + for (auto& e : mapFiles) + { + // Write the path of the file + size_t nPathSize = e.first.size(); + write((char*)&nPathSize, sizeof(uint32_t)); + write(e.first.c_str(), nPathSize); + + // Write the file entry properties + write((char*)&e.second.nSize, sizeof(uint32_t)); + write((char*)&e.second.nOffset, sizeof(uint32_t)); + } + std::vector sIndexString = scramble(stream, sKey); + uint32_t nIndexStringLen = uint32_t(sIndexString.size()); + // 4) Rewrite Map (it has been updated with offsets now) + // at start of file + ofs.seekp(0, std::ios::beg); + ofs.write((char*)&nIndexStringLen, sizeof(uint32_t)); + ofs.write(sIndexString.data(), nIndexStringLen); + ofs.close(); + return true; + } + + ResourceBuffer ResourcePack::GetFileBuffer(const std::string& sFile) + { return ResourceBuffer(baseFile, mapFiles[sFile].nOffset, mapFiles[sFile].nSize); } + + bool ResourcePack::Loaded() + { return baseFile.is_open(); } + + std::vector ResourcePack::scramble(const std::vector& data, const std::string& key) + { + if (key.empty()) return data; + std::vector o; + size_t c = 0; + for (auto s : data) o.push_back(s ^ key[(c++) % key.size()]); + return o; + }; + + std::string ResourcePack::makeposix(const std::string& path) + { + std::string o; + for (auto s : path) o += std::string(1, s == '\\' ? '/' : s); + return o; + }; + + // O------------------------------------------------------------------------------O + // | olc::PixelGameEngine IMPLEMENTATION | + // O------------------------------------------------------------------------------O + PixelGameEngine::PixelGameEngine() + { + sAppName = "Undefined"; + olc::PGEX::pge = this; + + // Bring in relevant Platform & Rendering systems depending + // on compiler parameters + olc_ConfigureSystem(); + } + + PixelGameEngine::~PixelGameEngine() + {} + + + olc::rcode PixelGameEngine::Construct(int32_t screen_w, int32_t screen_h, int32_t pixel_w, int32_t pixel_h, bool full_screen, bool vsync, bool cohesion) + { + bPixelCohesion = cohesion; + vScreenSize = { screen_w, screen_h }; + vInvScreenSize = { 1.0f / float(screen_w), 1.0f / float(screen_h) }; + vPixelSize = { pixel_w, pixel_h }; + vWindowSize = vScreenSize * vPixelSize; + bFullScreen = full_screen; + bEnableVSYNC = vsync; + vPixel = 2.0f / vScreenSize; + + if (vPixelSize.x <= 0 || vPixelSize.y <= 0 || vScreenSize.x <= 0 || vScreenSize.y <= 0) + return olc::FAIL; + return olc::OK; + } + + + void PixelGameEngine::SetScreenSize(int w, int h) + { + vScreenSize = { w, h }; + vInvScreenSize = { 1.0f / float(w), 1.0f / float(h) }; + for (auto& layer : vLayers) + { + layer.pDrawTarget.Create(vScreenSize.x, vScreenSize.y); + layer.bUpdate = true; + } + SetDrawTarget(nullptr); + renderer->ClearBuffer(olc::BLACK, true); + renderer->DisplayFrame(); + renderer->ClearBuffer(olc::BLACK, true); + renderer->UpdateViewport(vViewPos, vViewSize); + } + +#if !defined(PGE_USE_CUSTOM_START) + olc::rcode PixelGameEngine::Start() + { + if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL; + + // Construct the window + if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL; + olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y); + + // Start the thread + bAtomActive = true; + std::thread t = std::thread(&PixelGameEngine::EngineThread, this); + + // Some implementations may form an event loop here + platform->StartSystemEventLoop(); + + // Wait for thread to be exited + t.join(); + + if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL; + + return olc::OK; + } +#endif + + void PixelGameEngine::SetDrawTarget(Sprite* target) + { + if (target) + { + pDrawTarget = target; + } + else + { + nTargetLayer = 0; + pDrawTarget = vLayers[0].pDrawTarget.Sprite(); + } + } + + void PixelGameEngine::SetDrawTarget(uint8_t layer, bool bDirty) + { + if (layer < vLayers.size()) + { + pDrawTarget = vLayers[layer].pDrawTarget.Sprite(); + vLayers[layer].bUpdate = bDirty; + nTargetLayer = layer; + } + } + + void PixelGameEngine::EnableLayer(uint8_t layer, bool b) + { if (layer < vLayers.size()) vLayers[layer].bShow = b; } + + void PixelGameEngine::SetLayerOffset(uint8_t layer, const olc::vf2d& offset) + { SetLayerOffset(layer, offset.x, offset.y); } + + void PixelGameEngine::SetLayerOffset(uint8_t layer, float x, float y) + { if (layer < vLayers.size()) vLayers[layer].vOffset = { x, y }; } + + void PixelGameEngine::SetLayerScale(uint8_t layer, const olc::vf2d& scale) + { SetLayerScale(layer, scale.x, scale.y); } + + void PixelGameEngine::SetLayerScale(uint8_t layer, float x, float y) + { if (layer < vLayers.size()) vLayers[layer].vScale = { x, y }; } + + void PixelGameEngine::SetLayerTint(uint8_t layer, const olc::Pixel& tint) + { if (layer < vLayers.size()) vLayers[layer].tint = tint; } + + void PixelGameEngine::SetLayerCustomRenderFunction(uint8_t layer, std::function f) + { if (layer < vLayers.size()) vLayers[layer].funcHook = f; } + + std::vector& PixelGameEngine::GetLayers() + { return vLayers; } + + uint32_t PixelGameEngine::CreateLayer() + { + LayerDesc ld; + ld.pDrawTarget.Create(vScreenSize.x, vScreenSize.y); + vLayers.push_back(std::move(ld)); + return uint32_t(vLayers.size()) - 1; + } + + Sprite* PixelGameEngine::GetDrawTarget() const + { return pDrawTarget; } + + int32_t PixelGameEngine::GetDrawTargetWidth() const + { + if (pDrawTarget) + return pDrawTarget->width; + else + return 0; + } + + int32_t PixelGameEngine::GetDrawTargetHeight() const + { + if (pDrawTarget) + return pDrawTarget->height; + else + return 0; + } + + uint32_t PixelGameEngine::GetFPS() const + { return nLastFPS; } + + bool PixelGameEngine::IsFocused() const + { return bHasInputFocus; } + + HWButton PixelGameEngine::GetKey(Key k) const + { return pKeyboardState[k]; } + + HWButton PixelGameEngine::GetMouse(uint32_t b) const + { return pMouseState[b]; } + + int32_t PixelGameEngine::GetMouseX() const + { return vMousePos.x; } + + int32_t PixelGameEngine::GetMouseY() const + { return vMousePos.y; } + + const olc::vi2d& PixelGameEngine::GetMousePos() const + { return vMousePos; } + + int32_t PixelGameEngine::GetMouseWheel() const + { return nMouseWheelDelta; } + + int32_t PixelGameEngine::ScreenWidth() const + { return vScreenSize.x; } + + int32_t PixelGameEngine::ScreenHeight() const + { return vScreenSize.y; } + + float PixelGameEngine::GetElapsedTime() const + { return fLastElapsed; } + + const olc::vi2d& PixelGameEngine::GetWindowSize() const + { return vWindowSize; } + + const olc::vi2d& PixelGameEngine::GetPixelSize() const + { return vPixelSize; } + + const olc::vi2d& PixelGameEngine::GetScreenPixelSize() const + { return vScreenPixelSize; } + + const olc::vi2d& PixelGameEngine::GetWindowMouse() const + { return vMouseWindowPos; } + + bool PixelGameEngine::Draw(const olc::vi2d& pos, Pixel p) + { return Draw(pos.x, pos.y, p); } + + // This is it, the critical function that plots a pixel + bool PixelGameEngine::Draw(int32_t x, int32_t y, Pixel p) + { + if (!pDrawTarget) return false; + + if (nPixelMode == Pixel::NORMAL) + { + return pDrawTarget->SetPixel(x, y, p); + } + + if (nPixelMode == Pixel::MASK) + { + if (p.a == 255) + return pDrawTarget->SetPixel(x, y, p); + } + + if (nPixelMode == Pixel::ALPHA) + { + Pixel d = pDrawTarget->GetPixel(x, y); + float a = (float)(p.a / 255.0f) * fBlendFactor; + float c = 1.0f - a; + float r = a * (float)p.r + c * (float)d.r; + float g = a * (float)p.g + c * (float)d.g; + float b = a * (float)p.b + c * (float)d.b; + return pDrawTarget->SetPixel(x, y, Pixel((uint8_t)r, (uint8_t)g, (uint8_t)b/*, (uint8_t)(p.a * fBlendFactor)*/)); + } + + if (nPixelMode == Pixel::CUSTOM) + { + return pDrawTarget->SetPixel(x, y, funcPixelMode(x, y, p, pDrawTarget->GetPixel(x, y))); + } + + return false; + } + + + void PixelGameEngine::DrawLine(const olc::vi2d& pos1, const olc::vi2d& pos2, Pixel p, uint32_t pattern) + { DrawLine(pos1.x, pos1.y, pos2.x, pos2.y, p, pattern); } + + void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p, uint32_t pattern) + { + int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i; + dx = x2 - x1; dy = y2 - y1; + + auto rol = [&](void) { pattern = (pattern << 1) | (pattern >> 31); return pattern & 1; }; + + olc::vi2d p1(x1, y1), p2(x2, y2); + //if (!ClipLineToScreen(p1, p2)) + // return; + x1 = p1.x; y1 = p1.y; + x2 = p2.x; y2 = p2.y; + + // straight lines idea by gurkanctn + if (dx == 0) // Line is vertical + { + if (y2 < y1) std::swap(y1, y2); + for (y = y1; y <= y2; y++) if (rol()) Draw(x1, y, p); + return; + } + + if (dy == 0) // Line is horizontal + { + if (x2 < x1) std::swap(x1, x2); + for (x = x1; x <= x2; x++) if (rol()) Draw(x, y1, p); + return; + } + + // Line is Funk-aye + dx1 = abs(dx); dy1 = abs(dy); + px = 2 * dy1 - dx1; py = 2 * dx1 - dy1; + if (dy1 <= dx1) + { + if (dx >= 0) + { + x = x1; y = y1; xe = x2; + } + else + { + x = x2; y = y2; xe = x1; + } + + if (rol()) Draw(x, y, p); + + for (i = 0; x < xe; i++) + { + x = x + 1; + if (px < 0) + px = px + 2 * dy1; + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y = y + 1; else y = y - 1; + px = px + 2 * (dy1 - dx1); + } + if (rol()) Draw(x, y, p); + } + } + else + { + if (dy >= 0) + { + x = x1; y = y1; ye = y2; + } + else + { + x = x2; y = y2; ye = y1; + } + + if (rol()) Draw(x, y, p); + + for (i = 0; y < ye; i++) + { + y = y + 1; + if (py <= 0) + py = py + 2 * dx1; + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x = x + 1; else x = x - 1; + py = py + 2 * (dx1 - dy1); + } + if (rol()) Draw(x, y, p); + } + } + } + + void PixelGameEngine::DrawCircle(const olc::vi2d& pos, int32_t radius, Pixel p, uint8_t mask) + { DrawCircle(pos.x, pos.y, radius, p, mask); } + + void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p, uint8_t mask) + { // Thanks to IanM-Matrix1 #PR121 + if (radius < 0 || x < -radius || y < -radius || x - GetDrawTargetWidth() > radius || y - GetDrawTargetHeight() > radius) + return; + + if (radius > 0) + { + int x0 = 0; + int y0 = radius; + int d = 3 - 2 * radius; + + while (y0 >= x0) // only formulate 1/8 of circle + { + // Draw even octants + if (mask & 0x01) Draw(x + x0, y - y0, p);// Q6 - upper right right + if (mask & 0x04) Draw(x + y0, y + x0, p);// Q4 - lower lower right + if (mask & 0x10) Draw(x - x0, y + y0, p);// Q2 - lower left left + if (mask & 0x40) Draw(x - y0, y - x0, p);// Q0 - upper upper left + if (x0 != 0 && x0 != y0) + { + if (mask & 0x02) Draw(x + y0, y - x0, p);// Q7 - upper upper right + if (mask & 0x08) Draw(x + x0, y + y0, p);// Q5 - lower right right + if (mask & 0x20) Draw(x - y0, y + x0, p);// Q3 - lower lower left + if (mask & 0x80) Draw(x - x0, y - y0, p);// Q1 - upper left left + } + + if (d < 0) + d += 4 * x0++ + 6; + else + d += 4 * (x0++ - y0--) + 10; + } + } + else + Draw(x, y, p); + } + + void PixelGameEngine::FillCircle(const olc::vi2d& pos, int32_t radius, Pixel p) + { FillCircle(pos.x, pos.y, radius, p); } + + void PixelGameEngine::FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p) + { // Thanks to IanM-Matrix1 #PR121 + if (radius < 0 || x < -radius || y < -radius || x - GetDrawTargetWidth() > radius || y - GetDrawTargetHeight() > radius) + return; + + if (radius > 0) + { + int x0 = 0; + int y0 = radius; + int d = 3 - 2 * radius; + + auto drawline = [&](int sx, int ex, int y) + { + for (int x = sx; x <= ex; x++) + Draw(x, y, p); + }; + + while (y0 >= x0) + { + drawline(x - y0, x + y0, y - x0); + if (x0 > 0) drawline(x - y0, x + y0, y + x0); + + if (d < 0) + d += 4 * x0++ + 6; + else + { + if (x0 != y0) + { + drawline(x - x0, x + x0, y - y0); + drawline(x - x0, x + x0, y + y0); + } + d += 4 * (x0++ - y0--) + 10; + } + } + } + else + Draw(x, y, p); + } + + void PixelGameEngine::DrawRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p) + { DrawRect(pos.x, pos.y, size.x, size.y, p); } + + void PixelGameEngine::DrawRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) + { + DrawLine(x, y, x + w, y, p); + DrawLine(x + w, y, x + w, y + h, p); + DrawLine(x + w, y + h, x, y + h, p); + DrawLine(x, y + h, x, y, p); + } + + void PixelGameEngine::Clear(Pixel p) + { + int pixels = GetDrawTargetWidth() * GetDrawTargetHeight(); + Pixel* m = GetDrawTarget()->GetData(); + for (int i = 0; i < pixels; i++) m[i] = p; + } + + void PixelGameEngine::ClearBuffer(Pixel p, bool bDepth) + { renderer->ClearBuffer(p, bDepth); } + + olc::Sprite* PixelGameEngine::GetFontSprite() + { return fontRenderable.Sprite(); } + + bool PixelGameEngine::ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2) + { + // https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm + static constexpr int SEG_I = 0b0000, SEG_L = 0b0001, SEG_R = 0b0010, SEG_B = 0b0100, SEG_T = 0b1000; + auto Segment = [&vScreenSize = vScreenSize](const olc::vi2d& v) + { + int i = SEG_I; + if (v.x < 0) i |= SEG_L; else if (v.x > vScreenSize.x) i |= SEG_R; + if (v.y < 0) i |= SEG_B; else if (v.y > vScreenSize.y) i |= SEG_T; + return i; + }; + + int s1 = Segment(in_p1), s2 = Segment(in_p2); + + while (true) + { + if (!(s1 | s2)) return true; + else if (s1 & s2) return false; + else + { + int s3 = s2 > s1 ? s2 : s1; + olc::vi2d n; + if (s3 & SEG_T) { n.x = in_p1.x + (in_p2.x - in_p1.x) * (vScreenSize.y - in_p1.y) / (in_p2.y - in_p1.y); n.y = vScreenSize.y; } + else if (s3 & SEG_B) { n.x = in_p1.x + (in_p2.x - in_p1.x) * (0 - in_p1.y) / (in_p2.y - in_p1.y); n.y = 0; } + else if (s3 & SEG_R) { n.x = vScreenSize.x; n.y = in_p1.y + (in_p2.y - in_p1.y) * (vScreenSize.x - in_p1.x) / (in_p2.x - in_p1.x); } + else if (s3 & SEG_L) { n.x = 0; n.y = in_p1.y + (in_p2.y - in_p1.y) * (0 - in_p1.x) / (in_p2.x - in_p1.x); } + if (s3 == s1) { in_p1 = n; s1 = Segment(in_p1); } + else { in_p2 = n; s2 = Segment(in_p2); } + } + } + return true; + } + + void PixelGameEngine::EnablePixelTransfer(const bool bEnable) + { + bSuspendTextureTransfer = !bEnable; + } + + + void PixelGameEngine::FillRect(const olc::vi2d& pos, const olc::vi2d& size, Pixel p) + { FillRect(pos.x, pos.y, size.x, size.y, p); } + + void PixelGameEngine::FillRect(int32_t x, int32_t y, int32_t w, int32_t h, Pixel p) + { + int32_t x2 = x + w; + int32_t y2 = y + h; + + if (x < 0) x = 0; + if (x >= (int32_t)GetDrawTargetWidth()) x = (int32_t)GetDrawTargetWidth(); + if (y < 0) y = 0; + if (y >= (int32_t)GetDrawTargetHeight()) y = (int32_t)GetDrawTargetHeight(); + + if (x2 < 0) x2 = 0; + if (x2 >= (int32_t)GetDrawTargetWidth()) x2 = (int32_t)GetDrawTargetWidth(); + if (y2 < 0) y2 = 0; + if (y2 >= (int32_t)GetDrawTargetHeight()) y2 = (int32_t)GetDrawTargetHeight(); + + for (int i = x; i < x2; i++) + for (int j = y; j < y2; j++) + Draw(i, j, p); + } + + void PixelGameEngine::DrawTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p) + { DrawTriangle(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y, p); } + + void PixelGameEngine::DrawTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) + { + DrawLine(x1, y1, x2, y2, p); + DrawLine(x2, y2, x3, y3, p); + DrawLine(x3, y3, x1, y1, p); + } + + void PixelGameEngine::FillTriangle(const olc::vi2d& pos1, const olc::vi2d& pos2, const olc::vi2d& pos3, Pixel p) + { FillTriangle(pos1.x, pos1.y, pos2.x, pos2.y, pos3.x, pos3.y, p); } + + // https://www.avrfreaks.net/sites/default/files/triangles.c + void PixelGameEngine::FillTriangle(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3, Pixel p) + { + auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Draw(i, ny, p); }; + + int t1x, t2x, y, minx, maxx, t1xp, t2xp; + bool changed1 = false; + bool changed2 = false; + int signx1, signx2, dx1, dy1, dx2, dy2; + int e1, e2; + // Sort vertices + if (y1 > y2) { std::swap(y1, y2); std::swap(x1, x2); } + if (y1 > y3) { std::swap(y1, y3); std::swap(x1, x3); } + if (y2 > y3) { std::swap(y2, y3); std::swap(x2, x3); } + + t1x = t2x = x1; y = y1; // Starting points + dx1 = (int)(x2 - x1); + if (dx1 < 0) { dx1 = -dx1; signx1 = -1; } + else signx1 = 1; + dy1 = (int)(y2 - y1); + + dx2 = (int)(x3 - x1); + if (dx2 < 0) { dx2 = -dx2; signx2 = -1; } + else signx2 = 1; + dy2 = (int)(y3 - y1); + + if (dy1 > dx1) { std::swap(dx1, dy1); changed1 = true; } + if (dy2 > dx2) { std::swap(dy2, dx2); changed2 = true; } + + e2 = (int)(dx2 >> 1); + // Flat top, just process the second half + if (y1 == y2) goto next; + e1 = (int)(dx1 >> 1); + + for (int i = 0; i < dx1;) { + t1xp = 0; t2xp = 0; + if (t1x < t2x) { minx = t1x; maxx = t2x; } + else { minx = t2x; maxx = t1x; } + // process first line until y value is about to change + while (i < dx1) { + i++; + e1 += dy1; + while (e1 >= dx1) { + e1 -= dx1; + if (changed1) t1xp = signx1;//t1x += signx1; + else goto next1; + } + if (changed1) break; + else t1x += signx1; + } + // Move line + next1: + // process second line until y value is about to change + while (1) { + e2 += dy2; + while (e2 >= dx2) { + e2 -= dx2; + if (changed2) t2xp = signx2;//t2x += signx2; + else goto next2; + } + if (changed2) break; + else t2x += signx2; + } + next2: + if (minx > t1x) minx = t1x; + if (minx > t2x) minx = t2x; + if (maxx < t1x) maxx = t1x; + if (maxx < t2x) maxx = t2x; + drawline(minx, maxx, y); // Draw line from min to max points found on the y + // Now increase y + if (!changed1) t1x += signx1; + t1x += t1xp; + if (!changed2) t2x += signx2; + t2x += t2xp; + y += 1; + if (y == y2) break; + } + next: + // Second half + dx1 = (int)(x3 - x2); if (dx1 < 0) { dx1 = -dx1; signx1 = -1; } + else signx1 = 1; + dy1 = (int)(y3 - y2); + t1x = x2; + + if (dy1 > dx1) { // swap values + std::swap(dy1, dx1); + changed1 = true; + } + else changed1 = false; + + e1 = (int)(dx1 >> 1); + + for (int i = 0; i <= dx1; i++) { + t1xp = 0; t2xp = 0; + if (t1x < t2x) { minx = t1x; maxx = t2x; } + else { minx = t2x; maxx = t1x; } + // process first line until y value is about to change + while (i < dx1) { + e1 += dy1; + while (e1 >= dx1) { + e1 -= dx1; + if (changed1) { t1xp = signx1; break; }//t1x += signx1; + else goto next3; + } + if (changed1) break; + else t1x += signx1; + if (i < dx1) i++; + } + next3: + // process second line until y value is about to change + while (t2x != x3) { + e2 += dy2; + while (e2 >= dx2) { + e2 -= dx2; + if (changed2) t2xp = signx2; + else goto next4; + } + if (changed2) break; + else t2x += signx2; + } + next4: + + if (minx > t1x) minx = t1x; + if (minx > t2x) minx = t2x; + if (maxx < t1x) maxx = t1x; + if (maxx < t2x) maxx = t2x; + drawline(minx, maxx, y); + if (!changed1) t1x += signx1; + t1x += t1xp; + if (!changed2) t2x += signx2; + t2x += t2xp; + y += 1; + if (y > y3) return; + } + } + + void PixelGameEngine::DrawSprite(const olc::vi2d& pos, Sprite* sprite, uint32_t scale, uint8_t flip) + { DrawSprite(pos.x, pos.y, sprite, scale, flip); } + + void PixelGameEngine::DrawSprite(int32_t x, int32_t y, Sprite* sprite, uint32_t scale, uint8_t flip) + { + if (sprite == nullptr) + return; + + int32_t fxs = 0, fxm = 1, fx = 0; + int32_t fys = 0, fym = 1, fy = 0; + if (flip & olc::Sprite::Flip::HORIZ) { fxs = sprite->width - 1; fxm = -1; } + if (flip & olc::Sprite::Flip::VERT) { fys = sprite->height - 1; fym = -1; } + + if (scale > 1) + { + fx = fxs; + for (int32_t i = 0; i < sprite->width; i++, fx += fxm) + { + fy = fys; + for (int32_t j = 0; j < sprite->height; j++, fy += fym) + for (uint32_t is = 0; is < scale; is++) + for (uint32_t js = 0; js < scale; js++) + Draw(x + (i * scale) + is, y + (j * scale) + js, sprite->GetPixel(fx, fy)); + } + } + else + { + fx = fxs; + for (int32_t i = 0; i < sprite->width; i++, fx += fxm) + { + fy = fys; + for (int32_t j = 0; j < sprite->height; j++, fy += fym) + Draw(x + i, y + j, sprite->GetPixel(fx, fy)); + } + } + } + + void PixelGameEngine::DrawPartialSprite(const olc::vi2d& pos, Sprite* sprite, const olc::vi2d& sourcepos, const olc::vi2d& size, uint32_t scale, uint8_t flip) + { DrawPartialSprite(pos.x, pos.y, sprite, sourcepos.x, sourcepos.y, size.x, size.y, scale, flip); } + + void PixelGameEngine::DrawPartialSprite(int32_t x, int32_t y, Sprite* sprite, int32_t ox, int32_t oy, int32_t w, int32_t h, uint32_t scale, uint8_t flip) + { + if (sprite == nullptr) + return; + + int32_t fxs = 0, fxm = 1, fx = 0; + int32_t fys = 0, fym = 1, fy = 0; + if (flip & olc::Sprite::Flip::HORIZ) { fxs = w - 1; fxm = -1; } + if (flip & olc::Sprite::Flip::VERT) { fys = h - 1; fym = -1; } + + if (scale > 1) + { + fx = fxs; + for (int32_t i = 0; i < w; i++, fx += fxm) + { + fy = fys; + for (int32_t j = 0; j < h; j++, fy += fym) + for (uint32_t is = 0; is < scale; is++) + for (uint32_t js = 0; js < scale; js++) + Draw(x + (i * scale) + is, y + (j * scale) + js, sprite->GetPixel(fx + ox, fy + oy)); + } + } + else + { + fx = fxs; + for (int32_t i = 0; i < w; i++, fx += fxm) + { + fy = fys; + for (int32_t j = 0; j < h; j++, fy += fym) + Draw(x + i, y + j, sprite->GetPixel(fx + ox, fy + oy)); + } + } + } + + void PixelGameEngine::SetDecalMode(const olc::DecalMode& mode) + { nDecalMode = mode; } + + void PixelGameEngine::SetDecalStructure(const olc::DecalStructure& structure) + { nDecalStructure = structure; } + + void PixelGameEngine::DrawPartialDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint) + { + olc::vf2d vScreenSpacePos = + { + (pos.x * vInvScreenSize.x) * 2.0f - 1.0f, + -((pos.y * vInvScreenSize.y) * 2.0f - 1.0f) + }; + + + olc::vf2d vScreenSpaceDim = + { + ((pos.x + source_size.x * scale.x) * vInvScreenSize.x) * 2.0f - 1.0f, + -(((pos.y + source_size.y * scale.y) * vInvScreenSize.y) * 2.0f - 1.0f) + }; + + olc::vf2d vWindow = olc::vf2d(vViewSize); + olc::vf2d vQuantisedPos = ((vScreenSpacePos * vWindow) + olc::vf2d(0.5f, 0.5f)).floor() / vWindow; + olc::vf2d vQuantisedDim = ((vScreenSpaceDim * vWindow) + olc::vf2d(0.5f, -0.5f)).ceil() / vWindow; + + DecalInstance di; + di.points = 4; + di.decal = decal; + di.tint = { tint, tint, tint, tint }; + di.pos = { { vQuantisedPos.x, vQuantisedPos.y }, { vQuantisedPos.x, vQuantisedDim.y }, { vQuantisedDim.x, vQuantisedDim.y }, { vQuantisedDim.x, vQuantisedPos.y } }; + olc::vf2d uvtl = (source_pos + olc::vf2d(0.0001f, 0.0001f)) * decal->vUVScale; + olc::vf2d uvbr = (source_pos + source_size - olc::vf2d(0.0001f, 0.0001f)) * decal->vUVScale; + di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; + di.w = { 1,1,1,1 }; + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::DrawPartialDecal(const olc::vf2d& pos, const olc::vf2d& size, olc::Decal* decal, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { + olc::vf2d vScreenSpacePos = + { + (pos.x * vInvScreenSize.x) * 2.0f - 1.0f, + ((pos.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f + }; + + olc::vf2d vScreenSpaceDim = + { + vScreenSpacePos.x + (2.0f * size.x * vInvScreenSize.x), + vScreenSpacePos.y - (2.0f * size.y * vInvScreenSize.y) + }; + + DecalInstance di; + di.points = 4; + di.decal = decal; + di.tint = { tint, tint, tint, tint }; + di.pos = { { vScreenSpacePos.x, vScreenSpacePos.y }, { vScreenSpacePos.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpacePos.y } }; + olc::vf2d uvtl = (source_pos) * decal->vUVScale; + olc::vf2d uvbr = uvtl + ((source_size) * decal->vUVScale); + di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; + di.w = { 1,1,1,1 }; + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + + void PixelGameEngine::DrawDecal(const olc::vf2d& pos, olc::Decal* decal, const olc::vf2d& scale, const olc::Pixel& tint) + { + olc::vf2d vScreenSpacePos = + { + (pos.x * vInvScreenSize.x) * 2.0f - 1.0f, + ((pos.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f + }; + + olc::vf2d vScreenSpaceDim = + { + vScreenSpacePos.x + (2.0f * (float(decal->sprite->width) * vInvScreenSize.x)) * scale.x, + vScreenSpacePos.y - (2.0f * (float(decal->sprite->height) * vInvScreenSize.y)) * scale.y + }; + + DecalInstance di; + di.decal = decal; + di.points = 4; + di.tint = { tint, tint, tint, tint }; + di.pos = { { vScreenSpacePos.x, vScreenSpacePos.y }, { vScreenSpacePos.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpaceDim.y }, { vScreenSpaceDim.x, vScreenSpacePos.y } }; + di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; + di.w = { 1, 1, 1, 1 }; + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::DrawExplicitDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d* uv, const olc::Pixel* col, uint32_t elements) + { + DecalInstance di; + di.decal = decal; + di.pos.resize(elements); + di.uv.resize(elements); + di.w.resize(elements); + di.tint.resize(elements); + di.points = elements; + for (uint32_t i = 0; i < elements; i++) + { + di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + di.uv[i] = uv[i]; + di.tint[i] = col[i]; + di.w[i] = 1.0f; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const olc::Pixel tint) + { + DecalInstance di; + di.decal = decal; + di.points = uint32_t(pos.size()); + di.pos.resize(di.points); + di.uv.resize(di.points); + di.w.resize(di.points); + di.tint.resize(di.points); + for (uint32_t i = 0; i < di.points; i++) + { + di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + di.uv[i] = uv[i]; + di.tint[i] = tint; + di.w[i] = 1.0f; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& uv, const std::vector &tint) + { + DecalInstance di; + di.decal = decal; + di.points = uint32_t(pos.size()); + di.pos.resize(di.points); + di.uv.resize(di.points); + di.w.resize(di.points); + di.tint.resize(di.points); + for (uint32_t i = 0; i < di.points; i++) + { + di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + di.uv[i] = uv[i]; + di.tint[i] = tint[i]; + di.w[i] = 1.0f; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::DrawPolygonDecal(olc::Decal* decal, const std::vector& pos, const std::vector& depth, const std::vector& uv, const olc::Pixel tint) + { + DecalInstance di; + di.decal = decal; + di.points = uint32_t(pos.size()); + di.pos.resize(di.points); + di.uv.resize(di.points); + di.w.resize(di.points); + di.tint.resize(di.points); + for (uint32_t i = 0; i < di.points; i++) + { + di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + di.uv[i] = uv[i]; + di.tint[i] = tint; + di.w[i] = 1.0f; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + +#ifdef OLC_ENABLE_EXPERIMENTAL + // Lightweight 3D + void PixelGameEngine::LW3D_DrawTriangles(olc::Decal* decal, const std::vector>& pos, const std::vector& tex, const std::vector& col) + { + DecalInstance di; + di.decal = decal; + di.points = uint32_t(pos.size()); + di.pos.resize(di.points); + di.uv.resize(di.points); + di.w.resize(di.points); + di.tint.resize(di.points); + for (uint32_t i = 0; i < di.points; i++) + { + di.pos[i] = { pos[i][0], pos[i][1] }; + di.w[i] = pos[i][2]; + di.uv[i] = tex[i]; + di.tint[i] = col[i]; + } + di.mode = DecalMode::MODEL3D; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } +#endif + + void PixelGameEngine::DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p) + { + DecalInstance di; + di.decal = nullptr; + di.points = uint32_t(2); + di.pos.resize(di.points); + di.uv.resize(di.points); + di.w.resize(di.points); + di.tint.resize(di.points); + di.pos[0] = { (pos1.x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos1.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + di.uv[0] = { 0.0f, 0.0f }; + di.tint[0] = p; + di.w[0] = 1.0f; + di.pos[1] = { (pos2.x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos2.y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + di.uv[1] = { 0.0f, 0.0f }; + di.tint[1] = p; + di.w[1] = 1.0f; + di.mode = olc::DecalMode::WIREFRAME; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) + { + olc::vf2d vNewSize = (size - olc::vf2d(0.375f, 0.375f)).ceil(); + std::array points = { { {pos}, {pos.x, pos.y + vNewSize.y}, {pos + vNewSize}, {pos.x + vNewSize.x, pos.y} } }; + std::array uvs = { {{0,0},{0,0},{0,0},{0,0}} }; + std::array cols = { {col, col, col, col} }; + DrawExplicitDecal(nullptr, points.data(), uvs.data(), cols.data(), 4); + } + + void PixelGameEngine::GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR) + { + std::array points = { { {pos}, {pos.x, pos.y + size.y}, {pos + size}, {pos.x + size.x, pos.y} } }; + std::array uvs = { {{0,0},{0,0},{0,0},{0,0}} }; + std::array cols = { {colTL, colBL, colBR, colTR} }; + DrawExplicitDecal(nullptr, points.data(), uvs.data(), cols.data(), 4); + } + + void PixelGameEngine::DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& scale, const olc::Pixel& tint) + { + DecalInstance di; + di.decal = decal; + di.pos.resize(4); + di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; + di.w = { 1, 1, 1, 1 }; + di.tint = { tint, tint, tint, tint }; + di.points = 4; + di.pos[0] = (olc::vf2d(0.0f, 0.0f) - center) * scale; + di.pos[1] = (olc::vf2d(0.0f, float(decal->sprite->height)) - center) * scale; + di.pos[2] = (olc::vf2d(float(decal->sprite->width), float(decal->sprite->height)) - center) * scale; + di.pos[3] = (olc::vf2d(float(decal->sprite->width), 0.0f) - center) * scale; + float c = cos(fAngle), s = sin(fAngle); + for (int i = 0; i < 4; i++) + { + di.pos[i] = pos + olc::vf2d(di.pos[i].x * c - di.pos[i].y * s, di.pos[i].x * s + di.pos[i].y * c); + di.pos[i] = di.pos[i] * vInvScreenSize * 2.0f - olc::vf2d(1.0f, 1.0f); + di.pos[i].y *= -1.0f; + di.w[i] = 1; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + + void PixelGameEngine::DrawPartialRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::vf2d& scale, const olc::Pixel& tint) + { + DecalInstance di; + di.decal = decal; + di.points = 4; + di.tint = { tint, tint, tint, tint }; + di.w = { 1, 1, 1, 1 }; + di.pos.resize(4); + di.pos[0] = (olc::vf2d(0.0f, 0.0f) - center) * scale; + di.pos[1] = (olc::vf2d(0.0f, source_size.y) - center) * scale; + di.pos[2] = (olc::vf2d(source_size.x, source_size.y) - center) * scale; + di.pos[3] = (olc::vf2d(source_size.x, 0.0f) - center) * scale; + float c = cos(fAngle), s = sin(fAngle); + for (int i = 0; i < 4; i++) + { + di.pos[i] = pos + olc::vf2d(di.pos[i].x * c - di.pos[i].y * s, di.pos[i].x * s + di.pos[i].y * c); + di.pos[i] = di.pos[i] * vInvScreenSize * 2.0f - olc::vf2d(1.0f, 1.0f); + di.pos[i].y *= -1.0f; + } + + olc::vf2d uvtl = source_pos * decal->vUVScale; + olc::vf2d uvbr = uvtl + (source_size * decal->vUVScale); + di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + + void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { + DecalInstance di; + di.points = 4; + di.decal = decal; + di.tint = { tint, tint, tint, tint }; + di.w = { 1, 1, 1, 1 }; + di.pos.resize(4); + di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; + olc::vf2d center; + float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); + if (rd != 0) + { + olc::vf2d uvtl = source_pos * decal->vUVScale; + olc::vf2d uvbr = uvtl + (source_size * decal->vUVScale); + di.uv = { { uvtl.x, uvtl.y }, { uvtl.x, uvbr.y }, { uvbr.x, uvbr.y }, { uvbr.x, uvtl.y } }; + + rd = 1.0f / rd; + float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; + float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; + if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); + float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); + for (int i = 0; i < 4; i++) + { + float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; + di.uv[i] *= q; di.w[i] *= q; + di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + } + + void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d* pos, const olc::Pixel& tint) + { + // Thanks Nathan Reed, a brilliant article explaining whats going on here + // http://www.reedbeta.com/blog/quadrilateral-interpolation-part-1/ + DecalInstance di; + di.points = 4; + di.decal = decal; + di.tint = { tint, tint, tint, tint }; + di.w = { 1, 1, 1, 1 }; + di.pos.resize(4); + di.uv = { { 0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f} }; + olc::vf2d center; + float rd = ((pos[2].x - pos[0].x) * (pos[3].y - pos[1].y) - (pos[3].x - pos[1].x) * (pos[2].y - pos[0].y)); + if (rd != 0) + { + rd = 1.0f / rd; + float rn = ((pos[3].x - pos[1].x) * (pos[0].y - pos[1].y) - (pos[3].y - pos[1].y) * (pos[0].x - pos[1].x)) * rd; + float sn = ((pos[2].x - pos[0].x) * (pos[0].y - pos[1].y) - (pos[2].y - pos[0].y) * (pos[0].x - pos[1].x)) * rd; + if (!(rn < 0.f || rn > 1.f || sn < 0.f || sn > 1.f)) center = pos[0] + rn * (pos[2] - pos[0]); + float d[4]; for (int i = 0; i < 4; i++) d[i] = (pos[i] - center).mag(); + for (int i = 0; i < 4; i++) + { + float q = d[i] == 0.0f ? 1.0f : (d[i] + d[(i + 2) & 3]) / d[(i + 2) & 3]; + di.uv[i] *= q; di.w[i] *= q; + di.pos[i] = { (pos[i].x * vInvScreenSize.x) * 2.0f - 1.0f, ((pos[i].y * vInvScreenSize.y) * 2.0f - 1.0f) * -1.0f }; + } + di.mode = nDecalMode; + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di); + } + } + + void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::Pixel& tint) + { DrawWarpedDecal(decal, pos.data(), tint); } + + void PixelGameEngine::DrawWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::Pixel& tint) + { DrawWarpedDecal(decal, &pos[0], tint); } + + void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const std::array& pos, const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { DrawPartialWarpedDecal(decal, pos.data(), source_pos, source_size, tint); } + + void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) + { DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint); } + + void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale) + { + olc::vf2d spos = { 0.0f, 0.0f }; + for (auto c : sText) + { + if (c == '\n') + { + spos.x = 0; spos.y += 8.0f * scale.y; + } + else if (c == '\t') + { + spos.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + DrawPartialDecal(pos + spos, fontRenderable.Decal(), {float(ox) * 8.0f, float(oy) * 8.0f}, {8.0f, 8.0f}, scale, col); + spos.x += 8.0f * scale.x; + } + } + } + + void PixelGameEngine::DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col, const olc::vf2d& scale) + { + olc::vf2d spos = { 0.0f, 0.0f }; + for (auto c : sText) + { + if (c == '\n') + { + spos.x = 0; spos.y += 8.0f * scale.y; + } + else if (c == '\t') + { + spos.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + DrawPartialDecal(pos + spos, fontRenderable.Decal(), { float(ox) * 8.0f + float(vFontSpacing[c - 32].x), float(oy) * 8.0f }, { float(vFontSpacing[c - 32].y), 8.0f }, scale, col); + spos.x += float(vFontSpacing[c - 32].y) * scale.x; + } + } + } + // Thanks Oso-Grande/Sopadeoso For these awesom and stupidly clever Text Rotation routines... duh XD + void PixelGameEngine::DrawRotatedStringDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center, const Pixel col, const olc::vf2d& scale) + { + olc::vf2d spos = center; + for (auto c : sText) + { + if (c == '\n') + { + spos.x = center.x; spos.y -= 8.0f; + } + else if (c == '\t') + { + spos.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + DrawPartialRotatedDecal(pos, fontRenderable.Decal(), fAngle, spos, { float(ox) * 8.0f, float(oy) * 8.0f }, { 8.0f, 8.0f }, scale, col); + spos.x -= 8.0f; + } + } + } + + void PixelGameEngine::DrawRotatedStringPropDecal(const olc::vf2d& pos, const std::string& sText, const float fAngle, const olc::vf2d& center, const Pixel col, const olc::vf2d& scale) + { + olc::vf2d spos = center; + for (auto c : sText) + { + if (c == '\n') + { + spos.x = center.x; spos.y -= 8.0f; + } + else if (c == '\t') + { + spos.x += 8.0f * float(nTabSizeInSpaces) * scale.x; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + DrawPartialRotatedDecal(pos, fontRenderable.Decal(), fAngle, spos, { float(ox) * 8.0f + float(vFontSpacing[c - 32].x), float(oy) * 8.0f }, { float(vFontSpacing[c - 32].y), 8.0f }, scale, col); + spos.x -= float(vFontSpacing[c - 32].y); + } + } + } + + olc::vi2d PixelGameEngine::GetTextSize(const std::string& s) + { + olc::vi2d size = { 0,1 }; + olc::vi2d pos = { 0,1 }; + for (auto c : s) + { + if (c == '\n') { pos.y++; pos.x = 0; } + else if (c == '\t') { pos.x += nTabSizeInSpaces; } + else pos.x++; + size.x = std::max(size.x, pos.x); + size.y = std::max(size.y, pos.y); + } + return size * 8; + } + + void PixelGameEngine::DrawString(const olc::vi2d& pos, const std::string& sText, Pixel col, uint32_t scale) + { DrawString(pos.x, pos.y, sText, col, scale); } + + void PixelGameEngine::DrawString(int32_t x, int32_t y, const std::string& sText, Pixel col, uint32_t scale) + { + int32_t sx = 0; + int32_t sy = 0; + Pixel::Mode m = nPixelMode; + // Thanks @tucna, spotted bug with col.ALPHA :P + if (m != Pixel::CUSTOM) // Thanks @Megarev, required for "shaders" + { + if (col.a != 255) SetPixelMode(Pixel::ALPHA); + else SetPixelMode(Pixel::MASK); + } + for (auto c : sText) + { + if (c == '\n') + { + sx = 0; sy += 8 * scale; + } + else if (c == '\t') + { + sx += 8 * nTabSizeInSpaces * scale; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + + if (scale > 1) + { + for (uint32_t i = 0; i < 8; i++) + for (uint32_t j = 0; j < 8; j++) + if (fontRenderable.Sprite()->GetPixel(i + ox * 8, j + oy * 8).r > 0) + for (uint32_t is = 0; is < scale; is++) + for (uint32_t js = 0; js < scale; js++) + Draw(x + sx + (i * scale) + is, y + sy + (j * scale) + js, col); + } + else + { + for (uint32_t i = 0; i < 8; i++) + for (uint32_t j = 0; j < 8; j++) + if (fontRenderable.Sprite()->GetPixel(i + ox * 8, j + oy * 8).r > 0) + Draw(x + sx + i, y + sy + j, col); + } + sx += 8 * scale; + } + } + SetPixelMode(m); + } + + olc::vi2d PixelGameEngine::GetTextSizeProp(const std::string& s) + { + olc::vi2d size = { 0,1 }; + olc::vi2d pos = { 0,1 }; + for (auto c : s) + { + if (c == '\n') { pos.y += 1; pos.x = 0; } + else if (c == '\t') { pos.x += nTabSizeInSpaces * 8; } + else pos.x += vFontSpacing[c - 32].y; + size.x = std::max(size.x, pos.x); + size.y = std::max(size.y, pos.y); + } + + size.y *= 8; + return size; + } + + void PixelGameEngine::DrawStringProp(const olc::vi2d& pos, const std::string& sText, Pixel col, uint32_t scale) + { DrawStringProp(pos.x, pos.y, sText, col, scale); } + + void PixelGameEngine::DrawStringProp(int32_t x, int32_t y, const std::string& sText, Pixel col, uint32_t scale) + { + int32_t sx = 0; + int32_t sy = 0; + Pixel::Mode m = nPixelMode; + + if (m != Pixel::CUSTOM) + { + if (col.a != 255) SetPixelMode(Pixel::ALPHA); + else SetPixelMode(Pixel::MASK); + } + for (auto c : sText) + { + if (c == '\n') + { + sx = 0; sy += 8 * scale; + } + else if (c == '\t') + { + sx += 8 * nTabSizeInSpaces * scale; + } + else + { + int32_t ox = (c - 32) % 16; + int32_t oy = (c - 32) / 16; + + if (scale > 1) + { + for (int32_t i = 0; i < vFontSpacing[c - 32].y; i++) + for (int32_t j = 0; j < 8; j++) + if (fontRenderable.Sprite()->GetPixel(i + ox * 8 + vFontSpacing[c - 32].x, j + oy * 8).r > 0) + for (int32_t is = 0; is < int(scale); is++) + for (int32_t js = 0; js < int(scale); js++) + Draw(x + sx + (i * scale) + is, y + sy + (j * scale) + js, col); + } + else + { + for (int32_t i = 0; i < vFontSpacing[c - 32].y; i++) + for (int32_t j = 0; j < 8; j++) + if (fontRenderable.Sprite()->GetPixel(i + ox * 8 + vFontSpacing[c - 32].x, j + oy * 8).r > 0) + Draw(x + sx + i, y + sy + j, col); + } + sx += vFontSpacing[c - 32].y * scale; + } + } + SetPixelMode(m); + } + + void PixelGameEngine::SetPixelMode(Pixel::Mode m) + { nPixelMode = m; } + + Pixel::Mode PixelGameEngine::GetPixelMode() + { return nPixelMode; } + + void PixelGameEngine::SetPixelMode(std::function pixelMode) + { + funcPixelMode = pixelMode; + nPixelMode = Pixel::Mode::CUSTOM; + } + + void PixelGameEngine::SetPixelBlend(float fBlend) + { + fBlendFactor = fBlend; + if (fBlendFactor < 0.0f) fBlendFactor = 0.0f; + if (fBlendFactor > 1.0f) fBlendFactor = 1.0f; + } + + std::stringstream& PixelGameEngine::ConsoleOut() + { return ssConsoleOutput; } + + bool PixelGameEngine::IsConsoleShowing() const + { return bConsoleShow; } + + void PixelGameEngine::ConsoleShow(const olc::Key& keyExit, bool bSuspendTime) + { + if (bConsoleShow) + return; + + bConsoleShow = true; + bConsoleSuspendTime = bSuspendTime; + TextEntryEnable(true); + keyConsoleExit = keyExit; + pKeyboardState[keyConsoleExit].bHeld = false; + pKeyboardState[keyConsoleExit].bPressed = false; + pKeyboardState[keyConsoleExit].bReleased = true; + } + + void PixelGameEngine::ConsoleClear() + { sConsoleLines.clear(); } + + void PixelGameEngine::ConsoleCaptureStdOut(const bool bCapture) + { + if(bCapture) + sbufOldCout = std::cout.rdbuf(ssConsoleOutput.rdbuf()); + else + std::cout.rdbuf(sbufOldCout); + } + + void PixelGameEngine::UpdateConsole() + { + if (GetKey(keyConsoleExit).bPressed) + { + TextEntryEnable(false); + bConsoleSuspendTime = false; + bConsoleShow = false; + return; + } + + // Keep Console sizes based in real screen dimensions + vConsoleCharacterScale = olc::vf2d(1.0f, 2.0f) / (olc::vf2d(vViewSize) * vInvScreenSize); + vConsoleSize = (vViewSize / olc::vi2d(8, 16)) - olc::vi2d(2, 4); + + // If console has changed size, simply reset it + if (vConsoleSize.y != sConsoleLines.size()) + { + vConsoleCursor = { 0,0 }; + sConsoleLines.clear(); + sConsoleLines.resize(vConsoleSize.y); + } + + auto TypeCharacter = [&](const char c) + { + if (c >= 32 && c < 127) + { + sConsoleLines[vConsoleCursor.y].append(1, c); + vConsoleCursor.x++; + } + + if( c == '\n' || vConsoleCursor.x >= vConsoleSize.x) + { + vConsoleCursor.y++; vConsoleCursor.x = 0; + } + + if (vConsoleCursor.y >= vConsoleSize.y) + { + vConsoleCursor.y = vConsoleSize.y - 1; + for (size_t i = 1; i < vConsoleSize.y; i++) + sConsoleLines[i - 1] = sConsoleLines[i]; + sConsoleLines[vConsoleCursor.y].clear(); + } + }; + + // Empty out "std::cout", parsing as we go + while (ssConsoleOutput.rdbuf()->sgetc() != -1) + { + char c = ssConsoleOutput.rdbuf()->sbumpc(); + TypeCharacter(c); + } + + // Draw Shadow + GradientFillRectDecal({ 0,0 }, olc::vf2d(vScreenSize), olc::PixelF(0, 0, 0.5f, 0.5f), olc::PixelF(0, 0, 0.25f, 0.5f), olc::PixelF(0, 0, 0.25f, 0.5f), olc::PixelF(0, 0, 0.25f, 0.5f)); + + // Draw the console buffer + SetDecalMode(olc::DecalMode::NORMAL); + for (int32_t nLine = 0; nLine < vConsoleSize.y; nLine++) + DrawStringDecal(olc::vf2d( 1, 1 + float(nLine) ) * vConsoleCharacterScale * 8.0f, sConsoleLines[nLine], olc::WHITE, vConsoleCharacterScale); + + // Draw Input State + FillRectDecal(olc::vf2d(1 + float((TextEntryGetCursor() + 1)), 1 + float((vConsoleSize.y - 1))) * vConsoleCharacterScale * 8.0f, olc::vf2d(8, 8) * vConsoleCharacterScale, olc::DARK_CYAN); + DrawStringDecal(olc::vf2d(1, 1 + float((vConsoleSize.y - 1))) * vConsoleCharacterScale * 8.0f, std::string(">") + TextEntryGetString(), olc::YELLOW, vConsoleCharacterScale); + } + + + + void PixelGameEngine::TextEntryEnable(const bool bEnable, const std::string& sText) + { + if (bEnable) + { + nTextEntryCursor = int32_t(sText.size()); + sTextEntryString = sText; + bTextEntryEnable = true; + } + else + { + bTextEntryEnable = false; + } + } + + std::string PixelGameEngine::TextEntryGetString() const + { return sTextEntryString; } + + int32_t PixelGameEngine::TextEntryGetCursor() const + { return nTextEntryCursor; } + + bool PixelGameEngine::IsTextEntryEnabled() const + { return bTextEntryEnable; } + + + void PixelGameEngine::UpdateTextEntry() + { + // Check for typed characters + for (const auto& key : vKeyboardMap) + if (GetKey(std::get<0>(key)).bPressed) + { + sTextEntryString.insert(nTextEntryCursor, GetKey(olc::Key::SHIFT).bHeld ? std::get<2>(key) : std::get<1>(key)); + nTextEntryCursor++; + } + + // Check for command characters + if (GetKey(olc::Key::LEFT).bPressed) + nTextEntryCursor = std::max(0, nTextEntryCursor - 1); + if (GetKey(olc::Key::RIGHT).bPressed) + nTextEntryCursor = std::min(int32_t(sTextEntryString.size()), nTextEntryCursor + 1); + if (GetKey(olc::Key::BACK).bPressed && nTextEntryCursor > 0) + { + sTextEntryString.erase(nTextEntryCursor-1, 1); + nTextEntryCursor = std::max(0, nTextEntryCursor - 1); + } + if (GetKey(olc::Key::DEL).bPressed && nTextEntryCursor < sTextEntryString.size()) + sTextEntryString.erase(nTextEntryCursor, 1); + + if (GetKey(olc::Key::UP).bPressed) + { + if (!sCommandHistory.empty()) + { + if (sCommandHistoryIt != sCommandHistory.begin()) + sCommandHistoryIt--; + + nTextEntryCursor = int32_t(sCommandHistoryIt->size()); + sTextEntryString = *sCommandHistoryIt; + } + } + + if (GetKey(olc::Key::DOWN).bPressed) + { + if (!sCommandHistory.empty()) + { + if (sCommandHistoryIt != sCommandHistory.end()) + { + sCommandHistoryIt++; + if (sCommandHistoryIt != sCommandHistory.end()) + { + nTextEntryCursor = int32_t(sCommandHistoryIt->size()); + sTextEntryString = *sCommandHistoryIt; + } + else + { + nTextEntryCursor = 0; + sTextEntryString = ""; + } + } + } + } + + if (GetKey(olc::Key::ENTER).bPressed) + { + if (bConsoleShow) + { + std::cout << ">" + sTextEntryString + "\n"; + if (OnConsoleCommand(sTextEntryString)) + { + sCommandHistory.push_back(sTextEntryString); + sCommandHistoryIt = sCommandHistory.end(); + } + sTextEntryString.clear(); + nTextEntryCursor = 0; + } + else + { + OnTextEntryComplete(sTextEntryString); + TextEntryEnable(false); + } + } + } + + // User must override these functions as required. I have not made + // them abstract because I do need a default behaviour to occur if + // they are not overwritten + + bool PixelGameEngine::OnUserCreate() + { return false; } + + bool PixelGameEngine::OnUserUpdate(float fElapsedTime) + { UNUSED(fElapsedTime); return false; } + + bool PixelGameEngine::OnUserDestroy() + { return true; } + + void PixelGameEngine::OnTextEntryComplete(const std::string& sText) { UNUSED(sText); } + bool PixelGameEngine::OnConsoleCommand(const std::string& sCommand) { UNUSED(sCommand); return false; } + + + // Externalised API + void PixelGameEngine::olc_UpdateViewport() + { + int32_t ww = vScreenSize.x * vPixelSize.x; + int32_t wh = vScreenSize.y * vPixelSize.y; + float wasp = (float)ww / (float)wh; + + if (bPixelCohesion) + { + vScreenPixelSize = (vWindowSize / vScreenSize); + vViewSize = (vWindowSize / vScreenSize) * vScreenSize; + } + else + { + vViewSize.x = (int32_t)vWindowSize.x; + vViewSize.y = (int32_t)((float)vViewSize.x / wasp); + + if (vViewSize.y > vWindowSize.y) + { + vViewSize.y = vWindowSize.y; + vViewSize.x = (int32_t)((float)vViewSize.y * wasp); + } + } + + vViewPos = (vWindowSize - vViewSize) / 2; + } + + void PixelGameEngine::olc_UpdateWindowSize(int32_t x, int32_t y) + { + vWindowSize = { x, y }; + olc_UpdateViewport(); + } + + void PixelGameEngine::olc_UpdateMouseWheel(int32_t delta) + { nMouseWheelDeltaCache += delta; } + + void PixelGameEngine::olc_UpdateMouse(int32_t x, int32_t y) + { + // Mouse coords come in screen space + // But leave in pixel space + bHasMouseFocus = true; + vMouseWindowPos = { x, y }; + // Full Screen mode may have a weird viewport we must clamp to + x -= vViewPos.x; + y -= vViewPos.y; + vMousePosCache.x = (int32_t)(((float)x / (float)(vWindowSize.x - (vViewPos.x * 2)) * (float)vScreenSize.x)); + vMousePosCache.y = (int32_t)(((float)y / (float)(vWindowSize.y - (vViewPos.y * 2)) * (float)vScreenSize.y)); + if (vMousePosCache.x >= (int32_t)vScreenSize.x) vMousePosCache.x = vScreenSize.x - 1; + if (vMousePosCache.y >= (int32_t)vScreenSize.y) vMousePosCache.y = vScreenSize.y - 1; + if (vMousePosCache.x < 0) vMousePosCache.x = 0; + if (vMousePosCache.y < 0) vMousePosCache.y = 0; + } + + void PixelGameEngine::olc_UpdateMouseState(int32_t button, bool state) + { pMouseNewState[button] = state; } + + void PixelGameEngine::olc_UpdateKeyState(int32_t key, bool state) + { pKeyNewState[key] = state; } + + void PixelGameEngine::olc_UpdateMouseFocus(bool state) + { bHasMouseFocus = state; } + + void PixelGameEngine::olc_UpdateKeyFocus(bool state) + { bHasInputFocus = state; } + + void PixelGameEngine::olc_Reanimate() + { bAtomActive = true; } + + bool PixelGameEngine::olc_IsRunning() + { return bAtomActive; } + + void PixelGameEngine::olc_Terminate() + { bAtomActive = false; } + + void PixelGameEngine::EngineThread() + { + // Allow platform to do stuff here if needed, since its now in the + // context of this thread + if (platform->ThreadStartUp() == olc::FAIL) return; + + // Do engine context specific initialisation + olc_PrepareEngine(); + + // Create user resources as part of this thread + for (auto& ext : vExtensions) ext->OnBeforeUserCreate(); + if (!OnUserCreate()) bAtomActive = false; + for (auto& ext : vExtensions) ext->OnAfterUserCreate(); + + while (bAtomActive) + { + // Run as fast as possible + while (bAtomActive) { olc_CoreUpdate(); } + + // Allow the user to free resources if they have overrided the destroy function + if (!OnUserDestroy()) + { + // User denied destroy for some reason, so continue running + bAtomActive = true; + } + } + + platform->ThreadCleanUp(); + } + + void PixelGameEngine::olc_PrepareEngine() + { + // Start OpenGL, the context is owned by the game thread + if (platform->CreateGraphics(bFullScreen, bEnableVSYNC, vViewPos, vViewSize) == olc::FAIL) return; + + // Construct default font sheet + olc_ConstructFontSheet(); + + // Create Primary Layer "0" + CreateLayer(); + vLayers[0].bUpdate = true; + vLayers[0].bShow = true; + SetDrawTarget(nullptr); + + m_tp1 = std::chrono::system_clock::now(); + m_tp2 = std::chrono::system_clock::now(); + } + + + void PixelGameEngine::olc_CoreUpdate() + { + // Handle Timing + m_tp2 = std::chrono::system_clock::now(); + std::chrono::duration elapsedTime = m_tp2 - m_tp1; + m_tp1 = m_tp2; + + // Our time per frame coefficient + float fElapsedTime = elapsedTime.count(); + fLastElapsed = fElapsedTime; + + if (bConsoleSuspendTime) + fElapsedTime = 0.0f; + + // Some platforms will need to check for events + platform->HandleSystemEvent(); + + // Compare hardware input states from previous frame + auto ScanHardware = [&](HWButton* pKeys, bool* pStateOld, bool* pStateNew, uint32_t nKeyCount) + { + for (uint32_t i = 0; i < nKeyCount; i++) + { + pKeys[i].bPressed = false; + pKeys[i].bReleased = false; + if (pStateNew[i] != pStateOld[i]) + { + if (pStateNew[i]) + { + pKeys[i].bPressed = !pKeys[i].bHeld; + pKeys[i].bHeld = true; + } + else + { + pKeys[i].bReleased = true; + pKeys[i].bHeld = false; + } + } + pStateOld[i] = pStateNew[i]; + } + }; + + ScanHardware(pKeyboardState, pKeyOldState, pKeyNewState, 256); + ScanHardware(pMouseState, pMouseOldState, pMouseNewState, nMouseButtons); + + // Cache mouse coordinates so they remain consistent during frame + vMousePos = vMousePosCache; + nMouseWheelDelta = nMouseWheelDeltaCache; + nMouseWheelDeltaCache = 0; + + if (bTextEntryEnable) + { + UpdateTextEntry(); + } + + // Handle Frame Update + bool bExtensionBlockFrame = false; + for (auto& ext : vExtensions) bExtensionBlockFrame |= ext->OnBeforeUserUpdate(fElapsedTime); + if (!bExtensionBlockFrame) + { + if (!OnUserUpdate(fElapsedTime)) bAtomActive = false; + } + for (auto& ext : vExtensions) ext->OnAfterUserUpdate(fElapsedTime); + + if (bConsoleShow) + { + SetDrawTarget((uint8_t)0); + UpdateConsole(); + } + + // Display Frame + renderer->UpdateViewport(vViewPos, vViewSize); + renderer->ClearBuffer(olc::BLACK, true); + + // Layer 0 must always exist + vLayers[0].bUpdate = true; + vLayers[0].bShow = true; + SetDecalMode(DecalMode::NORMAL); + renderer->PrepareDrawing(); + + for (auto layer = vLayers.rbegin(); layer != vLayers.rend(); ++layer) + { + if (layer->bShow) + { + if (layer->funcHook == nullptr) + { + renderer->ApplyTexture(layer->pDrawTarget.Decal()->id); + if (!bSuspendTextureTransfer && layer->bUpdate) + { + layer->pDrawTarget.Decal()->Update(); + layer->bUpdate = false; + } + + renderer->DrawLayerQuad(layer->vOffset, layer->vScale, layer->tint); + + // Display Decals in order for this layer + for (auto& decal : layer->vecDecalInstance) + renderer->DrawDecal(decal); + layer->vecDecalInstance.clear(); + } + else + { + // Mwa ha ha.... Have Fun!!! + layer->funcHook(); + } + } + } + + + + // Present Graphics to screen + renderer->DisplayFrame(); + + // Update Title Bar + fFrameTimer += fElapsedTime; + nFrameCount++; + if (fFrameTimer >= 1.0f) + { + nLastFPS = nFrameCount; + fFrameTimer -= 1.0f; + std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount); + platform->SetWindowTitle(sTitle); + nFrameCount = 0; + } + } + + void PixelGameEngine::olc_ConstructFontSheet() + { + std::string data; + data += "?Q`0001oOch0o01o@F40o000000000"; + data += "O000000nOT0063Qo4d8>?7a14Gno94AA4gno94AaOT0>o3`oO400o7QN00000400"; + data += "Of80001oOg<7O7moBGT7O7lABET024@aBEd714AiOdl717a_=TH013Q>00000000"; + data += "720D000V?V5oB3Q_HdUoE7a9@DdDE4A9@DmoE4A;Hg]oM4Aj8S4D84@`00000000"; + data += "OaPT1000Oa`^13P1@AI[?g`1@A=[OdAoHgljA4Ao?WlBA7l1710007l100000000"; + data += "ObM6000oOfMV?3QoBDD`O7a0BDDH@5A0BDD<@5A0BGeVO5ao@CQR?5Po00000000"; + data += "Oc``000?Ogij70PO2D]??0Ph2DUM@7i`2DTg@7lh2GUj?0TO0C1870T?00000000"; + data += "70<4001o?P<7?1QoHg43O;`h@GT0@:@LB@d0>:@hN@L0@?aoN@<0O7ao0000?000"; + data += "OcH0001SOglLA7mg24TnK7ln24US>0PL24U140PnOgl0>7QgOcH0K71S0000A000"; + data += "00H00000@Dm1S007@DUSg00?OdTnH7YhOfTL<7Yh@Cl0700?@Ah0300700000000"; + data += "<008001QL00ZA41a@6HnI<1i@FHLM81M@@0LG81?O`0nC?Y7?`0ZA7Y300080000"; + data += "O`082000Oh0827mo6>Hn?Wmo?6HnMb11MP08@C11H`08@FP0@@0004@000000000"; + data += "00P00001Oab00003OcKP0006@6=PMgl<@440MglH@000000`@000001P00000000"; + data += "Ob@8@@00Ob@8@Ga13R@8Mga172@8?PAo3R@827QoOb@820@0O`0007`0000007P0"; + data += "O`000P08Od400g`<3V=P0G`673IP0`@3>1`00P@6O`P00g`SetPixel(px, py, olc::Pixel(k, k, k, k)); + if (++py == 48) { px++; py = 0; } + } + } + + fontRenderable.Decal()->Update(); + + constexpr std::array vSpacing = { { + 0x03,0x25,0x16,0x08,0x07,0x08,0x08,0x04,0x15,0x15,0x08,0x07,0x15,0x07,0x24,0x08, + 0x08,0x17,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x24,0x15,0x06,0x07,0x16,0x17, + 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x17,0x08,0x08,0x08, + 0x08,0x08,0x08,0x08,0x17,0x08,0x08,0x08,0x08,0x17,0x08,0x15,0x08,0x15,0x08,0x08, + 0x24,0x18,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x33,0x17,0x17,0x33,0x18,0x17,0x17, + 0x17,0x17,0x17,0x17,0x07,0x17,0x17,0x18,0x18,0x17,0x17,0x07,0x33,0x07,0x08,0x00, } }; + + for (auto c : vSpacing) vFontSpacing.push_back({ c >> 4, c & 15 }); + + // UK Standard Layout +#ifdef OLC_KEYBOARD_UK + vKeyboardMap = + { + {olc::Key::A, "a", "A"}, {olc::Key::B, "b", "B"}, {olc::Key::C, "c", "C"}, {olc::Key::D, "d", "D"}, {olc::Key::E, "e", "E"}, + {olc::Key::F, "f", "F"}, {olc::Key::G, "g", "G"}, {olc::Key::H, "h", "H"}, {olc::Key::I, "i", "I"}, {olc::Key::J, "j", "J"}, + {olc::Key::K, "k", "K"}, {olc::Key::L, "l", "L"}, {olc::Key::M, "m", "M"}, {olc::Key::N, "n", "N"}, {olc::Key::O, "o", "O"}, + {olc::Key::P, "p", "P"}, {olc::Key::Q, "q", "Q"}, {olc::Key::R, "r", "R"}, {olc::Key::S, "s", "S"}, {olc::Key::T, "t", "T"}, + {olc::Key::U, "u", "U"}, {olc::Key::V, "v", "V"}, {olc::Key::W, "w", "W"}, {olc::Key::X, "x", "X"}, {olc::Key::Y, "y", "Y"}, + {olc::Key::Z, "z", "Z"}, + + {olc::Key::K0, "0", ")"}, {olc::Key::K1, "1", "!"}, {olc::Key::K2, "2", "\""}, {olc::Key::K3, "3", "#"}, {olc::Key::K4, "4", "$"}, + {olc::Key::K5, "5", "%"}, {olc::Key::K6, "6", "^"}, {olc::Key::K7, "7", "&"}, {olc::Key::K8, "8", "*"}, {olc::Key::K9, "9", "("}, + + {olc::Key::NP0, "0", "0"}, {olc::Key::NP1, "1", "1"}, {olc::Key::NP2, "2", "2"}, {olc::Key::NP3, "3", "3"}, {olc::Key::NP4, "4", "4"}, + {olc::Key::NP5, "5", "5"}, {olc::Key::NP6, "6", "6"}, {olc::Key::NP7, "7", "7"}, {olc::Key::NP8, "8", "8"}, {olc::Key::NP9, "9", "9"}, + {olc::Key::NP_MUL, "*", "*"}, {olc::Key::NP_DIV, "/", "/"}, {olc::Key::NP_ADD, "+", "+"}, {olc::Key::NP_SUB, "-", "-"}, {olc::Key::NP_DECIMAL, ".", "."}, + + {olc::Key::PERIOD, ".", ">"}, {olc::Key::EQUALS, "=", "+"}, {olc::Key::COMMA, ",", "<"}, {olc::Key::MINUS, "-", "_"}, {olc::Key::SPACE, " ", " "}, + + {olc::Key::OEM_1, ";", ":"}, {olc::Key::OEM_2, "/", "?"}, {olc::Key::OEM_3, "\'", "@"}, {olc::Key::OEM_4, "[", "{"}, + {olc::Key::OEM_5, "\\", "|"}, {olc::Key::OEM_6, "]", "}"}, {olc::Key::OEM_7, "#", "~"}, + + // {olc::Key::TAB, "\t", "\t"} + }; +#endif + } + + void PixelGameEngine::pgex_Register(olc::PGEX* pgex) + { + if (std::find(vExtensions.begin(), vExtensions.end(), pgex) == vExtensions.end()) + vExtensions.push_back(pgex); + } + + + PGEX::PGEX(bool bHook) { if(bHook) pge->pgex_Register(this); } + void PGEX::OnBeforeUserCreate() {} + void PGEX::OnAfterUserCreate() {} + bool PGEX::OnBeforeUserUpdate(float& fElapsedTime) { return false; } + void PGEX::OnAfterUserUpdate(float fElapsedTime) {} + + // Need a couple of statics as these are singleton instances + // read from multiple locations + std::atomic PixelGameEngine::bAtomActive{ false }; + olc::PixelGameEngine* olc::PGEX::pge = nullptr; + olc::PixelGameEngine* olc::Platform::ptrPGE = nullptr; + olc::PixelGameEngine* olc::Renderer::ptrPGE = nullptr; + std::unique_ptr olc::Sprite::loader = nullptr; +}; +#pragma endregion + +// O------------------------------------------------------------------------------O +// | olcPixelGameEngine Renderers - the draw-y bits | +// O------------------------------------------------------------------------------O + +#if !defined(OLC_PGE_HEADLESS) + +#pragma region renderer_ogl10 +// O------------------------------------------------------------------------------O +// | START RENDERER: OpenGL 1.0 (the original, the best...) | +// O------------------------------------------------------------------------------O +#if defined(OLC_GFX_OPENGL10) + +#if defined(OLC_PLATFORM_WINAPI) + #include + #include + #if !defined(__MINGW32__) + #pragma comment(lib, "Dwmapi.lib") + #endif + typedef BOOL(WINAPI wglSwapInterval_t) (int interval); + static wglSwapInterval_t* wglSwapInterval = nullptr; + typedef HDC glDeviceContext_t; + typedef HGLRC glRenderContext_t; +#endif + +#if defined(__linux__) || defined(__FreeBSD__) + #include +#endif + +#if defined(OLC_PLATFORM_X11) + namespace X11 + { + #include + } + typedef int(glSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval); + static glSwapInterval_t* glSwapIntervalEXT; + typedef X11::GLXContext glDeviceContext_t; + typedef X11::GLXContext glRenderContext_t; +#endif + +#if defined(__APPLE__) + #define GL_SILENCE_DEPRECATION + #include + #include + #include +#endif + +namespace olc +{ + class Renderer_OGL10 : public olc::Renderer + { + private: +#if defined(OLC_PLATFORM_GLUT) + bool mFullScreen = false; +#else + glDeviceContext_t glDeviceContext = 0; + glRenderContext_t glRenderContext = 0; +#endif + + bool bSync = false; + olc::DecalMode nDecalMode = olc::DecalMode(-1); // Thanks Gusgo & Bispoo + olc::DecalStructure nDecalStructure = olc::DecalStructure(-1); +#if defined(OLC_PLATFORM_X11) + X11::Display* olc_Display = nullptr; + X11::Window* olc_Window = nullptr; + X11::XVisualInfo* olc_VisualInfo = nullptr; +#endif + + public: + void PrepareDevice() override + { +#if defined(OLC_PLATFORM_GLUT) + //glutInit has to be called with main() arguments, make fake ones + int argc = 0; + char* argv[1] = { (char*)"" }; + glutInit(&argc, argv); + glutInitWindowPosition(0, 0); + glutInitWindowSize(512, 512); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); + // Creates the window and the OpenGL context for it + glutCreateWindow("OneLoneCoder.com - Pixel Game Engine"); + glEnable(GL_TEXTURE_2D); // Turn on texturing + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); +#endif + } + + olc::rcode CreateDevice(std::vector params, bool bFullScreen, bool bVSYNC) override + { +#if defined(OLC_PLATFORM_WINAPI) + // Create Device Context + glDeviceContext = GetDC((HWND)(params[0])); + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int pf = 0; + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return olc::FAIL; + SetPixelFormat(glDeviceContext, pf, &pfd); + + if (!(glRenderContext = wglCreateContext(glDeviceContext))) return olc::FAIL; + wglMakeCurrent(glDeviceContext, glRenderContext); + + // Remove Frame cap + wglSwapInterval = (wglSwapInterval_t*)wglGetProcAddress("wglSwapIntervalEXT"); + if (wglSwapInterval && !bVSYNC) wglSwapInterval(0); + bSync = bVSYNC; +#endif + +#if defined(OLC_PLATFORM_X11) + using namespace X11; + // Linux has tighter coupling between OpenGL and X11, so we store + // various "platform" handles in the renderer + olc_Display = (X11::Display*)(params[0]); + olc_Window = (X11::Window*)(params[1]); + olc_VisualInfo = (X11::XVisualInfo*)(params[2]); + + glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE); + glXMakeCurrent(olc_Display, *olc_Window, glDeviceContext); + + XWindowAttributes gwa; + XGetWindowAttributes(olc_Display, *olc_Window, &gwa); + glViewport(0, 0, gwa.width, gwa.height); + + glSwapIntervalEXT = nullptr; + glSwapIntervalEXT = (glSwapInterval_t*)glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"); + + if (glSwapIntervalEXT == nullptr && !bVSYNC) + { + printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); + printf(" Don't worry though, things will still work, it's just the\n"); + printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); + } + + if (glSwapIntervalEXT != nullptr && !bVSYNC) + glSwapIntervalEXT(olc_Display, *olc_Window, 0); +#endif + +#if defined(OLC_PLATFORM_GLUT) + mFullScreen = bFullScreen; + if (!bVSYNC) + { +#if defined(__APPLE__) + GLint sync = 0; + CGLContextObj ctx = CGLGetCurrentContext(); + if (ctx) CGLSetParameter(ctx, kCGLCPSwapInterval, &sync); +#endif + } +#else + glEnable(GL_TEXTURE_2D); // Turn on texturing + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); +#endif + return olc::rcode::OK; + } + + olc::rcode DestroyDevice() override + { +#if defined(OLC_PLATFORM_WINAPI) + wglDeleteContext(glRenderContext); +#endif + +#if defined(OLC_PLATFORM_X11) + glXMakeCurrent(olc_Display, None, NULL); + glXDestroyContext(olc_Display, glDeviceContext); +#endif + +#if defined(OLC_PLATFORM_GLUT) + glutDestroyWindow(glutGetWindow()); +#endif + return olc::rcode::OK; + } + + void DisplayFrame() override + { +#if defined(OLC_PLATFORM_WINAPI) + SwapBuffers(glDeviceContext); + if (bSync) DwmFlush(); // Woooohooooooo!!!! SMOOOOOOOTH! +#endif + +#if defined(OLC_PLATFORM_X11) + X11::glXSwapBuffers(olc_Display, *olc_Window); +#endif + +#if defined(OLC_PLATFORM_GLUT) + glutSwapBuffers(); +#endif + } + + void PrepareDrawing() override + { + + //ClearBuffer(olc::GREEN, true); + glEnable(GL_BLEND); + nDecalMode = DecalMode::NORMAL; + nDecalStructure = DecalStructure::FAN; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + void SetDecalMode(const olc::DecalMode& mode) + { + if (mode != nDecalMode) + { + switch (mode) + { + case olc::DecalMode::NORMAL: + case olc::DecalMode::MODEL3D: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case olc::DecalMode::ADDITIVE: + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + break; + case olc::DecalMode::MULTIPLICATIVE: + glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); + break; + case olc::DecalMode::STENCIL: + glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + break; + case olc::DecalMode::ILLUMINATE: + glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); + break; + case olc::DecalMode::WIREFRAME: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + } + + nDecalMode = mode; + } + } + + void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) override + { + glBegin(GL_QUADS); + glColor4ub(tint.r, tint.g, tint.b, tint.a); + glTexCoord2f(0.0f * scale.x + offset.x, 1.0f * scale.y + offset.y); + glVertex3f(-1.0f /*+ vSubPixelOffset.x*/, -1.0f /*+ vSubPixelOffset.y*/, 0.0f); + glTexCoord2f(0.0f * scale.x + offset.x, 0.0f * scale.y + offset.y); + glVertex3f(-1.0f /*+ vSubPixelOffset.x*/, 1.0f /*+ vSubPixelOffset.y*/, 0.0f); + glTexCoord2f(1.0f * scale.x + offset.x, 0.0f * scale.y + offset.y); + glVertex3f(1.0f /*+ vSubPixelOffset.x*/, 1.0f /*+ vSubPixelOffset.y*/, 0.0f); + glTexCoord2f(1.0f * scale.x + offset.x, 1.0f * scale.y + offset.y); + glVertex3f(1.0f /*+ vSubPixelOffset.x*/, -1.0f /*+ vSubPixelOffset.y*/, 0.0f); + glEnd(); + } + + void DrawDecal(const olc::DecalInstance& decal) override + { + SetDecalMode(decal.mode); + + if (decal.decal == nullptr) + glBindTexture(GL_TEXTURE_2D, 0); + else + glBindTexture(GL_TEXTURE_2D, decal.decal->id); + + if (nDecalMode == DecalMode::MODEL3D) + { +#ifdef OLC_ENABLE_EXPERIMENTAL + glMatrixMode(GL_PROJECTION); glPushMatrix(); + glMatrixMode(GL_MODELVIEW); glPushMatrix(); + + glEnable(GL_DEPTH_TEST); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 1, 1000); + + #pragma comment (lib, "winmm.lib") + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, -40, -200); + glRotatef(float(clock()) * 0.1f, 1, 0, 0); + glRotatef(float(clock()) * 0.1f * 2, 0, 1, 0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_TRIANGLES); + + + // Render as 3D Spatial Entity + for (uint32_t n = 0; n < decal.points; n++) + { + glColor4ub(decal.tint[n].r, decal.tint[n].g, decal.tint[n].b, decal.tint[n].a); + glTexCoord2f(decal.uv[n].x, decal.uv[n].y); + glVertex3f(decal.pos[n].x, decal.pos[n].y, decal.w[n]); + } + + glEnd(); + + glMatrixMode(GL_PROJECTION); glPopMatrix(); + glMatrixMode(GL_MODELVIEW); glPopMatrix(); + glDisable(GL_DEPTH_TEST); +#endif + } + else + { + if (nDecalMode == DecalMode::WIREFRAME) + glBegin(GL_LINE_LOOP); + else + { + if(decal.structure == olc::DecalStructure::FAN) + glBegin(GL_TRIANGLE_FAN); + else if(decal.structure == olc::DecalStructure::STRIP) + glBegin(GL_TRIANGLE_STRIP); + else if(decal.structure == olc::DecalStructure::LIST) + glBegin(GL_TRIANGLES); + } + + // Render as 2D Spatial entity + for (uint32_t n = 0; n < decal.points; n++) + { + glColor4ub(decal.tint[n].r, decal.tint[n].g, decal.tint[n].b, decal.tint[n].a); + glTexCoord4f(decal.uv[n].x, decal.uv[n].y, 0.0f, decal.w[n]); + glVertex2f(decal.pos[n].x, decal.pos[n].y); + } + + glEnd(); + } + + + //glDisable(GL_DEPTH_TEST); + } + + uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered, const bool clamp) override + { + UNUSED(width); + UNUSED(height); + uint32_t id = 0; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + if (filtered) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (clamp) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + return id; + } + + uint32_t DeleteTexture(const uint32_t id) override + { + glDeleteTextures(1, &id); + return id; + } + + void UpdateTexture(uint32_t id, olc::Sprite* spr) override + { + UNUSED(id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spr->width, spr->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); + } + + void ReadTexture(uint32_t id, olc::Sprite* spr) override + { + glReadPixels(0, 0, spr->width, spr->height, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); + } + + void ApplyTexture(uint32_t id) override + { + glBindTexture(GL_TEXTURE_2D, id); + } + + void ClearBuffer(olc::Pixel p, bool bDepth) override + { + glClearColor(float(p.r) / 255.0f, float(p.g) / 255.0f, float(p.b) / 255.0f, float(p.a) / 255.0f); + glClear(GL_COLOR_BUFFER_BIT); + if (bDepth) glClear(GL_DEPTH_BUFFER_BIT); + } + + void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) override + { + glViewport(pos.x, pos.y, size.x, size.y); + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | END RENDERER: OpenGL 1.0 (the original, the best...) | +// O------------------------------------------------------------------------------O +#pragma endregion + +#pragma region renderer_ogl33 +// O------------------------------------------------------------------------------O +// | START RENDERER: OpenGL 3.3 (3.0 es) (sh-sh-sh-shaders....) | +// O------------------------------------------------------------------------------O +#if defined(OLC_GFX_OPENGL33) + +#if defined(OLC_PLATFORM_WINAPI) + #include + #include + #if !defined(__MINGW32__) + #pragma comment(lib, "Dwmapi.lib") + #endif + typedef void __stdcall locSwapInterval_t(GLsizei n); + typedef HDC glDeviceContext_t; + typedef HGLRC glRenderContext_t; + #define CALLSTYLE __stdcall + #define OGL_LOAD(t, n) (t*)wglGetProcAddress(#n) +#endif + +#if defined(__linux__) || defined(__FreeBSD__) + #include +#endif + +#if defined(OLC_PLATFORM_X11) + namespace X11 + { + #include + } + typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval); + typedef X11::GLXContext glDeviceContext_t; + typedef X11::GLXContext glRenderContext_t; + #define CALLSTYLE + #define OGL_LOAD(t, n) (t*)glXGetProcAddress((unsigned char*)#n); +#endif + +#if defined(__APPLE__) + #define GL_SILENCE_DEPRECATION + #include + #include + #include +#endif + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + #include + #include + #define GL_GLEXT_PROTOTYPES + #include + #include + #define CALLSTYLE + typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval); + #define GL_CLAMP GL_CLAMP_TO_EDGE + #define OGL_LOAD(t, n) n; +#endif + +namespace olc +{ + typedef char GLchar; + typedef ptrdiff_t GLsizeiptr; + typedef GLuint CALLSTYLE locCreateShader_t(GLenum type); + typedef GLuint CALLSTYLE locCreateProgram_t(void); + typedef void CALLSTYLE locDeleteShader_t(GLuint shader); +#if defined(OLC_PLATFORM_EMSCRIPTEN) + typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); +#else + typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); +#endif + typedef void CALLSTYLE locCompileShader_t(GLuint shader); + typedef void CALLSTYLE locLinkProgram_t(GLuint program); + typedef void CALLSTYLE locDeleteProgram_t(GLuint program); + typedef void CALLSTYLE locAttachShader_t(GLuint program, GLuint shader); + typedef void CALLSTYLE locBindBuffer_t(GLenum target, GLuint buffer); + typedef void CALLSTYLE locBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + typedef void CALLSTYLE locGenBuffers_t(GLsizei n, GLuint* buffers); + typedef void CALLSTYLE locVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + typedef void CALLSTYLE locEnableVertexAttribArray_t(GLuint index); + typedef void CALLSTYLE locUseProgram_t(GLuint program); + typedef void CALLSTYLE locBindVertexArray_t(GLuint array); + typedef void CALLSTYLE locGenVertexArrays_t(GLsizei n, GLuint* arrays); + typedef void CALLSTYLE locGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); + + constexpr size_t OLC_MAX_VERTS = 128; + + class Renderer_OGL33 : public olc::Renderer + { + private: +#if defined(OLC_PLATFORM_EMSCRIPTEN) + EGLDisplay olc_Display; + EGLConfig olc_Config; + EGLContext olc_Context; + EGLSurface olc_Surface; +#endif + +#if defined(OLC_PLATFORM_GLUT) + bool mFullScreen = false; +#else + #if !defined(OLC_PLATFORM_EMSCRIPTEN) + glDeviceContext_t glDeviceContext = 0; + glRenderContext_t glRenderContext = 0; + #endif +#endif + bool bSync = false; + olc::DecalMode nDecalMode = olc::DecalMode(-1); // Thanks Gusgo & Bispoo +#if defined(OLC_PLATFORM_X11) + X11::Display* olc_Display = nullptr; + X11::Window* olc_Window = nullptr; + X11::XVisualInfo* olc_VisualInfo = nullptr; +#endif + + private: + locCreateShader_t* locCreateShader = nullptr; + locShaderSource_t* locShaderSource = nullptr; + locCompileShader_t* locCompileShader = nullptr; + locDeleteShader_t* locDeleteShader = nullptr; + locCreateProgram_t* locCreateProgram = nullptr; + locDeleteProgram_t* locDeleteProgram = nullptr; + locLinkProgram_t* locLinkProgram = nullptr; + locAttachShader_t* locAttachShader = nullptr; + locBindBuffer_t* locBindBuffer = nullptr; + locBufferData_t* locBufferData = nullptr; + locGenBuffers_t* locGenBuffers = nullptr; + locVertexAttribPointer_t* locVertexAttribPointer = nullptr; + locEnableVertexAttribArray_t* locEnableVertexAttribArray = nullptr; + locUseProgram_t* locUseProgram = nullptr; + locBindVertexArray_t* locBindVertexArray = nullptr; + locGenVertexArrays_t* locGenVertexArrays = nullptr; + locSwapInterval_t* locSwapInterval = nullptr; + locGetShaderInfoLog_t* locGetShaderInfoLog = nullptr; + + uint32_t m_nFS = 0; + uint32_t m_nVS = 0; + uint32_t m_nQuadShader = 0; + uint32_t m_vbQuad = 0; + uint32_t m_vaQuad = 0; + + struct locVertex + { + float pos[3]; + olc::vf2d tex; + olc::Pixel col; + }; + + locVertex pVertexMem[OLC_MAX_VERTS]; + + olc::Renderable rendBlankQuad; + + public: + void PrepareDevice() override + { +#if defined(OLC_PLATFORM_GLUT) + //glutInit has to be called with main() arguments, make fake ones + int argc = 0; + char* argv[1] = { (char*)"" }; + glutInit(&argc, argv); + glutInitWindowPosition(0, 0); + glutInitWindowSize(512, 512); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA); + // Creates the window and the OpenGL context for it + glutCreateWindow("OneLoneCoder.com - Pixel Game Engine"); + glEnable(GL_TEXTURE_2D); // Turn on texturing + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); +#endif + } + + olc::rcode CreateDevice(std::vector params, bool bFullScreen, bool bVSYNC) override + { + // Create OpenGL Context +#if defined(OLC_PLATFORM_WINAPI) + // Create Device Context + glDeviceContext = GetDC((HWND)(params[0])); + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int pf = 0; + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) return olc::FAIL; + SetPixelFormat(glDeviceContext, pf, &pfd); + + if (!(glRenderContext = wglCreateContext(glDeviceContext))) return olc::FAIL; + wglMakeCurrent(glDeviceContext, glRenderContext); + + // Set Vertical Sync + locSwapInterval = OGL_LOAD(locSwapInterval_t, "wglSwapIntervalEXT"); + if (locSwapInterval && !bVSYNC) locSwapInterval(0); + bSync = bVSYNC; +#endif + +#if defined(OLC_PLATFORM_X11) + using namespace X11; + // Linux has tighter coupling between OpenGL and X11, so we store + // various "platform" handles in the renderer + olc_Display = (X11::Display*)(params[0]); + olc_Window = (X11::Window*)(params[1]); + olc_VisualInfo = (X11::XVisualInfo*)(params[2]); + + glDeviceContext = glXCreateContext(olc_Display, olc_VisualInfo, nullptr, GL_TRUE); + glXMakeCurrent(olc_Display, *olc_Window, glDeviceContext); + + XWindowAttributes gwa; + XGetWindowAttributes(olc_Display, *olc_Window, &gwa); + glViewport(0, 0, gwa.width, gwa.height); + + locSwapInterval = OGL_LOAD(locSwapInterval_t, "glXSwapIntervalEXT"); + + if (locSwapInterval == nullptr && !bVSYNC) + { + printf("NOTE: Could not disable VSYNC, glXSwapIntervalEXT() was not found!\n"); + printf(" Don't worry though, things will still work, it's just the\n"); + printf(" frame rate will be capped to your monitors refresh rate - javidx9\n"); + } + + if (locSwapInterval != nullptr && !bVSYNC) + locSwapInterval(olc_Display, *olc_Window, 0); +#endif + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + EGLint const attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; + EGLint const context_config[] = { EGL_CONTEXT_CLIENT_VERSION , 2, EGL_NONE }; + EGLint num_config; + + olc_Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(olc_Display, nullptr, nullptr); + eglChooseConfig(olc_Display, attribute_list, &olc_Config, 1, &num_config); + + /* create an EGL rendering context */ + olc_Context = eglCreateContext(olc_Display, olc_Config, EGL_NO_CONTEXT, context_config); + olc_Surface = eglCreateWindowSurface(olc_Display, olc_Config, NULL, nullptr); + eglMakeCurrent(olc_Display, olc_Surface, olc_Surface, olc_Context); + //eglSwapInterval is currently a NOP, plement anyways in case it becomes supported + locSwapInterval = &eglSwapInterval; + locSwapInterval(olc_Display, bVSYNC ? 1 : 0); +#endif + +#if defined(OLC_PLATFORM_GLUT) + mFullScreen = bFullScreen; + if (!bVSYNC) + { +#if defined(__APPLE__) + GLint sync = 0; + CGLContextObj ctx = CGLGetCurrentContext(); + if (ctx) CGLSetParameter(ctx, kCGLCPSwapInterval, &sync); +#endif + } +#else + #if !defined(OLC_PLATFORM_EMSCRIPTEN) + glEnable(GL_TEXTURE_2D); // Turn on texturing + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + #endif +#endif + // Load External OpenGL Functions + locCreateShader = OGL_LOAD(locCreateShader_t, glCreateShader); + locCompileShader = OGL_LOAD(locCompileShader_t, glCompileShader); + locShaderSource = OGL_LOAD(locShaderSource_t, glShaderSource); + locDeleteShader = OGL_LOAD(locDeleteShader_t, glDeleteShader); + locCreateProgram = OGL_LOAD(locCreateProgram_t, glCreateProgram); + locDeleteProgram = OGL_LOAD(locDeleteProgram_t, glDeleteProgram); + locLinkProgram = OGL_LOAD(locLinkProgram_t, glLinkProgram); + locAttachShader = OGL_LOAD(locAttachShader_t, glAttachShader); + locBindBuffer = OGL_LOAD(locBindBuffer_t, glBindBuffer); + locBufferData = OGL_LOAD(locBufferData_t, glBufferData); + locGenBuffers = OGL_LOAD(locGenBuffers_t, glGenBuffers); + locVertexAttribPointer = OGL_LOAD(locVertexAttribPointer_t, glVertexAttribPointer); + locEnableVertexAttribArray = OGL_LOAD(locEnableVertexAttribArray_t, glEnableVertexAttribArray); + locUseProgram = OGL_LOAD(locUseProgram_t, glUseProgram); + locGetShaderInfoLog = OGL_LOAD(locGetShaderInfoLog_t, glGetShaderInfoLog); +#if !defined(OLC_PLATFORM_EMSCRIPTEN) + locBindVertexArray = OGL_LOAD(locBindVertexArray_t, glBindVertexArray); + locGenVertexArrays = OGL_LOAD(locGenVertexArrays_t, glGenVertexArrays); +#else + locBindVertexArray = glBindVertexArrayOES; + locGenVertexArrays = glGenVertexArraysOES; +#endif + + // Load & Compile Quad Shader - assumes no errors + m_nFS = locCreateShader(0x8B30); + const GLchar* strFS = +#if defined(__arm__) || defined(OLC_PLATFORM_EMSCRIPTEN) + "#version 300 es\n" + "precision mediump float;" +#else + "#version 330 core\n" +#endif + "out vec4 pixel;\n""in vec2 oTex;\n" + "in vec4 oCol;\n""uniform sampler2D sprTex;\n""void main(){pixel = texture(sprTex, oTex) * oCol;}"; + locShaderSource(m_nFS, 1, &strFS, NULL); + locCompileShader(m_nFS); + + m_nVS = locCreateShader(0x8B31); + const GLchar* strVS = +#if defined(__arm__) || defined(OLC_PLATFORM_EMSCRIPTEN) + "#version 300 es\n" + "precision mediump float;" +#else + "#version 330 core\n" +#endif + "layout(location = 0) in vec3 aPos;\n""layout(location = 1) in vec2 aTex;\n" + "layout(location = 2) in vec4 aCol;\n""out vec2 oTex;\n""out vec4 oCol;\n" + "void main(){ float p = 1.0 / aPos.z; gl_Position = p * vec4(aPos.x, aPos.y, 0.0, 1.0); oTex = p * aTex; oCol = aCol;}"; + locShaderSource(m_nVS, 1, &strVS, NULL); + locCompileShader(m_nVS); + + m_nQuadShader = locCreateProgram(); + locAttachShader(m_nQuadShader, m_nFS); + locAttachShader(m_nQuadShader, m_nVS); + locLinkProgram(m_nQuadShader); + + // Create Quad + locGenBuffers(1, &m_vbQuad); + locGenVertexArrays(1, &m_vaQuad); + locBindVertexArray(m_vaQuad); + locBindBuffer(0x8892, m_vbQuad); + + locVertex verts[OLC_MAX_VERTS]; + locBufferData(0x8892, sizeof(locVertex) * OLC_MAX_VERTS, verts, 0x88E0); + locVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(locVertex), 0); locEnableVertexAttribArray(0); + locVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(locVertex), (void*)(3 * sizeof(float))); locEnableVertexAttribArray(1); + locVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(locVertex), (void*)(5 * sizeof(float))); locEnableVertexAttribArray(2); + locBindBuffer(0x8892, 0); + locBindVertexArray(0); + + // Create blank texture for spriteless decals + rendBlankQuad.Create(1, 1); + rendBlankQuad.Sprite()->GetData()[0] = olc::WHITE; + rendBlankQuad.Decal()->Update(); + return olc::rcode::OK; + } + + olc::rcode DestroyDevice() override + { +#if defined(OLC_PLATFORM_WINAPI) + wglDeleteContext(glRenderContext); +#endif + +#if defined(OLC_PLATFORM_X11) + glXMakeCurrent(olc_Display, None, NULL); + glXDestroyContext(olc_Display, glDeviceContext); +#endif + +#if defined(OLC_PLATFORM_GLUT) + glutDestroyWindow(glutGetWindow()); +#endif + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + eglMakeCurrent(olc_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(olc_Display, olc_Context); + eglDestroySurface(olc_Display, olc_Surface); + eglTerminate(olc_Display); + olc_Display = EGL_NO_DISPLAY; + olc_Surface = EGL_NO_SURFACE; + olc_Context = EGL_NO_CONTEXT; +#endif + return olc::rcode::OK; + } + + void DisplayFrame() override + { +#if defined(OLC_PLATFORM_WINAPI) + SwapBuffers(glDeviceContext); + if (bSync) DwmFlush(); // Woooohooooooo!!!! SMOOOOOOOTH! +#endif + +#if defined(OLC_PLATFORM_X11) + X11::glXSwapBuffers(olc_Display, *olc_Window); +#endif + +#if defined(OLC_PLATFORM_GLUT) + glutSwapBuffers(); +#endif + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + eglSwapBuffers(olc_Display, olc_Surface); +#endif + } + + void PrepareDrawing() override + { + glEnable(GL_BLEND); + nDecalMode = DecalMode::NORMAL; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + locUseProgram(m_nQuadShader); + locBindVertexArray(m_vaQuad); + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + locVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(locVertex), 0); locEnableVertexAttribArray(0); + locVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(locVertex), (void*)(3 * sizeof(float))); locEnableVertexAttribArray(1); + locVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(locVertex), (void*)(5 * sizeof(float))); locEnableVertexAttribArray(2); +#endif + } + + void SetDecalMode(const olc::DecalMode& mode) override + { + if (mode != nDecalMode) + { + switch (mode) + { + case olc::DecalMode::NORMAL: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; + case olc::DecalMode::ADDITIVE: glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; + case olc::DecalMode::MULTIPLICATIVE: glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); break; + case olc::DecalMode::STENCIL: glBlendFunc(GL_ZERO, GL_SRC_ALPHA); break; + case olc::DecalMode::ILLUMINATE: glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); break; + case olc::DecalMode::WIREFRAME: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; + } + + nDecalMode = mode; + } + } + + void DrawLayerQuad(const olc::vf2d& offset, const olc::vf2d& scale, const olc::Pixel tint) override + { + locBindBuffer(0x8892, m_vbQuad); + locVertex verts[4] = { + {{-1.0f, -1.0f, 1.0}, {0.0f * scale.x + offset.x, 1.0f * scale.y + offset.y}, tint}, + {{+1.0f, -1.0f, 1.0}, {1.0f * scale.x + offset.x, 1.0f * scale.y + offset.y}, tint}, + {{-1.0f, +1.0f, 1.0}, {0.0f * scale.x + offset.x, 0.0f * scale.y + offset.y}, tint}, + {{+1.0f, +1.0f, 1.0}, {1.0f * scale.x + offset.x, 0.0f * scale.y + offset.y}, tint}, + }; + + locBufferData(0x8892, sizeof(locVertex) * 4, verts, 0x88E0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + void DrawDecal(const olc::DecalInstance& decal) override + { + SetDecalMode(decal.mode); + if (decal.decal == nullptr) + glBindTexture(GL_TEXTURE_2D, rendBlankQuad.Decal()->id); + else + glBindTexture(GL_TEXTURE_2D, decal.decal->id); + + locBindBuffer(0x8892, m_vbQuad); + + for (uint32_t i = 0; i < decal.points; i++) + pVertexMem[i] = { { decal.pos[i].x, decal.pos[i].y, decal.w[i] }, { decal.uv[i].x, decal.uv[i].y }, decal.tint[i] }; + + locBufferData(0x8892, sizeof(locVertex) * decal.points, pVertexMem, 0x88E0); + + if (nDecalMode == DecalMode::WIREFRAME) + glDrawArrays(GL_LINE_LOOP, 0, decal.points); + else + glDrawArrays(GL_TRIANGLE_FAN, 0, decal.points); + } + + uint32_t CreateTexture(const uint32_t width, const uint32_t height, const bool filtered, const bool clamp) override + { + UNUSED(width); + UNUSED(height); + uint32_t id = 0; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + + if (filtered) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (clamp) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } +#if !defined(OLC_PLATFORM_EMSCRIPTEN) + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif + return id; + } + + uint32_t DeleteTexture(const uint32_t id) override + { + glDeleteTextures(1, &id); + return id; + } + + void UpdateTexture(uint32_t id, olc::Sprite* spr) override + { + UNUSED(id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, spr->width, spr->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); + } + + void ReadTexture(uint32_t id, olc::Sprite* spr) override + { + glReadPixels(0, 0, spr->width, spr->height, GL_RGBA, GL_UNSIGNED_BYTE, spr->GetData()); + } + + void ApplyTexture(uint32_t id) override + { + glBindTexture(GL_TEXTURE_2D, id); + } + + void ClearBuffer(olc::Pixel p, bool bDepth) override + { + glClearColor(float(p.r) / 255.0f, float(p.g) / 255.0f, float(p.b) / 255.0f, float(p.a) / 255.0f); + glClear(GL_COLOR_BUFFER_BIT); + if (bDepth) glClear(GL_DEPTH_BUFFER_BIT); + } + + void UpdateViewport(const olc::vi2d& pos, const olc::vi2d& size) override + { + glViewport(pos.x, pos.y, size.x, size.y); + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | END RENDERER: OpenGL 3.3 (3.0 es) (sh-sh-sh-shaders....) | +// O------------------------------------------------------------------------------O +#pragma endregion + +// O------------------------------------------------------------------------------O +// | olcPixelGameEngine Image loaders | +// O------------------------------------------------------------------------------O + +#pragma region image_gdi +// O------------------------------------------------------------------------------O +// | START IMAGE LOADER: GDI+, Windows Only, always exists, a little slow | +// O------------------------------------------------------------------------------O +#if defined(OLC_IMAGE_GDI) + +#define min(a, b) ((a < b) ? a : b) +#define max(a, b) ((a > b) ? a : b) +#include +#include +#if defined(__MINGW32__) // Thanks Gusgo & Dandistine, but c'mon mingw!! wtf?! + #include +#else + #include +#endif +#include +#undef min +#undef max + +#if !defined(__MINGW32__) + #pragma comment(lib, "gdiplus.lib") + #pragma comment(lib, "Shlwapi.lib") +#endif + +namespace olc +{ + // Thanks @MaGetzUb for this, which allows sprites to be defined + // at construction, by initialising the GDI subsystem + static class GDIPlusStartup + { + public: + GDIPlusStartup() + { + Gdiplus::GdiplusStartupInput startupInput; + GdiplusStartup(&token, &startupInput, NULL); + } + + ULONG_PTR token; + + ~GDIPlusStartup() + { + // Well, MarcusTU thought this was important :D + Gdiplus::GdiplusShutdown(token); + } + } gdistartup; + + class ImageLoader_GDIPlus : public olc::ImageLoader + { + private: + std::wstring ConvertS2W(std::string s) + { +#ifdef __MINGW32__ + wchar_t* buffer = new wchar_t[s.length() + 1]; + mbstowcs(buffer, s.c_str(), s.length()); + buffer[s.length()] = L'\0'; +#else + int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); + wchar_t* buffer = new wchar_t[count]; + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); +#endif + std::wstring w(buffer); + delete[] buffer; + return w; + } + + public: + ImageLoader_GDIPlus() : ImageLoader() + {} + + olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override + { + // clear out existing sprite + spr->pColData.clear(); + + // Open file + UNUSED(pack); + Gdiplus::Bitmap* bmp = nullptr; + if (pack != nullptr) + { + // Load sprite from input stream + ResourceBuffer rb = pack->GetFileBuffer(sImageFile); + bmp = Gdiplus::Bitmap::FromStream(SHCreateMemStream((BYTE*)rb.vMemory.data(), UINT(rb.vMemory.size()))); + } + else + { + // Check file exists + if (!_gfs::exists(sImageFile)) return olc::rcode::NO_FILE; + + // Load sprite from file + bmp = Gdiplus::Bitmap::FromFile(ConvertS2W(sImageFile).c_str()); + } + + if (bmp->GetLastStatus() != Gdiplus::Ok) return olc::rcode::FAIL; + spr->width = bmp->GetWidth(); + spr->height = bmp->GetHeight(); + + spr->pColData.resize(spr->width * spr->height); + + for (int y = 0; y < spr->height; y++) + for (int x = 0; x < spr->width; x++) + { + Gdiplus::Color c; + bmp->GetPixel(x, y, &c); + spr->SetPixel(x, y, olc::Pixel(c.GetRed(), c.GetGreen(), c.GetBlue(), c.GetAlpha())); + } + delete bmp; + return olc::rcode::OK; + } + + olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override + { + return olc::rcode::OK; + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | END IMAGE LOADER: GDI+ | +// O------------------------------------------------------------------------------O +#pragma endregion + +#pragma region image_libpng +// O------------------------------------------------------------------------------O +// | START IMAGE LOADER: libpng, default on linux, requires -lpng (libpng-dev) | +// O------------------------------------------------------------------------------O +#if defined(OLC_IMAGE_LIBPNG) +#include +namespace olc +{ + void pngReadStream(png_structp pngPtr, png_bytep data, png_size_t length) + { + png_voidp a = png_get_io_ptr(pngPtr); + ((std::istream*)a)->read((char*)data, length); + } + + class ImageLoader_LibPNG : public olc::ImageLoader + { + public: + ImageLoader_LibPNG() : ImageLoader() + {} + + olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override + { + UNUSED(pack); + + // clear out existing sprite + spr->pColData.clear(); + + //////////////////////////////////////////////////////////////////////////// + // Use libpng, Thanks to Guillaume Cottenceau + // https://gist.github.com/niw/5963798 + // Also reading png from streams + // http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/ + png_structp png; + png_infop info; + + auto loadPNG = [&]() + { + png_read_info(png, info); + png_byte color_type; + png_byte bit_depth; + png_bytep* row_pointers; + spr->width = png_get_image_width(png, info); + spr->height = png_get_image_height(png, info); + color_type = png_get_color_type(png, info); + bit_depth = png_get_bit_depth(png, info); + if (bit_depth == 16) png_set_strip_16(png); + if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png); + if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); + if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler(png, 0xFF, PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + png_read_update_info(png, info); + row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * spr->height); + for (int y = 0; y < spr->height; y++) { + row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png, info)); + } + png_read_image(png, row_pointers); + //////////////////////////////////////////////////////////////////////////// + // Create sprite array + spr->pColData.resize(spr->width * spr->height); + // Iterate through image rows, converting into sprite format + for (int y = 0; y < spr->height; y++) + { + png_bytep row = row_pointers[y]; + for (int x = 0; x < spr->width; x++) + { + png_bytep px = &(row[x * 4]); + spr->SetPixel(x, y, Pixel(px[0], px[1], px[2], px[3])); + } + } + + for (int y = 0; y < spr->height; y++) // Thanks maksym33 + free(row_pointers[y]); + free(row_pointers); + png_destroy_read_struct(&png, &info, nullptr); + }; + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) goto fail_load; + + info = png_create_info_struct(png); + if (!info) goto fail_load; + + if (setjmp(png_jmpbuf(png))) goto fail_load; + + if (pack == nullptr) + { + FILE* f = fopen(sImageFile.c_str(), "rb"); + if (!f) return olc::rcode::NO_FILE; + png_init_io(png, f); + loadPNG(); + fclose(f); + } + else + { + ResourceBuffer rb = pack->GetFileBuffer(sImageFile); + std::istream is(&rb); + png_set_read_fn(png, (png_voidp)&is, pngReadStream); + loadPNG(); + } + + return olc::rcode::OK; + + fail_load: + spr->width = 0; + spr->height = 0; + spr->pColData.clear(); + return olc::rcode::FAIL; + } + + olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override + { + return olc::rcode::OK; + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | END IMAGE LOADER: | +// O------------------------------------------------------------------------------O +#pragma endregion + +#pragma region image_stb +// O------------------------------------------------------------------------------O +// | START IMAGE LOADER: stb_image.h, all systems, very fast | +// O------------------------------------------------------------------------------O +// Thanks to Sean Barrett - https://github.com/nothings/stb/blob/master/stb_image.h +// MIT License - Copyright(c) 2017 Sean Barrett + +// Note you need to download the above file into your project folder, and +// #define OLC_IMAGE_STB +// #define OLC_PGE_APPLICATION +// #include "olcPixelGameEngine.h" + +#if defined(OLC_IMAGE_STB) +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +namespace olc +{ + class ImageLoader_STB : public olc::ImageLoader + { + public: + ImageLoader_STB() : ImageLoader() + {} + + olc::rcode LoadImageResource(olc::Sprite* spr, const std::string& sImageFile, olc::ResourcePack* pack) override + { + UNUSED(pack); + // clear out existing sprite + spr->pColData.clear(); + // Open file + stbi_uc* bytes = nullptr; + int w = 0, h = 0, cmp = 0; + if (pack != nullptr) + { + ResourceBuffer rb = pack->GetFileBuffer(sImageFile); + bytes = stbi_load_from_memory((unsigned char*)rb.vMemory.data(), rb.vMemory.size(), &w, &h, &cmp, 4); + } + else + { + // Check file exists + if (!_gfs::exists(sImageFile)) return olc::rcode::NO_FILE; + bytes = stbi_load(sImageFile.c_str(), &w, &h, &cmp, 4); + } + + if (!bytes) return olc::rcode::FAIL; + spr->width = w; spr->height = h; + spr->pColData.resize(spr->width * spr->height); + std::memcpy(spr->pColData.data(), bytes, spr->width * spr->height * 4); + delete[] bytes; + return olc::rcode::OK; + } + + olc::rcode SaveImageResource(olc::Sprite* spr, const std::string& sImageFile) override + { + return olc::rcode::OK; + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | START IMAGE LOADER: stb_image.h | +// O------------------------------------------------------------------------------O +#pragma endregion + +// O------------------------------------------------------------------------------O +// | olcPixelGameEngine Platforms | +// O------------------------------------------------------------------------------O + +#pragma region platform_windows +// O------------------------------------------------------------------------------O +// | START PLATFORM: MICROSOFT WINDOWS XP, VISTA, 7, 8, 10 | +// O------------------------------------------------------------------------------O +#if defined(OLC_PLATFORM_WINAPI) + +#if defined(_WIN32) && !defined(__MINGW32__) + #pragma comment(lib, "user32.lib") // Visual Studio Only + #pragma comment(lib, "gdi32.lib") // For other Windows Compilers please add + #pragma comment(lib, "opengl32.lib") // these libs to your linker input +#endif + +namespace olc +{ + class Platform_Windows : public olc::Platform + { + private: + HWND olc_hWnd = nullptr; + std::wstring wsAppName; + + std::wstring ConvertS2W(std::string s) + { +#ifdef __MINGW32__ + wchar_t* buffer = new wchar_t[s.length() + 1]; + mbstowcs(buffer, s.c_str(), s.length()); + buffer[s.length()] = L'\0'; +#else + int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); + wchar_t* buffer = new wchar_t[count]; + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); +#endif + std::wstring w(buffer); + delete[] buffer; + return w; + } + + public: + virtual olc::rcode ApplicationStartUp() override { return olc::rcode::OK; } + virtual olc::rcode ApplicationCleanUp() override { return olc::rcode::OK; } + virtual olc::rcode ThreadStartUp() override { return olc::rcode::OK; } + + virtual olc::rcode ThreadCleanUp() override + { + renderer->DestroyDevice(); + PostMessage(olc_hWnd, WM_DESTROY, 0, 0); + return olc::OK; + } + + virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override + { + if (renderer->CreateDevice({ olc_hWnd }, bFullScreen, bEnableVSYNC) == olc::rcode::OK) + { + renderer->UpdateViewport(vViewPos, vViewSize); + return olc::rcode::OK; + } + else + return olc::rcode::FAIL; + } + + virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override + { + WNDCLASS wc; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = olc_WindowEvent; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.lpszMenuName = nullptr; + wc.hbrBackground = nullptr; + wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE"); + RegisterClass(&wc); + + // Define window furniture + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME; + + olc::vi2d vTopLeft = vWindowPos; + + // Handle Fullscreen + if (bFullScreen) + { + dwExStyle = 0; + dwStyle = WS_VISIBLE | WS_POPUP; + HMONITOR hmon = MonitorFromWindow(olc_hWnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO mi = { sizeof(mi) }; + if (!GetMonitorInfo(hmon, &mi)) return olc::rcode::FAIL; + vWindowSize = { mi.rcMonitor.right, mi.rcMonitor.bottom }; + vTopLeft.x = 0; + vTopLeft.y = 0; + } + + // Keep client size as requested + RECT rWndRect = { 0, 0, vWindowSize.x, vWindowSize.y }; + AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); + int width = rWndRect.right - rWndRect.left; + int height = rWndRect.bottom - rWndRect.top; + + olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle, + vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this); + + // Create Keyboard Mapping + mapKeys[0x00] = Key::NONE; + mapKeys[0x41] = Key::A; mapKeys[0x42] = Key::B; mapKeys[0x43] = Key::C; mapKeys[0x44] = Key::D; mapKeys[0x45] = Key::E; + mapKeys[0x46] = Key::F; mapKeys[0x47] = Key::G; mapKeys[0x48] = Key::H; mapKeys[0x49] = Key::I; mapKeys[0x4A] = Key::J; + mapKeys[0x4B] = Key::K; mapKeys[0x4C] = Key::L; mapKeys[0x4D] = Key::M; mapKeys[0x4E] = Key::N; mapKeys[0x4F] = Key::O; + mapKeys[0x50] = Key::P; mapKeys[0x51] = Key::Q; mapKeys[0x52] = Key::R; mapKeys[0x53] = Key::S; mapKeys[0x54] = Key::T; + mapKeys[0x55] = Key::U; mapKeys[0x56] = Key::V; mapKeys[0x57] = Key::W; mapKeys[0x58] = Key::X; mapKeys[0x59] = Key::Y; + mapKeys[0x5A] = Key::Z; + + mapKeys[VK_F1] = Key::F1; mapKeys[VK_F2] = Key::F2; mapKeys[VK_F3] = Key::F3; mapKeys[VK_F4] = Key::F4; + mapKeys[VK_F5] = Key::F5; mapKeys[VK_F6] = Key::F6; mapKeys[VK_F7] = Key::F7; mapKeys[VK_F8] = Key::F8; + mapKeys[VK_F9] = Key::F9; mapKeys[VK_F10] = Key::F10; mapKeys[VK_F11] = Key::F11; mapKeys[VK_F12] = Key::F12; + + mapKeys[VK_DOWN] = Key::DOWN; mapKeys[VK_LEFT] = Key::LEFT; mapKeys[VK_RIGHT] = Key::RIGHT; mapKeys[VK_UP] = Key::UP; + //mapKeys[VK_RETURN] = Key::ENTER;// mapKeys[VK_RETURN] = Key::RETURN; + + mapKeys[VK_BACK] = Key::BACK; mapKeys[VK_ESCAPE] = Key::ESCAPE; mapKeys[VK_RETURN] = Key::ENTER; mapKeys[VK_PAUSE] = Key::PAUSE; + mapKeys[VK_SCROLL] = Key::SCROLL; mapKeys[VK_TAB] = Key::TAB; mapKeys[VK_DELETE] = Key::DEL; mapKeys[VK_HOME] = Key::HOME; + mapKeys[VK_END] = Key::END; mapKeys[VK_PRIOR] = Key::PGUP; mapKeys[VK_NEXT] = Key::PGDN; mapKeys[VK_INSERT] = Key::INS; + mapKeys[VK_SHIFT] = Key::SHIFT; mapKeys[VK_CONTROL] = Key::CTRL; + mapKeys[VK_SPACE] = Key::SPACE; + + mapKeys[0x30] = Key::K0; mapKeys[0x31] = Key::K1; mapKeys[0x32] = Key::K2; mapKeys[0x33] = Key::K3; mapKeys[0x34] = Key::K4; + mapKeys[0x35] = Key::K5; mapKeys[0x36] = Key::K6; mapKeys[0x37] = Key::K7; mapKeys[0x38] = Key::K8; mapKeys[0x39] = Key::K9; + + mapKeys[VK_NUMPAD0] = Key::NP0; mapKeys[VK_NUMPAD1] = Key::NP1; mapKeys[VK_NUMPAD2] = Key::NP2; mapKeys[VK_NUMPAD3] = Key::NP3; mapKeys[VK_NUMPAD4] = Key::NP4; + mapKeys[VK_NUMPAD5] = Key::NP5; mapKeys[VK_NUMPAD6] = Key::NP6; mapKeys[VK_NUMPAD7] = Key::NP7; mapKeys[VK_NUMPAD8] = Key::NP8; mapKeys[VK_NUMPAD9] = Key::NP9; + mapKeys[VK_MULTIPLY] = Key::NP_MUL; mapKeys[VK_ADD] = Key::NP_ADD; mapKeys[VK_DIVIDE] = Key::NP_DIV; mapKeys[VK_SUBTRACT] = Key::NP_SUB; mapKeys[VK_DECIMAL] = Key::NP_DECIMAL; + + // Thanks scripticuk + mapKeys[VK_OEM_1] = Key::OEM_1; // On US and UK keyboards this is the ';:' key + mapKeys[VK_OEM_2] = Key::OEM_2; // On US and UK keyboards this is the '/?' key + mapKeys[VK_OEM_3] = Key::OEM_3; // On US keyboard this is the '~' key + mapKeys[VK_OEM_4] = Key::OEM_4; // On US and UK keyboards this is the '[{' key + mapKeys[VK_OEM_5] = Key::OEM_5; // On US keyboard this is '\|' key. + mapKeys[VK_OEM_6] = Key::OEM_6; // On US and UK keyboards this is the ']}' key + mapKeys[VK_OEM_7] = Key::OEM_7; // On US keyboard this is the single/double quote key. On UK, this is the single quote/@ symbol key + mapKeys[VK_OEM_8] = Key::OEM_8; // miscellaneous characters. Varies by keyboard + mapKeys[VK_OEM_PLUS] = Key::EQUALS; // the '+' key on any keyboard + mapKeys[VK_OEM_COMMA] = Key::COMMA; // the comma key on any keyboard + mapKeys[VK_OEM_MINUS] = Key::MINUS; // the minus key on any keyboard + mapKeys[VK_OEM_PERIOD] = Key::PERIOD; // the period key on any keyboard + mapKeys[VK_CAPITAL] = Key::CAPS_LOCK; + return olc::OK; + } + + virtual olc::rcode SetWindowTitle(const std::string& s) override + { +#ifdef UNICODE + SetWindowText(olc_hWnd, ConvertS2W(s).c_str()); +#else + SetWindowText(olc_hWnd, s.c_str()); +#endif + return olc::OK; + } + + virtual olc::rcode StartSystemEventLoop() override + { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return olc::OK; + } + + virtual olc::rcode HandleSystemEvent() override { return olc::rcode::FAIL; } + + // Windows Event Handler - this is statically connected to the windows event system + static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_MOUSEMOVE: + { + // Thanks @ForAbby (Discord) + uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; + int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; + ptrPGE->olc_UpdateMouse(ix, iy); + return 0; + } + case WM_SIZE: ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; + case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; + case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; + case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; + case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], true); return 0; + case WM_KEYUP: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], false); return 0; + case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], true); return 0; + case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(mapKeys[wParam], false); return 0; + case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; + case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; + case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; + case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; + case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; + case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; + case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; + case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | END PLATFORM: MICROSOFT WINDOWS XP, VISTA, 7, 8, 10 | +// O------------------------------------------------------------------------------O +#pragma endregion + +#pragma region platform_linux +// O------------------------------------------------------------------------------O +// | START PLATFORM: LINUX | +// O------------------------------------------------------------------------------O +#if defined(OLC_PLATFORM_X11) +namespace olc +{ + class Platform_Linux : public olc::Platform + { + private: + X11::Display* olc_Display = nullptr; + X11::Window olc_WindowRoot; + X11::Window olc_Window; + X11::XVisualInfo* olc_VisualInfo; + X11::Colormap olc_ColourMap; + X11::XSetWindowAttributes olc_SetWindowAttribs; + + public: + virtual olc::rcode ApplicationStartUp() override + { + return olc::rcode::OK; + } + + virtual olc::rcode ApplicationCleanUp() override + { + XDestroyWindow(olc_Display, olc_Window); + return olc::rcode::OK; + } + + virtual olc::rcode ThreadStartUp() override + { + return olc::rcode::OK; + } + + virtual olc::rcode ThreadCleanUp() override + { + renderer->DestroyDevice(); + return olc::OK; + } + + virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override + { + if (renderer->CreateDevice({ olc_Display, &olc_Window, olc_VisualInfo }, bFullScreen, bEnableVSYNC) == olc::rcode::OK) + { + renderer->UpdateViewport(vViewPos, vViewSize); + return olc::rcode::OK; + } + else + return olc::rcode::FAIL; + } + + virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override + { + using namespace X11; + XInitThreads(); + + // Grab the deafult display and window + olc_Display = XOpenDisplay(NULL); + olc_WindowRoot = DefaultRootWindow(olc_Display); + + // Based on the display capabilities, configure the appearance of the window + GLint olc_GLAttribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; + olc_VisualInfo = glXChooseVisual(olc_Display, 0, olc_GLAttribs); + olc_ColourMap = XCreateColormap(olc_Display, olc_WindowRoot, olc_VisualInfo->visual, AllocNone); + olc_SetWindowAttribs.colormap = olc_ColourMap; + + // Register which events we are interested in receiving + olc_SetWindowAttribs.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | StructureNotifyMask; + + // Create the window + olc_Window = XCreateWindow(olc_Display, olc_WindowRoot, vWindowPos.x, vWindowPos.y, + vWindowSize.x, vWindowSize.y, + 0, olc_VisualInfo->depth, InputOutput, olc_VisualInfo->visual, + CWColormap | CWEventMask, &olc_SetWindowAttribs); + + Atom wmDelete = XInternAtom(olc_Display, "WM_DELETE_WINDOW", true); + XSetWMProtocols(olc_Display, olc_Window, &wmDelete, 1); + + XMapWindow(olc_Display, olc_Window); + XStoreName(olc_Display, olc_Window, "OneLoneCoder.com - Pixel Game Engine"); + + if (bFullScreen) // Thanks DragonEye, again :D + { + Atom wm_state; + Atom fullscreen; + wm_state = XInternAtom(olc_Display, "_NET_WM_STATE", False); + fullscreen = XInternAtom(olc_Display, "_NET_WM_STATE_FULLSCREEN", False); + XEvent xev{ 0 }; + xev.type = ClientMessage; + xev.xclient.window = olc_Window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = (bFullScreen ? 1 : 0); // the action (0: off, 1: on, 2: toggle) + xev.xclient.data.l[1] = fullscreen; // first property to alter + xev.xclient.data.l[2] = 0; // second property to alter + xev.xclient.data.l[3] = 0; // source indication + XMapWindow(olc_Display, olc_Window); + XSendEvent(olc_Display, DefaultRootWindow(olc_Display), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(olc_Display); + XWindowAttributes gwa; + XGetWindowAttributes(olc_Display, olc_Window, &gwa); + vWindowSize.x = gwa.width; + vWindowSize.y = gwa.height; + } + + // Create Keyboard Mapping + mapKeys[0x00] = Key::NONE; + mapKeys[0x61] = Key::A; mapKeys[0x62] = Key::B; mapKeys[0x63] = Key::C; mapKeys[0x64] = Key::D; mapKeys[0x65] = Key::E; + mapKeys[0x66] = Key::F; mapKeys[0x67] = Key::G; mapKeys[0x68] = Key::H; mapKeys[0x69] = Key::I; mapKeys[0x6A] = Key::J; + mapKeys[0x6B] = Key::K; mapKeys[0x6C] = Key::L; mapKeys[0x6D] = Key::M; mapKeys[0x6E] = Key::N; mapKeys[0x6F] = Key::O; + mapKeys[0x70] = Key::P; mapKeys[0x71] = Key::Q; mapKeys[0x72] = Key::R; mapKeys[0x73] = Key::S; mapKeys[0x74] = Key::T; + mapKeys[0x75] = Key::U; mapKeys[0x76] = Key::V; mapKeys[0x77] = Key::W; mapKeys[0x78] = Key::X; mapKeys[0x79] = Key::Y; + mapKeys[0x7A] = Key::Z; + + mapKeys[XK_F1] = Key::F1; mapKeys[XK_F2] = Key::F2; mapKeys[XK_F3] = Key::F3; mapKeys[XK_F4] = Key::F4; + mapKeys[XK_F5] = Key::F5; mapKeys[XK_F6] = Key::F6; mapKeys[XK_F7] = Key::F7; mapKeys[XK_F8] = Key::F8; + mapKeys[XK_F9] = Key::F9; mapKeys[XK_F10] = Key::F10; mapKeys[XK_F11] = Key::F11; mapKeys[XK_F12] = Key::F12; + + mapKeys[XK_Down] = Key::DOWN; mapKeys[XK_Left] = Key::LEFT; mapKeys[XK_Right] = Key::RIGHT; mapKeys[XK_Up] = Key::UP; + mapKeys[XK_KP_Enter] = Key::ENTER; mapKeys[XK_Return] = Key::ENTER; + + mapKeys[XK_BackSpace] = Key::BACK; mapKeys[XK_Escape] = Key::ESCAPE; mapKeys[XK_Linefeed] = Key::ENTER; mapKeys[XK_Pause] = Key::PAUSE; + mapKeys[XK_Scroll_Lock] = Key::SCROLL; mapKeys[XK_Tab] = Key::TAB; mapKeys[XK_Delete] = Key::DEL; mapKeys[XK_Home] = Key::HOME; + mapKeys[XK_End] = Key::END; mapKeys[XK_Page_Up] = Key::PGUP; mapKeys[XK_Page_Down] = Key::PGDN; mapKeys[XK_Insert] = Key::INS; + mapKeys[XK_Shift_L] = Key::SHIFT; mapKeys[XK_Shift_R] = Key::SHIFT; mapKeys[XK_Control_L] = Key::CTRL; mapKeys[XK_Control_R] = Key::CTRL; + mapKeys[XK_space] = Key::SPACE; mapKeys[XK_period] = Key::PERIOD; + + mapKeys[XK_0] = Key::K0; mapKeys[XK_1] = Key::K1; mapKeys[XK_2] = Key::K2; mapKeys[XK_3] = Key::K3; mapKeys[XK_4] = Key::K4; + mapKeys[XK_5] = Key::K5; mapKeys[XK_6] = Key::K6; mapKeys[XK_7] = Key::K7; mapKeys[XK_8] = Key::K8; mapKeys[XK_9] = Key::K9; + + mapKeys[XK_KP_0] = Key::NP0; mapKeys[XK_KP_1] = Key::NP1; mapKeys[XK_KP_2] = Key::NP2; mapKeys[XK_KP_3] = Key::NP3; mapKeys[XK_KP_4] = Key::NP4; + mapKeys[XK_KP_5] = Key::NP5; mapKeys[XK_KP_6] = Key::NP6; mapKeys[XK_KP_7] = Key::NP7; mapKeys[XK_KP_8] = Key::NP8; mapKeys[XK_KP_9] = Key::NP9; + mapKeys[XK_KP_Multiply] = Key::NP_MUL; mapKeys[XK_KP_Add] = Key::NP_ADD; mapKeys[XK_KP_Divide] = Key::NP_DIV; mapKeys[XK_KP_Subtract] = Key::NP_SUB; mapKeys[XK_KP_Decimal] = Key::NP_DECIMAL; + + // These keys vary depending on the keyboard. I've included comments for US and UK keyboard layouts + mapKeys[XK_semicolon] = Key::OEM_1; // On US and UK keyboards this is the ';:' key + mapKeys[XK_slash] = Key::OEM_2; // On US and UK keyboards this is the '/?' key + mapKeys[XK_asciitilde] = Key::OEM_3; // On US keyboard this is the '~' key + mapKeys[XK_bracketleft] = Key::OEM_4; // On US and UK keyboards this is the '[{' key + mapKeys[XK_backslash] = Key::OEM_5; // On US keyboard this is '\|' key. + mapKeys[XK_bracketright] = Key::OEM_6; // On US and UK keyboards this is the ']}' key + mapKeys[XK_apostrophe] = Key::OEM_7; // On US keyboard this is the single/double quote key. On UK, this is the single quote/@ symbol key + mapKeys[XK_numbersign] = Key::OEM_8; // miscellaneous characters. Varies by keyboard. I believe this to be the '#~' key on UK keyboards + mapKeys[XK_equal] = Key::EQUALS; // the '+' key on any keyboard + mapKeys[XK_comma] = Key::COMMA; // the comma key on any keyboard + mapKeys[XK_minus] = Key::MINUS; // the minus key on any keyboard + + mapKeys[XK_Caps_Lock] = Key::CAPS_LOCK; + + return olc::OK; + } + + virtual olc::rcode SetWindowTitle(const std::string& s) override + { + X11::XStoreName(olc_Display, olc_Window, s.c_str()); + return olc::OK; + } + + virtual olc::rcode StartSystemEventLoop() override + { + return olc::OK; + } + + virtual olc::rcode HandleSystemEvent() override + { + using namespace X11; + // Handle Xlib Message Loop - we do this in the + // same thread that OpenGL was created so we dont + // need to worry too much about multithreading with X11 + XEvent xev; + while (XPending(olc_Display)) + { + XNextEvent(olc_Display, &xev); + if (xev.type == Expose) + { + XWindowAttributes gwa; + XGetWindowAttributes(olc_Display, olc_Window, &gwa); + ptrPGE->olc_UpdateWindowSize(gwa.width, gwa.height); + } + else if (xev.type == ConfigureNotify) + { + XConfigureEvent xce = xev.xconfigure; + ptrPGE->olc_UpdateWindowSize(xce.width, xce.height); + } + else if (xev.type == KeyPress) + { + KeySym sym = XLookupKeysym(&xev.xkey, 0); + ptrPGE->olc_UpdateKeyState(mapKeys[sym], true); + XKeyEvent* e = (XKeyEvent*)&xev; // Because DragonEye loves numpads + XLookupString(e, NULL, 0, &sym, NULL); + ptrPGE->olc_UpdateKeyState(mapKeys[sym], true); + } + else if (xev.type == KeyRelease) + { + KeySym sym = XLookupKeysym(&xev.xkey, 0); + ptrPGE->olc_UpdateKeyState(mapKeys[sym], false); + XKeyEvent* e = (XKeyEvent*)&xev; + XLookupString(e, NULL, 0, &sym, NULL); + ptrPGE->olc_UpdateKeyState(mapKeys[sym], false); + } + else if (xev.type == ButtonPress) + { + switch (xev.xbutton.button) + { + case 1: ptrPGE->olc_UpdateMouseState(0, true); break; + case 2: ptrPGE->olc_UpdateMouseState(2, true); break; + case 3: ptrPGE->olc_UpdateMouseState(1, true); break; + case 4: ptrPGE->olc_UpdateMouseWheel(120); break; + case 5: ptrPGE->olc_UpdateMouseWheel(-120); break; + default: break; + } + } + else if (xev.type == ButtonRelease) + { + switch (xev.xbutton.button) + { + case 1: ptrPGE->olc_UpdateMouseState(0, false); break; + case 2: ptrPGE->olc_UpdateMouseState(2, false); break; + case 3: ptrPGE->olc_UpdateMouseState(1, false); break; + default: break; + } + } + else if (xev.type == MotionNotify) + { + ptrPGE->olc_UpdateMouse(xev.xmotion.x, xev.xmotion.y); + } + else if (xev.type == FocusIn) + { + ptrPGE->olc_UpdateKeyFocus(true); + } + else if (xev.type == FocusOut) + { + ptrPGE->olc_UpdateKeyFocus(false); + } + else if (xev.type == ClientMessage) + { + ptrPGE->olc_Terminate(); + } + } + return olc::OK; + } + }; +} +#endif +// O------------------------------------------------------------------------------O +// | END PLATFORM: LINUX | +// O------------------------------------------------------------------------------O +#pragma endregion + +#pragma region platform_glut +// O------------------------------------------------------------------------------O +// | START PLATFORM: GLUT (used to make it simple for Apple) | +// O------------------------------------------------------------------------------O +// +// VERY IMPORTANT!!! The Apple port was originally created by @Mumflr (discord) +// and the repo for the development of this project can be found here: +// https://github.com/MumflrFumperdink/olcPGEMac which contains maccy goodness +// and support on how to setup your build environment. +// +// "MASSIVE MASSIVE THANKS TO MUMFLR" - Javidx9 +#if defined(OLC_PLATFORM_GLUT) +namespace olc { + + class Platform_GLUT : public olc::Platform + { + public: + static std::atomic* bActiveRef; + + virtual olc::rcode ApplicationStartUp() override { + return olc::rcode::OK; + } + + virtual olc::rcode ApplicationCleanUp() override + { + return olc::rcode::OK; + } + + virtual olc::rcode ThreadStartUp() override + { + return olc::rcode::OK; + } + + virtual olc::rcode ThreadCleanUp() override + { + renderer->DestroyDevice(); + return olc::OK; + } + + virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override + { + if (renderer->CreateDevice({}, bFullScreen, bEnableVSYNC) == olc::rcode::OK) + { + renderer->UpdateViewport(vViewPos, vViewSize); + return olc::rcode::OK; + } + else + return olc::rcode::FAIL; + } + + static void ExitMainLoop() { + if (!ptrPGE->OnUserDestroy()) { + *bActiveRef = true; + return; + } + platform->ThreadCleanUp(); + platform->ApplicationCleanUp(); + exit(0); + } + +#if defined(__APPLE__) + static void scrollWheelUpdate(id selff, SEL _sel, id theEvent) { + static const SEL deltaYSel = sel_registerName("deltaY"); + +#if defined(__aarch64__) // Thanks ruarq! + double deltaY = ((double (*)(id, SEL))objc_msgSend)(theEvent, deltaYSel); +#else + double deltaY = ((double (*)(id, SEL))objc_msgSend_fpret)(theEvent, deltaYSel); +#endif + + for (int i = 0; i < abs(deltaY); i++) { + if (deltaY > 0) { + ptrPGE->olc_UpdateMouseWheel(-1); + } + else if (deltaY < 0) { + ptrPGE->olc_UpdateMouseWheel(1); + } + } + } +#endif + static void ThreadFunct() { +#if defined(__APPLE__) + static bool hasEnabledCocoa = false; + if (!hasEnabledCocoa) { + // Objective-C Wizardry + Class NSApplicationClass = objc_getClass("NSApplication"); + + // NSApp = [NSApplication sharedApplication] + SEL sharedApplicationSel = sel_registerName("sharedApplication"); + id NSApp = ((id(*)(Class, SEL))objc_msgSend)(NSApplicationClass, sharedApplicationSel); + // window = [NSApp mainWindow] + SEL mainWindowSel = sel_registerName("mainWindow"); + id window = ((id(*)(id, SEL))objc_msgSend)(NSApp, mainWindowSel); + + // [window setStyleMask: NSWindowStyleMaskClosable | ~NSWindowStyleMaskResizable] + SEL setStyleMaskSel = sel_registerName("setStyleMask:"); + ((void (*)(id, SEL, NSUInteger))objc_msgSend)(window, setStyleMaskSel, 7); + + hasEnabledCocoa = true; + } +#endif + if (!*bActiveRef) { + ExitMainLoop(); + return; + } + glutPostRedisplay(); + } + + static void DrawFunct() { + ptrPGE->olc_CoreUpdate(); + } + + virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override + { +#if defined(__APPLE__) + Class GLUTViewClass = objc_getClass("GLUTView"); + + SEL scrollWheelSel = sel_registerName("scrollWheel:"); + bool resultAddMethod = class_addMethod(GLUTViewClass, scrollWheelSel, (IMP)scrollWheelUpdate, "v@:@"); + assert(resultAddMethod); +#endif + + renderer->PrepareDevice(); + + if (bFullScreen) + { + vWindowSize.x = glutGet(GLUT_SCREEN_WIDTH); + vWindowSize.y = glutGet(GLUT_SCREEN_HEIGHT); + glutFullScreen(); + } + else + { + if (vWindowSize.x > glutGet(GLUT_SCREEN_WIDTH) || vWindowSize.y > glutGet(GLUT_SCREEN_HEIGHT)) + { + perror("ERROR: The specified window dimensions do not fit on your screen\n"); + return olc::FAIL; + } + glutReshapeWindow(vWindowSize.x, vWindowSize.y - 1); + } + + // Create Keyboard Mapping + mapKeys[0x00] = Key::NONE; + mapKeys['A'] = Key::A; mapKeys['B'] = Key::B; mapKeys['C'] = Key::C; mapKeys['D'] = Key::D; mapKeys['E'] = Key::E; + mapKeys['F'] = Key::F; mapKeys['G'] = Key::G; mapKeys['H'] = Key::H; mapKeys['I'] = Key::I; mapKeys['J'] = Key::J; + mapKeys['K'] = Key::K; mapKeys['L'] = Key::L; mapKeys['M'] = Key::M; mapKeys['N'] = Key::N; mapKeys['O'] = Key::O; + mapKeys['P'] = Key::P; mapKeys['Q'] = Key::Q; mapKeys['R'] = Key::R; mapKeys['S'] = Key::S; mapKeys['T'] = Key::T; + mapKeys['U'] = Key::U; mapKeys['V'] = Key::V; mapKeys['W'] = Key::W; mapKeys['X'] = Key::X; mapKeys['Y'] = Key::Y; + mapKeys['Z'] = Key::Z; + + mapKeys[GLUT_KEY_F1] = Key::F1; mapKeys[GLUT_KEY_F2] = Key::F2; mapKeys[GLUT_KEY_F3] = Key::F3; mapKeys[GLUT_KEY_F4] = Key::F4; + mapKeys[GLUT_KEY_F5] = Key::F5; mapKeys[GLUT_KEY_F6] = Key::F6; mapKeys[GLUT_KEY_F7] = Key::F7; mapKeys[GLUT_KEY_F8] = Key::F8; + mapKeys[GLUT_KEY_F9] = Key::F9; mapKeys[GLUT_KEY_F10] = Key::F10; mapKeys[GLUT_KEY_F11] = Key::F11; mapKeys[GLUT_KEY_F12] = Key::F12; + + mapKeys[GLUT_KEY_DOWN] = Key::DOWN; mapKeys[GLUT_KEY_LEFT] = Key::LEFT; mapKeys[GLUT_KEY_RIGHT] = Key::RIGHT; mapKeys[GLUT_KEY_UP] = Key::UP; + mapKeys[13] = Key::ENTER; + + mapKeys[127] = Key::BACK; mapKeys[27] = Key::ESCAPE; + mapKeys[9] = Key::TAB; mapKeys[GLUT_KEY_HOME] = Key::HOME; + mapKeys[GLUT_KEY_END] = Key::END; mapKeys[GLUT_KEY_PAGE_UP] = Key::PGUP; mapKeys[GLUT_KEY_PAGE_DOWN] = Key::PGDN; mapKeys[GLUT_KEY_INSERT] = Key::INS; + mapKeys[32] = Key::SPACE; mapKeys[46] = Key::PERIOD; + + mapKeys[48] = Key::K0; mapKeys[49] = Key::K1; mapKeys[50] = Key::K2; mapKeys[51] = Key::K3; mapKeys[52] = Key::K4; + mapKeys[53] = Key::K5; mapKeys[54] = Key::K6; mapKeys[55] = Key::K7; mapKeys[56] = Key::K8; mapKeys[57] = Key::K9; + + // NOTE: MISSING KEYS :O + + glutKeyboardFunc([](unsigned char key, int x, int y) -> void { + switch (glutGetModifiers()) { + case 0: //This is when there are no modifiers + if ('a' <= key && key <= 'z') key -= 32; + break; + case GLUT_ACTIVE_SHIFT: + ptrPGE->olc_UpdateKeyState(Key::SHIFT, true); + break; + case GLUT_ACTIVE_CTRL: + if ('a' <= key && key <= 'z') key -= 32; + ptrPGE->olc_UpdateKeyState(Key::CTRL, true); + break; + case GLUT_ACTIVE_ALT: + if ('a' <= key && key <= 'z') key -= 32; + break; + } + + if (mapKeys[key]) + ptrPGE->olc_UpdateKeyState(mapKeys[key], true); + }); + + glutKeyboardUpFunc([](unsigned char key, int x, int y) -> void { + switch (glutGetModifiers()) { + case 0: //This is when there are no modifiers + if ('a' <= key && key <= 'z') key -= 32; + break; + case GLUT_ACTIVE_SHIFT: + ptrPGE->olc_UpdateKeyState(Key::SHIFT, false); + break; + case GLUT_ACTIVE_CTRL: + if ('a' <= key && key <= 'z') key -= 32; + ptrPGE->olc_UpdateKeyState(Key::CTRL, false); + break; + case GLUT_ACTIVE_ALT: + if ('a' <= key && key <= 'z') key -= 32; + //No ALT in PGE + break; + } + + if (mapKeys[key]) + ptrPGE->olc_UpdateKeyState(mapKeys[key], false); + }); + + //Special keys + glutSpecialFunc([](int key, int x, int y) -> void { + if (mapKeys[key]) + ptrPGE->olc_UpdateKeyState(mapKeys[key], true); + }); + + glutSpecialUpFunc([](int key, int x, int y) -> void { + if (mapKeys[key]) + ptrPGE->olc_UpdateKeyState(mapKeys[key], false); + }); + + glutMouseFunc([](int button, int state, int x, int y) -> void { + switch (button) { + case GLUT_LEFT_BUTTON: + if (state == GLUT_UP) ptrPGE->olc_UpdateMouseState(0, false); + else if (state == GLUT_DOWN) ptrPGE->olc_UpdateMouseState(0, true); + break; + case GLUT_MIDDLE_BUTTON: + if (state == GLUT_UP) ptrPGE->olc_UpdateMouseState(2, false); + else if (state == GLUT_DOWN) ptrPGE->olc_UpdateMouseState(2, true); + break; + case GLUT_RIGHT_BUTTON: + if (state == GLUT_UP) ptrPGE->olc_UpdateMouseState(1, false); + else if (state == GLUT_DOWN) ptrPGE->olc_UpdateMouseState(1, true); + break; + } + }); + + auto mouseMoveCall = [](int x, int y) -> void { + ptrPGE->olc_UpdateMouse(x, y); + }; + + glutMotionFunc(mouseMoveCall); + glutPassiveMotionFunc(mouseMoveCall); + + glutEntryFunc([](int state) -> void { + if (state == GLUT_ENTERED) ptrPGE->olc_UpdateKeyFocus(true); + else if (state == GLUT_LEFT) ptrPGE->olc_UpdateKeyFocus(false); + }); + + glutDisplayFunc(DrawFunct); + glutIdleFunc(ThreadFunct); + + return olc::OK; + } + + virtual olc::rcode SetWindowTitle(const std::string& s) override + { + glutSetWindowTitle(s.c_str()); + return olc::OK; + } + + virtual olc::rcode StartSystemEventLoop() override { + glutMainLoop(); + return olc::OK; + } + + virtual olc::rcode HandleSystemEvent() override + { + return olc::OK; + } + }; + + std::atomic* Platform_GLUT::bActiveRef{ nullptr }; + + //Custom Start + olc::rcode PixelGameEngine::Start() + { + if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL; + + // Construct the window + if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL; + olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y); + + if (platform->ThreadStartUp() == olc::FAIL) return olc::FAIL; + olc_PrepareEngine(); + if (!OnUserCreate()) return olc::FAIL; + Platform_GLUT::bActiveRef = &bAtomActive; + glutWMCloseFunc(Platform_GLUT::ExitMainLoop); + bAtomActive = true; + platform->StartSystemEventLoop(); + + //This code will not even be run but why not + if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL; + + return olc::OK; + } +} + +#endif +// O------------------------------------------------------------------------------O +// | END PLATFORM: GLUT | +// O------------------------------------------------------------------------------O +#pragma endregion + + +#pragma region platform_emscripten +// O------------------------------------------------------------------------------O +// | START PLATFORM: Emscripten - Totally Game Changing... | +// O------------------------------------------------------------------------------O + +// +// Firstly a big mega thank you to members of the OLC Community for sorting this +// out. Making a browser compatible version has been a priority for quite some +// time, but I lacked the expertise to do it. This awesome feature is possible +// because a group of former strangers got together and formed friendships over +// their shared passion for code. If anything demonstrates how powerful helping +// each other can be, it's this. - Javidx9 + +// Emscripten Platform: MaGetzUb, Moros1138, Slavka, Dandistine, Gorbit99, Bispoo +// also: Ishidex, Gusgo99, SlicEnDicE, Alexio + + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + +#include +#include + +extern "C" +{ + EMSCRIPTEN_KEEPALIVE inline int olc_OnPageUnload() + { olc::platform->ApplicationCleanUp(); return 0; } +} + +namespace olc +{ + class Platform_Emscripten : public olc::Platform + { + public: + + virtual olc::rcode ApplicationStartUp() override + { return olc::rcode::OK; } + + virtual olc::rcode ApplicationCleanUp() override + { ThreadCleanUp(); return olc::rcode::OK; } + + virtual olc::rcode ThreadStartUp() override + { return olc::rcode::OK; } + + virtual olc::rcode ThreadCleanUp() override + { renderer->DestroyDevice(); return olc::OK; } + + virtual olc::rcode CreateGraphics(bool bFullScreen, bool bEnableVSYNC, const olc::vi2d& vViewPos, const olc::vi2d& vViewSize) override + { + if (renderer->CreateDevice({}, bFullScreen, bEnableVSYNC) == olc::rcode::OK) + { + renderer->UpdateViewport(vViewPos, vViewSize); + return olc::rcode::OK; + } + else + return olc::rcode::FAIL; + } + + virtual olc::rcode CreateWindowPane(const olc::vi2d& vWindowPos, olc::vi2d& vWindowSize, bool bFullScreen) override + { + emscripten_set_canvas_element_size("#canvas", vWindowSize.x, vWindowSize.y); + + mapKeys[DOM_PK_UNKNOWN] = Key::NONE; + mapKeys[DOM_PK_A] = Key::A; mapKeys[DOM_PK_B] = Key::B; mapKeys[DOM_PK_C] = Key::C; mapKeys[DOM_PK_D] = Key::D; + mapKeys[DOM_PK_E] = Key::E; mapKeys[DOM_PK_F] = Key::F; mapKeys[DOM_PK_G] = Key::G; mapKeys[DOM_PK_H] = Key::H; + mapKeys[DOM_PK_I] = Key::I; mapKeys[DOM_PK_J] = Key::J; mapKeys[DOM_PK_K] = Key::K; mapKeys[DOM_PK_L] = Key::L; + mapKeys[DOM_PK_M] = Key::M; mapKeys[DOM_PK_N] = Key::N; mapKeys[DOM_PK_O] = Key::O; mapKeys[DOM_PK_P] = Key::P; + mapKeys[DOM_PK_Q] = Key::Q; mapKeys[DOM_PK_R] = Key::R; mapKeys[DOM_PK_S] = Key::S; mapKeys[DOM_PK_T] = Key::T; + mapKeys[DOM_PK_U] = Key::U; mapKeys[DOM_PK_V] = Key::V; mapKeys[DOM_PK_W] = Key::W; mapKeys[DOM_PK_X] = Key::X; + mapKeys[DOM_PK_Y] = Key::Y; mapKeys[DOM_PK_Z] = Key::Z; + mapKeys[DOM_PK_0] = Key::K0; mapKeys[DOM_PK_1] = Key::K1; mapKeys[DOM_PK_2] = Key::K2; + mapKeys[DOM_PK_3] = Key::K3; mapKeys[DOM_PK_4] = Key::K4; mapKeys[DOM_PK_5] = Key::K5; + mapKeys[DOM_PK_6] = Key::K6; mapKeys[DOM_PK_7] = Key::K7; mapKeys[DOM_PK_8] = Key::K8; + mapKeys[DOM_PK_9] = Key::K9; + mapKeys[DOM_PK_F1] = Key::F1; mapKeys[DOM_PK_F2] = Key::F2; mapKeys[DOM_PK_F3] = Key::F3; mapKeys[DOM_PK_F4] = Key::F4; + mapKeys[DOM_PK_F5] = Key::F5; mapKeys[DOM_PK_F6] = Key::F6; mapKeys[DOM_PK_F7] = Key::F7; mapKeys[DOM_PK_F8] = Key::F8; + mapKeys[DOM_PK_F9] = Key::F9; mapKeys[DOM_PK_F10] = Key::F10; mapKeys[DOM_PK_F11] = Key::F11; mapKeys[DOM_PK_F12] = Key::F12; + mapKeys[DOM_PK_ARROW_UP] = Key::UP; mapKeys[DOM_PK_ARROW_DOWN] = Key::DOWN; + mapKeys[DOM_PK_ARROW_LEFT] = Key::LEFT; mapKeys[DOM_PK_ARROW_RIGHT] = Key::RIGHT; + mapKeys[DOM_PK_SPACE] = Key::SPACE; mapKeys[DOM_PK_TAB] = Key::TAB; + mapKeys[DOM_PK_SHIFT_LEFT] = Key::SHIFT; mapKeys[DOM_PK_SHIFT_RIGHT] = Key::SHIFT; + mapKeys[DOM_PK_CONTROL_LEFT] = Key::CTRL; mapKeys[DOM_PK_CONTROL_RIGHT] = Key::CTRL; + mapKeys[DOM_PK_INSERT] = Key::INS; mapKeys[DOM_PK_DELETE] = Key::DEL; mapKeys[DOM_PK_HOME] = Key::HOME; + mapKeys[DOM_PK_END] = Key::END; mapKeys[DOM_PK_PAGE_UP] = Key::PGUP; mapKeys[DOM_PK_PAGE_DOWN] = Key::PGDN; + mapKeys[DOM_PK_BACKSPACE] = Key::BACK; mapKeys[DOM_PK_ESCAPE] = Key::ESCAPE; + mapKeys[DOM_PK_ENTER] = Key::ENTER; mapKeys[DOM_PK_NUMPAD_EQUAL] = Key::EQUALS; + mapKeys[DOM_PK_NUMPAD_ENTER] = Key::ENTER; mapKeys[DOM_PK_PAUSE] = Key::PAUSE; + mapKeys[DOM_PK_SCROLL_LOCK] = Key::SCROLL; + mapKeys[DOM_PK_NUMPAD_0] = Key::NP0; mapKeys[DOM_PK_NUMPAD_1] = Key::NP1; mapKeys[DOM_PK_NUMPAD_2] = Key::NP2; + mapKeys[DOM_PK_NUMPAD_3] = Key::NP3; mapKeys[DOM_PK_NUMPAD_4] = Key::NP4; mapKeys[DOM_PK_NUMPAD_5] = Key::NP5; + mapKeys[DOM_PK_NUMPAD_6] = Key::NP6; mapKeys[DOM_PK_NUMPAD_7] = Key::NP7; mapKeys[DOM_PK_NUMPAD_8] = Key::NP8; + mapKeys[DOM_PK_NUMPAD_9] = Key::NP9; + mapKeys[DOM_PK_NUMPAD_MULTIPLY] = Key::NP_MUL; mapKeys[DOM_PK_NUMPAD_DIVIDE] = Key::NP_DIV; + mapKeys[DOM_PK_NUMPAD_ADD] = Key::NP_ADD; mapKeys[DOM_PK_NUMPAD_SUBTRACT] = Key::NP_SUB; + mapKeys[DOM_PK_NUMPAD_DECIMAL] = Key::NP_DECIMAL; + mapKeys[DOM_PK_PERIOD] = Key::PERIOD; mapKeys[DOM_PK_EQUAL] = Key::EQUALS; + mapKeys[DOM_PK_COMMA] = Key::COMMA; mapKeys[DOM_PK_MINUS] = Key::MINUS; + mapKeys[DOM_PK_CAPS_LOCK] = Key::CAPS_LOCK; + mapKeys[DOM_PK_SEMICOLON] = Key::OEM_1; mapKeys[DOM_PK_SLASH] = Key::OEM_2; mapKeys[DOM_PK_BACKQUOTE] = Key::OEM_3; + mapKeys[DOM_PK_BRACKET_LEFT] = Key::OEM_4; mapKeys[DOM_PK_BACKSLASH] = Key::OEM_5; mapKeys[DOM_PK_BRACKET_RIGHT] = Key::OEM_6; + mapKeys[DOM_PK_QUOTE] = Key::OEM_7; mapKeys[DOM_PK_BACKSLASH] = Key::OEM_8; + + // Keyboard Callbacks + emscripten_set_keydown_callback("#canvas", 0, 1, keyboard_callback); + emscripten_set_keyup_callback("#canvas", 0, 1, keyboard_callback); + + // Mouse Callbacks + emscripten_set_wheel_callback("#canvas", 0, 1, wheel_callback); + emscripten_set_mousedown_callback("#canvas", 0, 1, mouse_callback); + emscripten_set_mouseup_callback("#canvas", 0, 1, mouse_callback); + emscripten_set_mousemove_callback("#canvas", 0, 1, mouse_callback); + + // Touch Callbacks + emscripten_set_touchstart_callback("#canvas", 0, 1, touch_callback); + emscripten_set_touchmove_callback("#canvas", 0, 1, touch_callback); + emscripten_set_touchend_callback("#canvas", 0, 1, touch_callback); + + // Canvas Focus Callbacks + emscripten_set_blur_callback("#canvas", 0, 1, focus_callback); + emscripten_set_focus_callback("#canvas", 0, 1, focus_callback); + +#pragma warning disable format + EM_ASM( window.onunload = Module._olc_OnPageUnload; ); + + // IMPORTANT! - Sorry About This... + // + // In order to handle certain browser based events, such as resizing and + // going to full screen, we have to effectively inject code into the container + // running the PGE. Yes, I vomited about 11 times too when the others were + // convincing me this is the future. Well, this isnt the future, and if it + // were to be, I want no part of what must be a miserable distopian free + // for all of anarchic code injection to get rudimentary events like "Resize()". + // + // Wake up people! Of course theres a spoon. There has to be to keep feeding + // the giant web baby. + + + // Fullscreen and Resize Observers + EM_ASM({ + + // cache for reuse + Module._olc_EmscriptenShellCss = "width: 100%; height: 70vh; margin-left: auto; margin-right: auto;"; + + // width / height = aspect ratio + Module._olc_WindowAspectRatio = $0 / $1; + Module.canvas.parentNode.addEventListener("resize", function(e) { + + if (e.defaultPrevented) { e.stopPropagation(); return; } + var viewWidth = e.detail.width; + var viewHeight = e.detail.width / Module._olc_WindowAspectRatio; + if (viewHeight > e.detail.height) + { + viewHeight = e.detail.height; + viewWidth = e.detail.height * Module._olc_WindowAspectRatio; + } + + if (Module.canvas.parentNode.className == 'emscripten_border') + Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss + " width: " + viewWidth.toString() + "px; height: " + viewHeight.toString() + "px;"; + + Module.canvas.setAttribute("width", viewWidth); + Module.canvas.setAttribute("height", viewHeight); + + if (document.fullscreenElement != null) + { + var top = (e.detail.height - viewHeight) / 2; + var left = (e.detail.width - viewWidth) / 2; + Module.canvas.style.position = "fixed"; + Module.canvas.style.top = top.toString() + "px"; + Module.canvas.style.left = left.toString() + "px"; + Module.canvas.style.width = ""; + Module.canvas.style.height = ""; + } + + // trigger PGE update + Module._olc_PGE_UpdateWindowSize(viewWidth, viewHeight); + // this is really only needed when enter/exiting fullscreen + Module.canvas.focus(); + // prevent this event from ever affecting the document beyond this element + e.stopPropagation(); + }); + + // helper function to prevent repeating the same code everywhere + Module._olc_ResizeCanvas = function() + { + // yes, we still have to wait, sigh.. + setTimeout(function() + { + // if default template, stretch width as well + if (Module.canvas.parentNode.className == 'emscripten_border') + Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss; + + // override it's styling so we can get it's stretched size + Module.canvas.style.cssText = "width: 100%; height: 100%; outline: none;"; + + // setup custom resize event + var resizeEvent = new CustomEvent('resize', + { + detail: { + width: Module.canvas.clientWidth, + height : Module.canvas.clientHeight + }, + bubbles : true, + cancelable : true + }); + + // trigger custom resize event on canvas element + Module.canvas.dispatchEvent(resizeEvent); + }, 50); + }; + + + // Disable Refresh Gesture on mobile + document.body.style.cssText += " overscroll-behavior-y: contain;"; + + if (Module.canvas.parentNode.className == 'emscripten_border') + { + // force body to have no margin in emscripten's minimal shell + document.body.style.margin = "0"; + Module.canvas.parentNode.style.cssText = Module._olc_EmscriptenShellCss; + } + + Module._olc_ResizeCanvas(); + + // observe and react to resizing of the container element + var resizeObserver = new ResizeObserver(function(entries) {Module._olc_ResizeCanvas();}).observe(Module.canvas.parentNode); + + // observe and react to changes that occur when entering/exiting fullscreen + var mutationObserver = new MutationObserver(function(mutationsList, observer) + { + // a change has occurred, let's check them out! + for (var i = 0; i < mutationsList.length; i++) + { + // cycle through all of the newly added elements + for (var j = 0; j < mutationsList[i].addedNodes.length; j++) + { + // if this element is a our canvas, trigger resize + if (mutationsList[i].addedNodes[j].id == 'canvas') + Module._olc_ResizeCanvas(); + } + } + }).observe(Module.canvas.parentNode, + { + attributes: false, + childList : true, + subtree : false + }); + + // add resize listener on window + window.addEventListener("resize", function(e) { Module._olc_ResizeCanvas(); }); + + }, vWindowSize.x, vWindowSize.y); // Fullscreen and Resize Observers +#pragma warning restore format + return olc::rcode::OK; + } + + // Interface PGE's UpdateWindowSize, for use in Javascript + void UpdateWindowSize(int width, int height) + { + ptrPGE->olc_UpdateWindowSize(width, height); + } + + //TY Gorbit + static EM_BOOL focus_callback(int eventType, const EmscriptenFocusEvent* focusEvent, void* userData) + { + if (eventType == EMSCRIPTEN_EVENT_BLUR) + { + ptrPGE->olc_UpdateKeyFocus(false); + ptrPGE->olc_UpdateMouseFocus(false); + } + else if (eventType == EMSCRIPTEN_EVENT_FOCUS) + { + ptrPGE->olc_UpdateKeyFocus(true); + ptrPGE->olc_UpdateMouseFocus(true); + } + + return 0; + } + + //TY Moros + static EM_BOOL keyboard_callback(int eventType, const EmscriptenKeyboardEvent* e, void* userData) + { + if (eventType == EMSCRIPTEN_EVENT_KEYDOWN) + ptrPGE->olc_UpdateKeyState(mapKeys[emscripten_compute_dom_pk_code(e->code)], true); + + // THANK GOD!! for this compute function. And thanks Dandistine for pointing it out! + if (eventType == EMSCRIPTEN_EVENT_KEYUP) + ptrPGE->olc_UpdateKeyState(mapKeys[emscripten_compute_dom_pk_code(e->code)], false); + + //Consume keyboard events so that keys like F1 and F5 don't do weird things + return EM_TRUE; + } + + //TY Moros + static EM_BOOL wheel_callback(int eventType, const EmscriptenWheelEvent* e, void* userData) + { + if (eventType == EMSCRIPTEN_EVENT_WHEEL) + ptrPGE->olc_UpdateMouseWheel(-1 * e->deltaY); + + return EM_TRUE; + } + + //TY Bispoo + static EM_BOOL touch_callback(int eventType, const EmscriptenTouchEvent* e, void* userData) + { + // Move + if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) + { + ptrPGE->olc_UpdateMouse(e->touches->targetX, e->touches->targetY); + } + + // Start + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) + { + ptrPGE->olc_UpdateMouse(e->touches->targetX, e->touches->targetY); + ptrPGE->olc_UpdateMouseState(0, true); + } + + // End + if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) + { + ptrPGE->olc_UpdateMouseState(0, false); + } + + return EM_TRUE; + } + + //TY Moros + static EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent* e, void* userData) + { + //Mouse Movement + if (eventType == EMSCRIPTEN_EVENT_MOUSEMOVE) + ptrPGE->olc_UpdateMouse(e->targetX, e->targetY); + + + //Mouse button press + if (e->button == 0) // left click + { + if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) + ptrPGE->olc_UpdateMouseState(0, true); + else if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) + ptrPGE->olc_UpdateMouseState(0, false); + } + + if (e->button == 2) // right click + { + if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) + ptrPGE->olc_UpdateMouseState(1, true); + else if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) + ptrPGE->olc_UpdateMouseState(1, false); + + } + + if (e->button == 1) // middle click + { + if (eventType == EMSCRIPTEN_EVENT_MOUSEDOWN) + ptrPGE->olc_UpdateMouseState(2, true); + else if (eventType == EMSCRIPTEN_EVENT_MOUSEUP) + ptrPGE->olc_UpdateMouseState(2, false); + + //at the moment only middle mouse needs to consume events. + return EM_TRUE; + } + + return EM_FALSE; + } + + + virtual olc::rcode SetWindowTitle(const std::string& s) override + { emscripten_set_window_title(s.c_str()); return olc::OK; } + + virtual olc::rcode StartSystemEventLoop() override + { return olc::OK; } + + virtual olc::rcode HandleSystemEvent() override + { return olc::OK; } + + static void MainLoop() + { + olc::Platform::ptrPGE->olc_CoreUpdate(); + if (!ptrPGE->olc_IsRunning()) + { + if (ptrPGE->OnUserDestroy()) + { + emscripten_cancel_main_loop(); + platform->ApplicationCleanUp(); + } + else + { + ptrPGE->olc_Reanimate(); + } + } + } + }; + + //Emscripten needs a special Start function + //Much of this is usually done in EngineThread, but that isn't used here + olc::rcode PixelGameEngine::Start() + { + if (platform->ApplicationStartUp() != olc::OK) return olc::FAIL; + + // Construct the window + if (platform->CreateWindowPane({ 30,30 }, vWindowSize, bFullScreen) != olc::OK) return olc::FAIL; + olc_UpdateWindowSize(vWindowSize.x, vWindowSize.y); + + // Some implementations may form an event loop here + if (platform->ThreadStartUp() == olc::FAIL) return olc::FAIL; + + // Do engine context specific initialisation + olc_PrepareEngine(); + + // Consider the "thread" started + bAtomActive = true; + + // Create user resources as part of this thread + for (auto& ext : vExtensions) ext->OnBeforeUserCreate(); + if (!OnUserCreate()) bAtomActive = false; + for (auto& ext : vExtensions) ext->OnAfterUserCreate(); + + platform->StartSystemEventLoop(); + + //This causes a heap memory corruption in Emscripten for some reason + //Platform_Emscripten::bActiveRef = &bAtomActive; + emscripten_set_main_loop(&Platform_Emscripten::MainLoop, 0, 1); + + // Wait for thread to be exited + if (platform->ApplicationCleanUp() != olc::OK) return olc::FAIL; + return olc::OK; + } +} + +extern "C" +{ + EMSCRIPTEN_KEEPALIVE inline void olc_PGE_UpdateWindowSize(int width, int height) + { + emscripten_set_canvas_element_size("#canvas", width, height); + // Thanks slavka + ((olc::Platform_Emscripten*)olc::platform.get())->UpdateWindowSize(width, height); + } +} + +#endif +// O------------------------------------------------------------------------------O +// | END PLATFORM: Emscripten | +// O------------------------------------------------------------------------------O +#pragma endregion + + +#endif // Headless + +// O------------------------------------------------------------------------------O +// | olcPixelGameEngine Auto-Configuration | +// O------------------------------------------------------------------------------O +#pragma region pge_config +namespace olc +{ + void PixelGameEngine::olc_ConfigureSystem() + { + +#if !defined(OLC_PGE_HEADLESS) + +#if defined(OLC_IMAGE_GDI) + olc::Sprite::loader = std::make_unique(); +#endif + +#if defined(OLC_IMAGE_LIBPNG) + olc::Sprite::loader = std::make_unique(); +#endif + +#if defined(OLC_IMAGE_STB) + olc::Sprite::loader = std::make_unique(); +#endif + +#if defined(OLC_IMAGE_CUSTOM_EX) + olc::Sprite::loader = std::make_unique(); +#endif + + + + +#if defined(OLC_PLATFORM_WINAPI) + platform = std::make_unique(); +#endif + +#if defined(OLC_PLATFORM_X11) + platform = std::make_unique(); +#endif + +#if defined(OLC_PLATFORM_GLUT) + platform = std::make_unique(); +#endif + +#if defined(OLC_PLATFORM_EMSCRIPTEN) + platform = std::make_unique(); +#endif + +#if defined(OLC_PLATFORM_CUSTOM_EX) + platform = std::make_unique(); +#endif + + + +#if defined(OLC_GFX_OPENGL10) + renderer = std::make_unique(); +#endif + +#if defined(OLC_GFX_OPENGL33) + renderer = std::make_unique(); +#endif + +#if defined(OLC_GFX_OPENGLES2) + renderer = std::make_unique(); +#endif + +#if defined(OLC_GFX_DIRECTX10) + renderer = std::make_unique(); +#endif + +#if defined(OLC_GFX_DIRECTX11) + renderer = std::make_unique(); +#endif + +#if defined(OLC_GFX_CUSTOM_EX) + renderer = std::make_unique(); +#endif + + // Associate components with PGE instance + platform->ptrPGE = this; + renderer->ptrPGE = this; +#else + olc::Sprite::loader = nullptr; + platform = nullptr; + renderer = nullptr; +#endif + } +} + +#pragma endregion + +#endif // End OLC_PGE_APPLICATION + +// O------------------------------------------------------------------------------O +// | END OF OLC_PGE_APPLICATION | +// O------------------------------------------------------------------------------O + diff --git a/pixelGameEngine.h b/pixelGameEngine.h index 37ffb75..f2a8ee2 100644 --- a/pixelGameEngine.h +++ b/pixelGameEngine.h @@ -1111,6 +1111,7 @@ namespace olc std::string TextEntryGetString() const; int32_t TextEntryGetCursor() const; bool IsTextEntryEnabled() const; + void SetFPSDisplay(bool display); @@ -1172,7 +1173,8 @@ namespace olc bool bEnableVSYNC = false; float fFrameTimer = 1.0f; float fLastElapsed = 0.0f; - int nFrameCount = 0; + int nFrameCount = 0; + bool showFPS = true; bool bSuspendTextureTransfer = false; Renderable fontRenderable; std::vector vLayers; @@ -3198,6 +3200,8 @@ namespace olc bool PixelGameEngine::IsTextEntryEnabled() const { return bTextEntryEnable; } + void PixelGameEngine::SetFPSDisplay(bool display) + { showFPS=display; } void PixelGameEngine::UpdateTextEntry() @@ -3536,7 +3540,7 @@ namespace olc { nLastFPS = nFrameCount; fFrameTimer -= 1.0f; - std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + " - FPS: " + std::to_string(nFrameCount); + std::string sTitle = "OneLoneCoder.com - Pixel Game Engine - " + sAppName + ((showFPS)?" - FPS: " + std::to_string(nFrameCount):""); platform->SetWindowTitle(sTitle); nFrameCount = 0; }