From 81c12b7867cffd3ac94c76bc23da14077c8e21f2 Mon Sep 17 00:00:00 2001 From: Tangent Date: Mon, 22 Dec 2025 18:22:05 -0700 Subject: [PATCH] initial area a day late to commit .. or 2 ? but whatever --- .gitignore | 1 + ReadMe.md | 60 ++++ Spawn Area.json | 74 +++++ Spawn Area.png | Bin 0 -> 80044 bytes map.json | 78 +++++ run.sh | 5 + src/lib/dkjson.lua | 752 +++++++++++++++++++++++++++++++++++++++++++++ src/main.lua | 240 +++++++++++++++ 8 files changed, 1210 insertions(+) create mode 100644 .gitignore create mode 100644 ReadMe.md create mode 100644 Spawn Area.json create mode 100644 Spawn Area.png create mode 100644 map.json create mode 100755 run.sh create mode 100644 src/lib/dkjson.lua create mode 100644 src/main.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..7cf91e4 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,60 @@ +# Immortality-Bae-travelmap +Immortality-Bae is a private family server. + +## travelmap +I made a little program for displaying a simplistic graph of nodes and edges to +make a travel network with calculated distances... specifically for my Minecraft +World. + +Data must be in a map.json file. `nodes` is an object with named arrays of info. +Each node is an `x`, `y`, and `z` position. The optional 4th item is `true` to +display the node's name, a string to display *that* as the node name, or `false` +to completely hide a node. `edges` is an array of arrays of two names of nodes. +An optional 3rd item can be set to `false` to hide an edge, or a string for a +custom label. `edges` can be nonexistent. + +Pan with WASD or arrow keys. +Zoom with +/- keys. +`r` to reload the default map. + +You can drag and drop a JSON file to load it. + +Both nodes and edges can have an extra object with metadata, some of which is +used by this program. An array called `offset` will adjust the positioning of +labels. An array called `color` will set the color via 0 to 1 RGBA values. + +Added `areas` which are exactly like nodes except that they always draw a white +label and can have a custom radius specified. Drawn between edges and nodes. +Use transparency. 25% works well. +- `box` can be an array of width/height to draw a rectangular shape instead +- `radius` can be an array of x/z radii to draw an ellipse instead of a circle +- `mode` can be "line" to draw an outline instead of filling +- `image` can be a file path, in which case `size` must be an array of desired + width/height + - (path relative to the JSON file? or relative to the executable?) + +Images are only loaded if they will be displayed. + +## Example JSON + +```json +{ + "nodes": { + "TestA": [100, 0, 100], + "TestB": [150, 0, 200], + "TestC": [300, 0, -200, true], + "TestE": [1200, 0, 200, "Displayed Name"], + "TestFarLeft": [-200, 5000, 50], + "TestFarBottomRight": [1200, 0, 2000, true], + "Hidden Node": [0, 0, 0, false] + }, + "edges": [ + ["TestA", "TestB"], + ["TestA", "TestC"], + ["TestB", "TestC"], + ["TestC", "TestE"], + ["TestE", "TestFarLeft"], + ["Hidden Node", "TestC", false] + ] +} +``` diff --git a/Spawn Area.json b/Spawn Area.json new file mode 100644 index 0000000..46b9143 --- /dev/null +++ b/Spawn Area.json @@ -0,0 +1,74 @@ +{ + "nodes": { + "zombie dungeon": [116, -29, -32, true], + "Rail Factory": [30, 66, -14, true, {"offset": [1, -1]}], + "di'Luna's original boat": [26, 64, -36, true], + "original mine entrance": [16, 63, -13, true, {"offset": [-103, -10]}], + "Ky's houseboat": [-25, 63, -30, true], + "path origin": [16, 63, -18, false], + "factory turn": [28, 64, -19, false], + "b-side di'Luna's underground base": [44, 62, -51, false], + "di'Luna's underground base": [52, 58, -51, true], + "path beside supersmelter": [41, 63, -92, false], + "supersmelter": [61, 59, -92, true], + "back entrance to Battle of Day 194 Monument": [7, 62, -142, false], + "Battle of Day 194 Monument": [-4, 62, -133, true], + "front entrance to Battle of Day 194 Monument": [-17, 62, -123, false], + "non-dock to Battle of Day 194 Monument": [-12, 62, -110, false], + "tiny waterside farm": [-43, 62, -99, true], + "library path split": [-87, 65, -91, false], + "Ky's iron vein mine": [-75, 63, -64, true], + "Bookcase Library": [-97, 65, -91, true], + "first path split": [22, 64, -17, false], + "Birdie Bridge path split": [-14, 69, 17, false], + "original supersmelter": [-11, 66, 37, true, {"offset": [-90, 0]}], + "sugarcane farm": [14, 68, 8, true], + "iron farm": [21, 68, 47, true], + "mob farm (AFK spot)": [62, 183, 43, "mob farm"], + "Tangent's farm area": [-90, 64, 18, true], + "Tangent's iron vein mine (surface)": [-88, 69, 88, "Tangent's iron vein mine"], + "Tangent's original chicken coop": [104, 70, 236, true], + "coop path deadend": [52, 63, 123, false], + "east rail origin": [40, 66, -22, true], + "east rail deadend": [1707, 66, 31, false], + "Ky's 2nd iron vein mine": [-102, -23, -43, true], + "nether portal": [-131, 65, -35, true], + "Ky's 3rd iron vein mine": [-160, 70, -160, true], + "Staircase of Moria": [-33, 65, 64, true, {"offset": [-7, 2]}], + "Slimeless Barrens": [-304, -58, 64, true], + "end of original mineshaft": [15, -61, 112, "bottom of original mine"], + "trial chamber entrance": [-15, 63, 4, true, {"offset": [-133, -18]}], + "trial chamber (top)": [70, -24, 4, "trial chamber", {"offset": [40, -1]}] + }, + "edges": [ + ["original mine entrance", "path origin", ""], + ["path origin", "first path split", ""], + ["first path split", "factory turn", ""], + ["factory turn", "Rail Factory", ""], + ["factory turn", "b-side di'Luna's underground base"], + ["b-side di'Luna's underground base", "di'Luna's underground base"], + ["b-side di'Luna's underground base", "path beside supersmelter"], + ["path beside supersmelter", "supersmelter"], + ["path beside supersmelter", "back entrance to Battle of Day 194 Monument"], + ["front entrance to Battle of Day 194 Monument", "back entrance to Battle of Day 194 Monument", null, {"offset": [-10, -2]}], + ["front entrance to Battle of Day 194 Monument", "non-dock to Battle of Day 194 Monument", ""], + ["front entrance to Battle of Day 194 Monument", "tiny waterside farm", null, {"offset": [-10, 0]}], + ["tiny waterside farm", "library path split"], + ["library path split", "Ky's iron vein mine"], + ["library path split", "Bookcase Library"], + ["first path split", "Birdie Bridge path split", true, {"offset": [-16, 3]}], + ["original supersmelter", "Birdie Bridge path split"], + ["Tangent's farm area", "Birdie Bridge path split"], + ["Tangent's farm area", "Tangent's iron vein mine (surface)"], + ["Tangent's original chicken coop", "coop path deadend"], + ["east rail origin", "east rail deadend", false], + ["Staircase of Moria", "Slimeless Barrens"], + ["original mine entrance", "end of original mineshaft", true, {"offset": [1, 50]}], + ["Ky's iron vein mine", "Ky's 2nd iron vein mine"], + ["trial chamber entrance", "trial chamber (top)", true, {"offset": [40, -1]}] + ], + "areas": { + "spawn village": [44, 67, 55, true, {"radius": 54, "offset": [25, 50]}], + "Tangent's farm area": [-90, 64, 18, null, {"radius": 22}] + } +} diff --git a/Spawn Area.png b/Spawn Area.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef2bb3dcb992640bccf8b61ea1fe6a633b22a61 GIT binary patch literal 80044 zcmbrmcR1F4_&O15@=C;olxhN8L(f#A+YAl&gI5LWP| zJ0k?bZGHmbw-JFL7EK_~+Qk)LmcTdm*ePCfBoN3B693yqcos*8FYb1_pdz<>l$>T4 zBNJuEMf~0|!UfrLnyx>_yWI42uXIcQam)#@qLiU(c@vd#wu9_xicVs%bGAD2!#!dc<885AOV4V{S>C5gEDN|Lv|%<$TP3XBD?IdwBc&TpsM( zR{W!3#%;52Y-nicr){Ikpod!dnE*;0$^XUc4i|$zFQIV7wXEA`lPvX*?GXZ@*OUJX z@yZYgAD?U^{_*(44&opG$4k#h*=ZX=Z+p9*V?%PN)F;!N&L2O%Fc1iKgXi@HDz8kI zuTHKm^@p~$w2(6Q_4Q5R-?WCFVbRf|V^#Yl{#0^hm9j+DtM$B8PxJg0$WBjB@8nzD zB(=)T#Whx1-I;6NDn3=3d^MfVt4Z&VhT(@r+)mmfWcmbt1zCA{soBqhRDlCEw1!qz zR%@qJnlCKy6#V?z530X7{SK1uEkx z4<4LN*j!H7e%Gizk|!i2Bqk=twtRWWYq3i~T31_pX|iN0Q@5be#Zy(au`ynJ-rdXG zd~~9qr_pFFYg6lpnA1o`LAQmCjg6%x*U6LiqDMF*f+%PHRPUeaDskz|x7OF!=PUhl zPnpf*cUtO?A3x|B7({HlxvBS>?HXe6iC@qOl${ASTuVw!JeS_)J=d&o;lc%{RgZ=7 zj(lrfTU%Re>xr_(ZgK05r=LH6F6ee2dtNe?$YkKsb=$ULeWCdFn5#th{e29Nn4~iq zzE1osa;$rDqE*#v#dK7INy7EdY?JiV)Ku21SMCF03eVk!Vjewu6d4(*!06^z-500g zn{ywx75BDx=E0*!-a|2FqDLCZ_)5=4?X(o4_pN#y7spLW$`PcbsMu>K8W9meys9}( zQuRjsHidonpFe+|o14r2-1Q!%fX~L-HC>85d+I)ZeA78`^ytxY$GrUfmnkWdn`^UU z-!e)Tepn{B|4!?)t>`k&e)00<^HZulmbOKMk!|T(n+pXMJlx#5ofEMlwlBF3vm8IJ z_FN(+Jbbo}JwXpA8%)FV?1zk_6g9PI37KdN$s zT|Z!_60Qjl^u(@TzrJnTw$rCimnV_NHaO1=T%ZY#h>WB;c<|tX1I^vt-90^O(qqmg zM=1i@EKbKLA3Ai%(0lo&rR7+zFxlR{kB*9e>+Fn;i7B#d=Z#lYnBwE%>B_zl(0q%W zg5tTfk2h}Tb4iciV`DB;-CZp$4lANwSywnCPMtb+`}XY;=LridtDj{a1=W53gpa>{ z`!-(OWnpnqOG|72zI`VnKBe(QHw@uuq!KFZ`riH7df^@f1_r)=|Ng^=4}9fINfi}7 zn;Yxr&!3;`&21WuO*tyLAW zZFv4oQA4A3V8Fz}!a`5)NN8<(TH1l&8l{w(_KJ_b^-0tZMFFrI(1sjqd$)*Klf^-g=zsuvvmvWOTIaaJ-9He4}+| z{s9>=F|n1E6&fBLYFgT1C$Uu1I={!^>}ne`Q587yvGy#(3U5i@s_zB1J)a|Y@7^68 z6g1%ACMzo|C%3aKT-Ca^wpNlcw4y?~OoJtigoGsSl;%nvO>j;Bv|@^SP*SNu7QPo% ze>%3oDaq5lp`ih%e)8l=rIaT%##gko4$AD>wJYDMW9C;dpQ@xo>r0iW8BT5P$ZOST zMO+6zJlQrx65FtVlfJ#WFtPa4F?6cddf&c%4n-2u(uT>+(aLN+RhKVcUhMJNe0JK> zMBuP*l~GQIQw(27{UGYtByNZ%X9R8@*VWggk1xUPgMNwg1=c61i}ihWLZ?s1-+Xqlv&fM>s@`ifCF+Ix=*IeLkJqBUVaMEI zZ0eAQ50zD3yNrMDb0}i;7|$-~@h(YD?%&)P-Te0L8yhRD-mi=NmUK%dM8u$XZNOnU zJ*#}>hhB?beUvg1j7^(s)20IJYiq6&&)di1i#P8X@zZs5bcBV4&7BXC*Dy3xBR$N(fW5`y z$3Y!bUM`i|8c6ovJE?1=w^_EoE}Lx>&nlTLK6dS~x3;vDlq%@~Qj&D7ETD@Lt=HNI zsHtDHkMYpd`-`P%ayB$IMb(-3QKF)+Elodv&bV^2q-)YiEJEpoZxtmeiIUN z{{8Xe$M|k|9W^E8F-lTY{*m9oeSO!Z5~hZS6_}W)sdH_6%7NmVO>;5~E6Ou6CJHLn ztu-_>lpKO=g$rW7TRD4sm-ESGrlp;^5~NI;t<8=8=0_Qn^dvmoKBSlPel`}^9j>oW zPF&2a*l;h>Gq`f4t+~0+PE?zl#<$9@NN=w4O?97KZrOp{q(B<5TkEdXixK+^EWg`+*pThBJY<|X7BC39nOFX~BiXy<5 z5=b&5{AyIa9I1Vg9{Dccs_4Pa{(t}d_v=?pcaK}Q{w*zy^IkrTrSfsmfg&Ksu1}`S zKsv+oaPRLsK`0q_xWa57Yp1rJ77-yM*`9dmmO#vBQ^!k}9&2;I~i&iFQa zOZxQbwkVc@{CpZ(+Ju|LE7mzdA9BB2zH6sm27e?u;B*ZxPP@KK*T~2%7ng#Hip_Wa zWUH&IqdEik@81UutExH|T=OI@PETK761!YAWY)Fx^JnEEJyq$~uV16mOSO}hl#~oQ zxZ$vs*@8nuZLO@{U)iy>k?riy9C-wc@b2Ba5C##kBafavdsZE=pMiXrjN;|X2SRIg z3v7;t)^c%iiQE4I*dSqkZ<=!^Ue&NnV0n`)tgy?bq+v4s(p=IetBrH?K(1J>ni?%hJ*+*ovG!jV77ky=yS%x^V2Wr}x>jgoGwDemb)_rX7Fl znchiRmdiFanMx@vekd;o*;R833Jm}Ka9d1YTbnMRI;_rQm+xKPO~P5?MI~qn<>|yn zMX^)w-NSt2#*NxXM@xE-mtRdk&C2RWi5)5?F5W!+w=!hjvbVQ4H#eU%sVgfhtEs7R zE?K{qP-C2JCwe4J^Hcw{HutwLUnt!fGq*3zGvct@>a?6a0ZWC2g$>5*enj#a^3nwF zVLox%XBm~$>?Aq?7z;{Q8g(xaUk2mze z1O6E0^4l+(_Q+iZpmyo;a?#X$T0bZdqg)%xG&MR}#64AGkw+zS@7}%dR{3X-(b3UW z&hOk>PmYS{kf<9VWwNqreXt9cZ9F3;>{@_h5^6%g;Z1PfTh z_fogn`F2CT2qh3m=c#U$C`skuODx2-B^E3xt)3=?sw*XBkfL6s#~WPp>tgE_&MU^o zzt)y#YmDs;4HGIVDiRVBWLXX~F^R-BpoU|4k?*=O#PGtGoRpM~k?|$_;Nv=z(b3Vn zckcps4ZC#OWvi5U{=K~$3g^yMG1!)d;XS~R!ovai1qB3JdU~YCs(_YUM-l@#xwuA! z?0;16B%^s>Qv>WDuMipc^l7fc;HRafC9Jj5`ER-&i<7s-;suWb_$sTaMk=LLS62^5 z1s{}2N={~S?Y)cr#jBhD*+ii2+qafgd{e~cXW{kg`#*pFeEO6+t6lfnwZruEO6*k> z0sVJ(GcL7dc@H{?UA%bF%()+gw$P~R9(Gk&*nyy=tFN`Ef#_J-*cio~$4k$b{rgg1 zs!%6;i?a_816zS@+hEOFSo{VzFZNzvdHWW(FW;zYJ8#sOlUTt0`^Ck@Yin!os;jXm zXJ=;y9g3=qqF3G|C*QD$J$LThfddD;y}eCyiV6zajrf(lB9k~$)DJT+aaG z1HXoI0D};s4{0A8v=gNWxS`{RYh#gD{N{~aSMhDUi_cOulPpVf?y)mRRMD&&OvffB zPWUOZKI!c>3a=BoJ#-^Pes&}|)YR0p+heM%Jf3{laXvm^^JMB6%JFBa!3lyWR9o=A>?6{sb1!$X_np$fj za56$EPsch>2S+T)sAg!$bkxz#&JK&z%d4z?>=2Plv{(t_lHly<=`HefQqb|m8jAsoMq(}}z{1ip^r*P-d|$qGr>9Dq<}0kZ>1URiebcVkA%FU+ z4ZY?gvfkXKq@tm5M)d%)o>}$`N`fwOKOkU~>cP8rGO4W{?d>9h=8j_V&=(9nr~MNW zdfwiw#|i_gdbzWsr>Ek2^{&v`*_MlZZe`tHzkY>=;@AZ0xi~4g-bouMYmxWkmJp+u z)5i&C&`UmG+U6uVu={M8#E&08DypeXO-#)5*b4xR^`Jt7`6;pn1qG?6X`=Eu6nXw$ zy~$5^{P^+R6i1d;#^M}q^jEJo=2E5S7Z;m;%;r2- zM4v42{P-k{C_YtgQc+UAxz)^)t$l=nfq|ZW1u)*1d)L%ghMqna34*{vuwBNDKGo?-w~l^9q!1+M%9AOql}E@AoS+v zq(I3gCMNv-{Vnp`U0p@l*zVU4UcGwtdG2lK0(fI+MuXd0%yzX^+dS~{H+E-W7pS%7F zB#K)xX3qmdLn|ioJJB?9b*yb{CO;G*6u9IHaQhn)O{#s|Tq9BW`g=Mso}B-`({kEtjt&)uv;dBxQX4-XeEX%I8V z+Ye2o(GOls~5~`2BmXsC}a>``izSY?NOug!P@`NfvY5vRk{arqr>pVO> zoDsiKwIJi@@oFVrA}1#Yh|XCF|7^U6+js0J(wl^~h5PpT7_h#(yZhUmoHBzfJ>E?s+mGZ)5VgNo+u!9m z+W;7XdYqr{eCw8C;w2MP(;!(ETn%gnM#jl`_k&bas?R0P)<(76Bj@-;wb_32PBd>#!1Dxt5|H^ZdCgX_zAG5elO0giZx5S2M># z`8ZYJP*Xw$wz+TBKW!FT89Jv0KcbxE=jWG}O8EHrfM>{hXYfqLp6)`{7+ZG<;5vT+B;OO2Xy_c7y`v=-_}&0tAeX2U=ERJdMKo z@#C#qw?yrK`PKG!Kt}$Sp}U8fODofJJiGRx{0)2i=`@6No&(%C}>+A3lK1ySf%)vjK#>%#$bG zvu97y`a&lpU7V0i;)4edzF6c*N=i}$oM#Kq&@T~YVM!`1TuMNH0UP>C+aj0+sSM=0 zpcCXv;IyK&bOpreH1V4Phqq|c*!VaA5-QsB74OmFFknUPjH`Fg9t%^%x*1vP9&{KS z9BfFC_CaIF%*+JCC@66GJ?&(emzUS9_1^GT7SlHo5t02A6piCH z`eELmi;0t{IAx0d$Bmr!{pCh;YOTDCK}o3FkW3AAbcz8yauW0>$6>MT@s|mdmDyag zadoAmrO!0^w-E18_!fl>M7E_9KY^E4gYFH1n07-S$@LpmNl7egllwA9Lb?7570h9Z zp5cOpdYRA#ddTUtb_0K%Ig@rZeN-ekZ|v{(j60{OsAyooC`mR2Jagef-IX(6E&dm2 zbT4C!V*JaD#?gUjxV6t-re=Ntj+~o2XRNu)klE?~B#qa94OCZj$?Cqs?+5SvNAjFFf<84hrD~Xj)}Fsi z?OO#QB=)$iVQOnyn+0za10CHhA$sh`)T5Psc2oxsV$);yLjjh%ET0TvMKPrXd+*`H zhrPYMfBx9f1gErF@GX%v{XHe#9lTK`CUsB#`RAC-gXUY~Q&ZSnCOIA8#5BP-A341P z<(C|e*W`>p6N{bVio%3y-(BVbz};n2&|sR=VMRvEtE{2|31$sV2P_=WyUK{}=utFA z@KD$)5T^I~-%H{Ml%=Aj1q_l1O8RUnD0O>_fb|Bk(FD)V&H`)fVRkRu7->!h1DhRg zV)wg5-3At>uyWngGp^3$lZgN{xBlr}yLYEb#m2=QIeZw};V5jb;bBMtAf0sN@)j1a z6j_s^ie7SXva-IPa0Yb0X=)0ITZfgZ0g4$IF!~laCH9M$m<~%A`M!PEGOl_q^p0Qm zw}5VQQHe8RXm~hSmPO`cWo2b^vI2Dd8skJuNBBMg)iL5_mks}4jy)&wX;bwQ9Xa|E z=)=*Y@z0*QH=J%C7#hOCVv|#nS_yZ4|4v6#QN`j_6E7)NKrhJ6&-Y##O~nQW$zlv0 zuIH=Z=Hz^$$O`bZG``hm?{Ga;Pt$vAvPbfygNuvHwQDg`Y0yI4k4s2+s?J}~*FRbo z78j??_eq8HdQJy7O>mVF-gRSr%7>4SPbbxOp_eCypJv!>cl6C+hUAet0%U`DNDAcY_We;T%SJvpSQ>ee4HiJPY>0cyYSvtftlk77F0il5(+Cz12#XL2|!56t*g(kByDI z92^gVg8{4-4y685zQXI~W@j1bN+VC{{7)8u?u|CLQ-r@#ia=}w4AnVKFSc-VPfrPc{!6@3*mRnj znsiXzNE4b|M311Ds-}il_ra6jlK0-3<{T#f0D|`C55H81-2w-!cS2=NII!KpgXe9d zqM{b(=i@h)us0#-$SB^tc@w}(Z!Gh~#YhO0E6z#j>0}8^g%2;;;}`9+O-xL_oP66k zA>};&{ji3K45_4^(vV5>&gGt2eMqXFe!T)RH3$Gy%7w+ptC$rF$7bZ_YG$=3G^E4I zy2SBRk#+mse<~_^YzmsvRC78fR8uw5GzI9$@1C_2UOe<)Z6Oaz0y}^}9#Cyx(lr-X z;z#Mu9$MQ!?W)JSSSqL)l!S7MGRgejIQ!|-r(ej-;!YHFs-(87rp|IZj-}T@@;rO? zjw0HdtZJ&GlM{7uU#TXoOFlx~!+S)*#aJC2*!cRGO$5suiLy z6uHd~&EATu9|V;K=OWfO?vp38JFJGk-cwCwU}B=(>kqXEHk9L^?dC%CD`uWhj(`_% zv6*zX7?E8wH0JT{vXYz}fW4UxvxJG?zh_h$z~S?gl9J48gKLaq8$Jv=945ctVCpD( zL?-cZl@S^gtXXpfUW3wTgFiA^?M&j%4-Hy=>CG=JK-gbhUWPZ3aaFvX_i>GJT6#J( zo@5SA9B^SF9g|Ms+}2HUd?cR?O?BE;0$>u_6*|M5?GRjz)yM;_G=&Rj7r1?+=L zLj-Xp&eo83>rCALRPAoh(4}Q!%4)vl>3@(|=XRocbSL;hX7DjTm`-sLZfHghKfKw10J2BwcvaOns9*lAT^|;? zD#;=^4$43rxhc>QSEdal?35wr>A9vix%=-DwGWg{f{*|wy3XV+wh?gY%a?5Kn*i59 z#wVicqj>e{4;+9EKr(#U4|c92Jr|3=ow$OfK` zii!%+V^(HkIC`|Jz?Mm}nXYUTR5wB?MJ#@DeX(b1a`L62v2d(c&K1DfVoH{iCog0^ zZtZUXn5DfDQP@m;T~5=4c%=hd!i?`4blNRxnUs`NQCT@I)={xKMIjMR38C={S~oNk zX0+?5hzR(Ro7h>oPaee5R0eqM z<#YrE2lE-gJxOGDU~?=PL4W{(o!hk7&P$Z@bS${$U7y_x^^>8sH?pSQaX|tfR4vDj2!&Ck}jmR{RXbu<$I3Q+}5^2FPujF z2*toXjceRA?$5%2h9H?-l*rw`<){tif*e8Wb}vCuC@*L9a`1nnli~LmhbSD&LrDsH z^M(B{!c#}aNL588rQkHZild|8%0Q5Sgv9Gh9N>@z}30r9QPB530lsDAf zIcF?%1%=%!1A9#bVxX<=VaBBgyR#`^@Y=q00KAd#GGTwZmz0*4rl#)pa<~b(f-zKq z=yNJdOG|&%|L;9dpmq|=06aaE+B_Yel>sGFemcDI*<+6tSs_>f?*7k3xO>ygtd+|J z#zsp^%bD^oU1~Kza9%f#N7b*cti-9ZuaIm(3_Ut(xxwM#bWKi~2Q|j12B+vlMj{hP z{=WwE|Ge)uyP}`y#w0s;9+3G@YJ4@_wEUB$$#KeN6{(9`vLE7An zO-&G`*!@Q4SCSP%e{roQBtcY0p@fo!Lb+=fG>iX^CyqTv8Sce#N>Wm0EI|}0?V1Vp zPcRRUJOa)MO7fGrZfnc;KR>-68ccY2^`OHZ=B_Scp%@8I5c*%802l3lj@{;~#cfqp z3g4<@esI;G8z-w}Zio60VDHpb)7$$`gX#JIZeY%#0|&xlVlEIpySDar{2&GBjcHD^ zW5k1+U$D35_8@IUAc%6ILSW_0<;y3V+t|n_=&2yc(4 z+1~E*VVzbydTq_qy=6ym4a7*;oAx0?r2mUBj!=rSlM}I=o;KtvsOJbdIN7-4SNDUg z7dxfNBp&laEa14pBRCs+{}e&I%r7X|>IdOZo_t}pQ{93?g?GTaG&QM_n97&^^Lq6- z3(L&x%zX6XiT$N+FtYi@+pPZJJwV#Zvr0+fh zI0I-Qq*FN50RaI`-U2tki^CKF?}(`fR(O|-Ux7pL4!jL&SL=S1IURahTB*&q;Gt`4f5%}>_t&BsX|rBUZ%h631n3Lt zCgm#Q_!&NW^?@_0sjYdrxk$#rE`nkITAqSfF?yf;vJ(X(1;tQ!bOam2?5A3eP6lcr zR;WeZqlXUxzyqp5s9uL(b)OLWVSYa-6~GmJ*+gJ{b?OM2&*+`6?d^bGckbMQxKL)$ z)6*0Bce~mJlKQ^`K%bj)D$;{V24hT*H>!11!hLS!^=ie&ilxkh_OaW@H{7r7Pb@%i zgsDf3nPjcH@q*~J9bxz~ov%h`2D)RuSPDv@Kb@X*smUP=jTGpXJ*||%G zdU|n?sX3@YG8se7hMrQFCmQE;;7SXlLKCz@Xn2KmW`V)MTp&n+f%anYDJdxmp^T^L$o(i8Lkle0Uj6*3M-iZL<;v~e z%57_p{;>byLuMDzUBC7*1SfSeaLe$&6d53Hq9Nv3(P0J1w3mWHRZ$U*f+kQFSP)0a z8S!bt84U<_C^-JS`)JKE@?GaawPDxPg|mZr62;fuyOE^HxH{G`>`%U{TI7w$L|=;i zIaM*s<8TgZ<6XMV`RRz(f-Wfqg*Tj{ZtvBJ!oosaD6xRp<8amv?S+Z<*F9d07}Vpi z5=Kh0q^xWmX`0{T<9g+uMX)WWrd&$8;0EKIJKzLC5_`N=J@a%jx@=N+KSzsyxiY2yQpPgRi2bg7(T_vN|;dNg-J8`;vl%#R0W_o&h01^rc0pKaGUIjgT z_*5wcL8D>m%`l^~s2^Yp-vS;t^YW539oSlztuP@G7d;|sX9S%d z=`f207#Skzny*5oR$yZMbndjq{zG5aQXRnEWeGz92r>}^gLuZP?}HA@N&yqjt6;US zuhh^Y#s#^_Q_7H=2Eu%&RX(kOYgPZWXuK+K)Wyrx;dL*_E7r@Xmi-;%iB$qu#z+LhwKvD_$wZ9SW5+65Jx)uKi&OjF8zj#|$@%k;FtHrzC}yv3u7p z1MlTQcxZ=cXu2KhdEiIaU}KymEkgQ$hF5P1*{}u?Uu`7lKZGzKeK`&0P_Z^s54%S< z-|CFRz(>ohvTq$7%Lsc*tc)b@mz)C}gYQW6T+8u;u{Oo#I^=BOqr}6i@GjHlM$+&j zf&yp-`8HjgG{LaFfA#nK%*OTr9jn9&%gM^#5_(2F= zeI0j#FX~DCpkVC$?Hc&UYlz^ph>9-Z3_IT5ya<0Etm*V=Rb;tN-#6S`X@{Q(+$q7x zcj{DjPR{I<&#H%##V2^GAP*Q|m?5eTMe}Nkj*dAfoArYb7jb)xe_nSGjYGaYG$aI# zNc0E=O|V?g?=LRgh?dovtk_t6s;?hCH3aAJ`nnyQjpkcc=H?fal#-_MwlGiJp)GG5 z`9IWrl%L5-NM5W^^yIWwyyx4rSi(k-|9FUEgwS|{X-WShaj@fE4#P2bzP;@hEq!4f^D>m1suoSWpk3d}n*Nm#)T(6C-amNUR980t0 z;?MJ>%Ux66wWc|vsf=E8%`puN^YhJ3<*=nu;Cp&DTh*kkEiI4xtzmW`o}OL*dw=kC z#1w$YxVgs>rOHT4bL;b`-Dq;$9LDY8=HNj1W_Kj7{tr0rU%vbaUscf5(kig(5Rg%H zaCmEwg+g3-CnTq19EF{eI!MxE;ftj0Ry0sjQO)N@uXBmlVt{aLNV)IrlCEzTxb*J~ zI20jq?ORp3IZfmOl^s;1B-C=tY(D(44dvOop|y?!AHz_uA)B>Xz*hk1g24wx6YgG< z_{H*f0YN@5$~+d4-ce3%g&K%kfjBcZB>W+m?M(^N37K5h78Xew+Rv(tzKqQyBLLgS zy{tSju?m_vlGavM4;fpLTx@EBUJJ5hPK>Uc$uqrANyW&>$i(E+TY2~XePlr#N4_Mc zT#O=mEYn9M-Jfne>o(5Dl0;9^(GklVI8W@zzcF#pl2?QLe`sLf8I_!JZntL|HKV~* z?Q%FavG~lFFF)K8ihTN%=tV|F^`H{9cDJkf*O|P6ex<5vnyn2SNBib&LY+zGwH!9+ z)VSYsUZh@P!8I)5b%20%Cg|}CSGu-HS)mcpZS~6xjxRn(ip4j#y%a1OUZ{zQA1}jE zCMK;kO|y;-HJg8z6u@&6;^J60edZq3nNZ0*Vw7AN2v?H_45+DT&{^pE`SS=OGtk}V z78Ym@9ok7kf$O@$JG0iMo6`+iImwRCrHK=z9lwSVW*1Hj~Y?R6ISuuwzeZ%z9ZFyJs#Jr#!OZLof!WNW<; z5)yimlCqD2;#tEGR2zixpl7f3kP{iao+7N9QK$QUM2Ghh^=Vj_X$P}byjA4*TKYPr z8mJhw7B6LI={A~&9dNH1N6ZsKK9b9DOdCHxfu4?BMz_z@dgQlAq>SvLTL2?SkDMAp zs=nmy+nMZIMux+O-B1y%g&E{7Yuxx}IDfSsxfzU8ebGX*KNH_bj9LA*B|#(ywW`Bv z;YO%rzJ0&E*p-wVZTGhl=#TU6z)p&+Vk$W~<+U0$7krF{-btSbkq1+bQ8raqZ~5}& z%jdeHrU-3r2nUaXg52RTk06l4h!LqNfcU=Z05^&5mH9C@2?O*51na#nniq7&tFm|X z&4uhPQ&9_iP}jS)!_Lq#F=eHv>z29CvnO~asi(EHwnF)nPFSxW{&DG)HAplD5Lj6U z#w$jNqIS5N6vQ-Si%iSW5Q#RDsOWdAQ|({BO3ZyZ4}dwx-3aX$z<<O6~h8WsXqP zf)`KmL_K}_^LD#FG0w32$sT5G^{lL{w{O*fHIF=kBi+)Xo^kae>FC77Isz&4@X0rD z-BZWB0ErOdr>RDq(+>G$$<^_kz5X)E&83L$KvK}?H@|f&TH0p=9pMnN*v-w^NE@uI zxQjUybgpl#PGM0mL&$9#o5xQ>b$1%b5f(pH#<{hvSks>4mwbIS%1VdUW4%RpYr z!&Fn$K{rPC2Eh`51_)8z{TiQeuyuT{s^#XZ!<^-GQ1cDE=G(e9f2=S1tR3YN*Q+Vi zn?x>E%_kW+kM~CRCYs5b_?h!bo;NGC1qhW9^;W_OudcR;4Bms~=p*j)?3Hn9D`dT_ zUx~lSR9UGq49eV*+Am5ud~&9(!KMHqMz#|t4#=SP22>YuU-v{KFQn0;|M?m_-lF`q z<@B$b;VwlScyLXUmaKT&DLPImX)EDXHJ=p?fT)XnWw{tH`~3a(Jo|a-JO~ZM=zH12 zO=XnC-@&_SccOMw#x>$D5(ftTEojcy5QZnvh9P#rQJSOvN+2OBl2Pq$wC7sRe?(F~ zBV!+aNcu^2A99p>ytIGkMtEb%W^P+x_n4Tl2G1(&(A#(B8q{% zoY%9pS5{Z|Fe4~eeSaTfKYRB;zB2Is& zKCr>m5bv@<v_nTW-cx~61OS1{@r!ii z_XY1?*3k(fPNnef#FXf2&!+cQnl*s(UH>$?RxGw*mf5ThK&pTM3r#RI+|b&Rr>AT? z-VUOCNL34oelT1%m4Fevq|E(My_&8d_H725h`BpQ*Ji-}2V9ffiXk2tGn=oy3|=qqp<6E7`+SHh0wgEBSvDFUX^Eg{6p zAZ()dKsFs796Urz3&GN(tUJamala_QI$}(S8UCsXMobNP8%Rhv!{%EOSL&Sx<~ZdH zvI>wmwzK=vXBT(sVUhD#FmaIxK$ZcAm|U3XgqlRe>BK?eYi}{p15cQg6jmyRBpA0n z%g;$phSqhD=KHDmo{CjvyprQB%vM zynG4o!M&^-z7(Q`S?yd@foLqSsv1l&@ix(~2`F|TFvkq{7gi*zf* zi{;tjR+o3X_cLhxQ>a17;yw6 znOOl_fbFnz;of7|S3cQ(ePtfAH~`6LFn{5BN`S|C85vS$gDer4rcFK@M9~hgeZrw= z+}Xp{b_z5M1Ci!kDm(pm&T-$q7|BhHq9LF%>YKW|hIr@UX7EON^oNKbz>uvy?p#9L z{b(N852$C8-DR;!9O-TDOMm*zt2~$b0yILQ z6&qs}ux$??IFOA59=K?%@=I*uZ0*m34on_v*%_D$?JU5&?)s!lg>JTS4Ipph#?i~v zgVU~9e=O8{6MQz7kpU= zy~prviTM!B5LNdf;b>)TeZmi!mPa8W2=@V;eV=e%F}?jQ=gV~u3C5MHt$lW10$;a} zrEmmon2B1}BdP7gZ;;hq@UgkW@9tLdoY;JD?KR>MJwdJV$(OllYTv&{k_1Et8y*=w zh|iXpMfW#yL~fgTdv9RtL0*E|g+MMk{m-7BCL?|j5+rAPHWoy(+ARfQMlALPB_S_{ zrw71i*^;$ka)Om*XP-j5va(X%lIQt!^SLocRNn^~A_O?gA`dO%xkjYKnKR4iL;Lpc zhd75dyb5BGvIrpy+Rm4gGoOusHbK)~G9nfVo7|>s{u?M^1eX?Kld2hhy9)1kjy+Bo zd&j~V!6_l3m)`d6>sQ3Ppl}y^D)L4J`1zsjK##@DL(eowJs258DAa?O92Q1KXSbe+ zU@sa6dmcDdElsnfM%7HB{_qX)8}-g5`m9t4=#?4x2wx`}Gr+p zFohk5nA7kudzd1;2dT9`=U1Y{l-Xt>u+Wxmzjp5Ww)lEv^f7?#=%wM*i~-KP&WZ9| zWWAQW*98Tmp|%67r`1APBdUi)2|*fYbFe-Ny875sZl&C>^g_{gb6yDCs5C$FEI}yF zo;{24PD4XOL;xY7t<1Hm?ccvYz~8_5=4Bza@FEN%-K>vbk{}|LP>Gob zt)qA(0aSQYxc|9A2oil@uwBCjgOuw4axHlO?=tJ`F)uS{!lMn;&DGRG6vY{5ig}z?R)Fnr8^@9breC?$T@UC-U-cySU6Quc1-HP(#&y^X3h%08CYk zP5>$6yX+@U;BF$)0KnU3fx2)TBRu#PRs>cLae^5O6_ZxjK*TV%RMBV#6(v~`-C&%P#WQXw;Q>Fbsh~w+wjl>g|wp- zwTG*>G~=kJS-dedrWhJQuR(YPpbYjGCP|<`6GMHbIY%X2zpQP3FKI1g%gVjp%f5#d zQ!@*MJ*#ZN+??Vzg;Pnl({Hz@ef!od<|Q`!UDY~1U(nV%wL(nLI-|t8tvE54lY{gD z%e_0}cPX7^j)~tLcax4Au8WO2#(Vo*;YNbZlU1qVpX}Q*5b`7%ON50g>?9?S9uhJ*k1oV8rYqA`Vg9V^?|R zm+a_Ug%XaJ9Uf8vpegP8hrs;aM1UvYsj{^^>F8WB_C5U1&Bd&mPZpR}R!{rd-7SUg zi91zLYFYn*{KuKS{_w&3iBAQw|3$)GN&bAy>}zW9lLIn{%??fZtd%1g&E)hvPLcIf zW8>uL=qaB|S?yX^uRim6E=$@~V2k@2CWrIq%P#$N(u9nfomk@Aq|(8p~&N>7iJyboG0O6fT! zxP0iMm}vW_`4(=ql#~=q)U6Si;-Vq|=WDvUz`CTw1aMO8D9S2u30w%!f7D~7L@;*) zjkwl0doLMTQ@nWUB@WEoUE{urL``t;MLdWC({swF-ro^kU0!wb@Zj=@h`15~b_(PH zX2Z+N+n%Y9xp4=3`y%&wPJ@I)1%iBh`p|&dPt9|rgHJ}3O7{|s%f?nL&x&>PinjrJ#&WHj~9dBb3qwd zlzaVWPzhdx7MwmkA9yu;XYYlE7{OXtN*vTfgM*T(oIq}K?p|2h%eAWfRtJbN7b6q? z6LQhc`&`?aN9V9+8lOw9LZYT*@g3ghlkZuq@`|YymjY$~nCyKHVE;<8ww9J8P0j-{ zNO&FZe}sLGbZ`BqPk&hD2+z5WluEU894^%{TENKZ!y{M%=*A!!O-)UnNoqz$y`N(Q z;m}{u3c<|Hak!?Y_5i6LgkSKNi>y!j9K~f4iPL(2-fn;Jr0U|foJAb$ z0CadrjwIWwuqdfdz|~cY6Gif1`fHkorr%L{qub!!`p0OYU4#Ha$_W7unC-yI`jTb8FPeX4uu5+x#%fBsYkr(z1jak#PX z;Co|xE34Ol^N@e20s+#o=`hMenh2ch>gI-Q9E3HPTn)E`@SZD6OUS$+83y++ltCmj zHFbD6!3PV1@r~gg!tp!H#&*X|H@KE0kMR*dlUgbvbU!jwJPB}}kzj{Vk*4`eU*dDW zK9)F6&yUhTVflAkIT&Th?RAAcy5 zh$!}roDM8}3JQuLw+|%SuWzg^E2X^6%v8;|x(LnD#YHWeA9azQ{60ppkdI`U_K{b^ z8Xp@QLo_zD_TXNBz#9x0aaf+tT={U_4w@3}Lc7GM zC@oV{wr*|&)(i^m&ZVdm=RQD4^KmHV=BM_h;)3<@#bdMw^<&hDlo`wBHw z^m(b#4XCDSxM2xa&7?}@# zHa2-~8bdfzNlDvjvIxnMqruc4FYcoIyEk8eG9V1&_ho%`5&UvGh=$JZ9f8~eNj=g; z45$41WjwNva2S#YzW~B0m};M^a9nz5`i~=lj@qhX_c0{^ov{5={P!r|i9b9WKpu0O?g4UQ$jgY;<2%K10^y?T zDbZH#ndnnDFvf`#v#;+C?-%fz5bDJ3T$*K{`~GnkSJgH`rK0c%e&No!zA4?U$4EdN zo|~Q?9UBw0Rt%&>fSf820U8W%&ywNc5v65ih&shKMAj44OfB&XGdQ^oaY_g{U-bD^(hlOMHF#yos zPJ8HZjcHDzEifEmBDp|IkO(??jDnmT!#gl9kh66AY`Js$IZO?KPMtsR ztDa_MWAh>TBMD1YP0h{~nk3@WMyd}qs=mC$LCLX$pQKgE4rK?*Dv(@OyYx^VRW|ey z1rYOyPsqPx6^72cOL9M(4!T!L+#AanAH-lM=I-F(n@cefj0h4QO{iI*{2d)H*aOxQaRRG~GT|CZ^=(f7{0z4u&?n@_UIAC$Uy=( zhER|D0gz$nlP9YF0Q6FJZtmC6lAreMC$1X;e-c0z(r$i_@n|2}Is*CL;S-j5KmJMy zM?^}BivB+Q!S8Wss2Vb~cnpDW)t|-1{bc*<>zjbD?qxEB8EGBBhaWNuI6z>$kkyU> zH5Nb2ZcqfbS=>fh^6(I6G=;h>5c9gcd;=3~FnVBsb5L6d(Whym^9H4^oTLuIFzVg2 z|Fn)mSo4=*Ui(m%^taRofA}C@$`Sz=@4|&WPNak#bRn3SM%GMac7hr;Fp)o=zgOX4 zsUXu<#^tco)ig~I&o9|o9NNUhyF6Lqr3baeo?c#>044Ofmp*x22DorKxXh3+oJwn_md`Nz-5w?5?ZgQnsB9msrO8}ADFTYk5GL7vuM z=5D*)ZSXZayE|LIbibw-1q%0tJ|@!7ciaSzxXIJ;B<_CVGNTJ2UgKT6LPumiQR-*G1R8o@}@ z_ozJK4N2$JHsU`CmH0NfAN!yGd56mlxz)sJBwQ%My=;>|6|c8S$x}!*rH;cCGK$gtx%`QdC2{S(BEDX^5M}@25M@* zP^3SxBW~M%TI41fj>Na{C9)NNE=FKYx z2B@~rBO^bU<}{=;7Hj)mLP!A!29y8-Ey7E9oCxL;FohH%-#^}w13W9&ru$jc}Jc+GVo(0S|@BHco6>&Rc{_v^S=K7CsRmePSQw7l1xd3 zv<*q@5Q@rFnHq>nrJ_M%CrKrhgmywmM6-}2!%i}kN-7x|go^0*crTyxyRNT4&N=(+ ztk!z3*Kps@=_d9KRmT8B9tuq@Bqn_}83K5AMhzqvDANEa+hylY1cKy4&D>e{kP=?u zcs%WBUw>W;0nOx)f@rOE;z35bp6$)lfHdwO>jjT%KyuNeM+WX+Km^oS`)zI0Ct^-d zNJ=UMY(!Vc^+m;?8NmaE=YIL(1>F7DJ3)i!u}ICL%9@(v^nU#$-`Ph139-FkTKo8I z4?GCOU>6Yq)b7{R@5}W3ui@+bY278gFaJ`QeME5osJ>V>vF-WOr^Gz*ImehpI{o^w z%kRij`wN#YLDC*Pe3-AvY3EKsjjA5r?ipobPW;P>6F+!S>~s~`UR-VN2X2G6a~IQ7 zX-ClydKxGy`vr!fqB8s!;(!YHtQpuPbpemDbs*X<(mZH*F#ETAW~Zj^T)jC8;R9w@ zG#G~u_cy$sm*;DmIDN{Lf0wJ5_|F}-RX4Dxmc}e}!Lt`H?$@`%HLubzR}V+b_vKx; zp4zg9=0taEy0=o(n7d2n&!(3$EWuN3~BjO{RCK%$e9a8y%?m?qAZX!y3r(zg%#vt$L7kds08nxCAkjIOtg!k*#|&V~OGm;KK0c zY_GI`OSkX{4;-C8<35I2pOuG*fMP9!HAujLTKUmkm%gl zM^G}KU*8x#urSzrSHQx_xlKK|lSNlurp5h@R}riE&c<@bcPk`j#Kwa4Q898opkd<} zk(c5?Dagx<=X0YKfM+8DjiYGfWLx4{(s$OI;^aFyW9Prix4@ME3NGFm-mAw|E)qfq zYoOqsUsw&Giq(vo3t|zy2zHU;6sma_T`#-_ob$`o)2OeU=PjY^BDpR2?kmDOk;QCD zW4rx(@#0ASOErb;Oi^9BQ|Ba0psMr))kl;`9Wksjh8*FbVu(BM&R>}yKfKEIc5q&v zu~UNlCv^&`lxHsj!owlf0|R?M+IRWJ4MLVs_2{S{4hrfcA)z!d$V9E=-aY#2$vbZH zXuaOv3F6Edrn%<*$B)ta9@C;s_>_*DH}{d2#@@-gty_WG<#}o8)G1TG@~FOkr3gox zk5zopsDb0hC&R{qK?6gO6KWD(RGZ$dT|UjCf-(~U`OTaA4dbg?I{OW2p3=iFKmW_hYsN2oQfHdKhB<^r;ok zdq2kqOS#X=Ma!29@o;Pqb#-;bef5vytlbiucigLWuDJ&oD(-9J=m_4*ztvoOi)^_A z2Ud|{Ls3C901;MS9KF)YDmE;v1|*d8)!(p%98?3_Qm#-^JiR!FrdN7)kMAy(6cS5q zZBJ;@!n!%?#h8}imW?RF_JbN|OIk>j`$8od+sCn4$R#<1YVP%y2+g~1`^q`SL~WLa zv6_qoUj-@v( zJ+WyVsIjQ&($;r4crnm+iX1^Hf}xmZ<<%<-w;{Rr1MHgOm6wIFOu(u=Jw2aonHpqt z`(tqa#>M~lTR=*&Uf@R5w2m2XqTmY-?iXX))X)ekb0o5xsHUwSG8%r2zyLou zM|Kc7fGA8k%j-U-a7<@@nY&`e5UUT}l%Em(!`)dUaSAZ0rT%j_t@Dvzjt0suy4P>- z7&SG}@T;pX{QkYSMCVGLjs2D_tUqDtUAFN>mb$a zVb#!!8RKyht4=c}beSED1&xm{evPMea{dsPu`=LeH;IO&RpT~5kd52^B z3(uR=#d3q!owzaG>&MrEoDXoMbCz6fn<;92b8Gh1z7dmE%L#hY=MT!DX~WM4bSHge z!-oAb03@3o9RsC&MR;#fkD{z@S|*Wu2N5A|m%X-Db%IYv*xe^a~-iUARJ_79M7R6iDdPqk>+Ht0R z8>AZfHYhQ)w!eauxdA2f@Wj;dDk@wtgFsszUw;F~T%&$xeM`UesbTAMj_BFX9AHSb z#yRt#@A?^{ZuyRH?VH@>S_mbsdb6ZsripNaINuUWVosinoqx*q{M(+D3FB0wifV6< zmlEyYVf~MLbkC21?D|q$#6eBk2blyO9v&?<2pZfp+}{^f?)f;Ocke(Vdr8(jcjgRc z%F0{2#?P9?$$OYC1kXL}6nvMSo0h(Qh5Zzud`}uKPCG7HyGJKy?D}x5L*WrWiY`6q zpMM;-ZPRmk|AY@j->|Ig+hxlHvm{VzFW5)U9#{)b$z#T~P!=^cHM|$TPH#?N+Ag@? zZHSJz^C)8|ZCK3&+U0IWk+cSF9&IIp!_6BvQOf<%0@mRUX2B{R^`uWP}>V3%$kXS8414K{V*{Ugn{!f{%pIkxz`|uYt@vAK*f#_*ORR zmH&Vq3Lx?;l6z?U`ic${Yt}H1@MUDq^@QC;u zmQf|v95frYc6MkI{Zf^BoxDys-OE%vBCy`_Ib5BcUtV6fKRNkh>Xv#j9xr-1cJK?`TBAh(h+Dj9RU>bowBa61WsJTDRo)(V z_r)=~yK-UJ-Zu0lx*cJxHI)r(^4yZR#_DR2Q|y*bzLYv(gq$06f;uB#-n$oIq(b1I zW`w1cm8nYTvn}72m6cXh=)b=>W9Cel2`(NAE>5wEijguhGBkbt;GK8u+BFsHVxKCQLH6E&gA#Bo}Ars41I|Zf?wL+Lm~hdWeQx6hW58j=g{X zKA(Nn_c8nPWS<;1Q6uB|#N+y|FGUk676$j-#2^8H1!$q>@nB10Vwv`r%SX74e$wCq zz+MPVSnN*DU~4v6Y` z^X}1Lhse+Pue{uo;(6ab@zz7ZW6pnEccTt8zPkDf^eCZqN`55AEf{s%y-+3eUlj1J z!E}54a~ZH>>t_GDL>NI@+Zvq}N;3E<<8QQhbndqJ(AY?7)4YT$h{_Y5>gei9y^51N zY&&~84TguC+rzk7U3@iTWF!Xn{Yzu$T=i+f`Pe)VZxgUhO_N=?@g`^INx$Rz<~o&B zRuYAZ>hpJ~Ag2C0Mtx37*@(mQPOhDO)~x0HmdH&$D2^_>wXByr{QbG5V;_G(B!kea z5Me0=Wyd)$e!r;5O#1$Ls^9t0-4xr z$vVxT6QK9*w4iBQR#=x$|x>j;DX*i`4jZxBaJ{#50?CHD)K1D(G zggiU{uWHMHwwxJ|9NfB^+(iy^oH>EpwfMx;;9}b)aRwFe6rS=qw3Fux` z2lr!cGAcsS6+-iDIsq@dDda%aAl7B-c^#oR<&sujZfW_GZyv!VpU)cy3actHG4OG{ zw6i;_5{V)5C1QSFy?9{{f^86$mX)PHYSczfqz(5Dg%;H&E{W6B(3q0b8T4V#i;GuY zetPi0n2sDH;p3>u#E*UERAaY5-e|AgswIoon_ItyiY>CBhBC28U2{#k&4vxaTng$S za`EwF6Nf)5Y`4X2-=8!Xnqh_iD9*H^)G8~pdl^Tm3JsAt0tF(=1h){PIvNt&Tbtav zqW4$a6G{uwWhf^vBf&G_qO{SnPqP3YSguag&?7~l!E>X!;m;#}_2X!IQ3^uyf`1CxdTQ*+}7~hy;oCI=1b% zv@Au0&5J#qz8fDNGmb|`x=bVYKo5cIu9~q=c$rTvy^l^R2^eWJ#pe)YpW8jB)BYXc z`?83?-EyQALV6D>^BFBG+Lf|nyNd-}95Zr3uY~@GM2tq?N__-bYu20^pF6g9_dl4T zThxM4X1JJHh`-rGlOR{uMHj=;0TP$I$($?To_+iFm6k>@>wUUnKliO`WBEbTP;Rww#a)Kiu8^;O_ZdZ7rB&J2-kklVpYs zv)i=EzxPqxQD}EV1-}@|)6VKZ#ghzaND&jUZf@OR9|QB?xiS0zn&wY&~7u11^ z#ZSg}cJtS-cSdxs=NROQrv9^^0`KWN+s2B~GC0XlKHd+1)P70xgfJtyl=Bh)|D0rnYpeW@z;Gk}T%%bq^Y}ucL-loB ztQk>rvA<&C>>}IT43{ZHlw=A4z8eaLYOd08f3T4_$l3=({A7ZRe&BusOLG2zf8Hls zT*PPvON74Mwwq}n#8QKzGk_=8MhlF3z>pz=Oh4i2`^LuKoE&f;6oH`yH8jTvL+u{N z5-h-7g7@G1*GFp_+2uBvckqR@v>;8NzWvpmYA4c!;QeVo!O;LvhfB`;>#w`sU2Wb7 z44yQ6#hBvo;&QjZi#)vEDRn@)xT>$y8G{%QjEPix+>|NHHHPLO90#CYeZwIJG-m18 zOqYiLIOCc7b81MGaVlZyS$lTb+rJl~SqSDy$lXH$N_gk=O%IL{Fhly={n{@`F8Fv) zIO~^xdvaIBkL(TMcQ8E!V?PQL0Ax@dP&hzhg06qJd3U*DaO}vEv{3Rlyt1vAM$=R| z&^p{?SS0tZz_TKND2wcb_8e%d``a2GJh}RZhEEQ-yI%0d+1hF{vqwwoa#B)5RURmm z%qae!&K)liyMzK=@wndX_@zW)liRv^g)RN$Su<|iob9it0Wu(Qcq`$CAD7Y=NqNSe z+7DR60vu@8sdTZF2{L6s(Mo)&^ax}*c9A%Sz08&bg?x2wE+3}Gpp4rnBq-53{mD(i z)HbWD<1s7&S$CB;9IZhV9{n{bWzT{2U3M-74<6WPq14|^t-tJ?$!enD9$kYpTMOJ! zo3(0a`+ZxV(7ul11LfsO6z4QvWNd6wS&gHNtWH<&uAh}4KPKZ$XtMyUIWt~mxTer0 zFf|FKY=?E%H)}+sGiF4g35ob;n@G{}ID&lvG7nm@Ss@EcDsds>$466)Qd|lBi?ubA zDVkHcfYid!iUK-cS>?$CnB$r^I&=Z@ApZBvJzwq7-S1^0fWoZ3YedBzMJt?UUw`;8 zSyns4mHd|OHvN7wV`qTF<>l)7yFaaRb)BAVo>($+#*RC93hL{Z#OT7yeWR&4KBWJF zzW_vllWk4noU7mZBZr4q;&!Ebz!Zu5c!!Ssz#!6XroyTr5^OymIQF^ABUZPcp>mXo zrn)*7HZ-dMy{uax<>OZh)E+z_cZ15jlVfi3k~gyS(Sw4FB$PcbZXRL9Z5?jhvS%a@Zmf}J zM15V|qZ9h$C(fD(iwJ(m9nEd@`t?ujcW(L|L@{i!y``$bQ)dwy4Eb_$+&V4UUiy_+ za|<^2lFh?|j3{;`{9t{31Q}m3F&CfOgD^d{Up{s)P%)|{DlA%=yM?klZptUFqloc0 zj4x@BF=XrsmE-Zh6kC)8H9_n|z`bYBuGu|Spz;DrIFetjt0HQ zuBIiELnFh()A)jgfpEIIduXY<#|kJs>V9THVGIeJ`-1UWQ)u96D8U~-IbZL=gKE(9;sQ|gfR4_`ZDXPqZi<;3<{ zS+!!Zi9Lw{(T-{l_Q#f2Gtsh_SL>`#5#qB2;<#!xGA@8q!p8U!MbGAL(Z%y5sLha< zpsAO-pF~YW76R$+^GJPRax$>^&Eu;b$0vtz+k-`4h+S(k(Nd+$d}6O z0{U}e?`ZA|p;Z@{n(oa@xvRCgms^YdlvlziFg&T7Hk~z3hJ!;w2Pk}ap5@IQXH!y` zy+G|t8YIT%BgWlC>WF$8uqu~%eoMj25yD}o;xaiXLm|nYo0rf+7g!fjwa!*O{0HR$ zg-hzKTR+g?P19clW?iKjUHqHMAVhK2Z$!!@HC5dLo^SDXy=eZrwA4T12+_hsi&=By zAOl~CB%V6sisg&#FHJo!x+_b>^Q5pxUVZtA2Y{>J6Ms1D%DN%_pRjiMH0Mh{pfY$n zpU@f3$gK#JAT4+%klqCTNm+c$KRD8f0aD{ll_tqb>R0Gx?&Ui^T=B7ogmid#b_xX zW!d7+*d4?=SbKa<*d_YMclAcdbDU_y1$^A&$( z1;R;#svS3YE7n2Qm&KUS~F!oMRAD zH|a+wCzYT?ZYysmUO)EqB+)CmjYafF9zgMzxaUqaqGog;--TOY?)Z9+dv15-DZ4%_ zm+xm9rCmF2{mX)N=ij#IZ&P~t?9===Wlmb)tkqqv+l=;wSHWegYy1-wRQTu-UNnh# z_(!DiZ!%mRpZLdF8He?%ZEDJ4Mh7xRNoOgsiNgZ(UtZbJn-7{ushKB_*V&_6_eK9B0oRQ%%d&C=RR_09Mc*#glr|zG3)@%jzpUA5E97Br6 z(B3_Jc7Xd(+ID|1{K+W7#Nw9%R}+3u9|s2p$!--So!r-7TzVro2vx5^{%s&H=XbCE z`|m9kzar|i@5GX=U9$!_rhIi{-6VSy-T(#k+}K@V#RP!I>ICEp^Jtr^x)GBuBf+I8 zK)pu==tw%W#S5_4(^}lOYF*p6GZU^YU%HgLUdE6GE5yndzYY>~KW$BwA%_lm{Hp8<1)L7M-bKQBjpLn2OHO%0KR zgXHBqu}-EEt*HF2rlvKrjObVxn$1&AaGrHSRUR+;YjfHwyv1>Qe~bg~L&TqITl{v% zO(txC;BucjW1Qf5QeZYm zPVBvwn(Fai)OH-UCTcYsJUo7aG_@u+`&y-7;F3_D7X6Ft8TnazGXZH+%1B(I4KL|n zHUafAin^;;uB=8eet6zYAOxRtxF<8O`D2F$gKS9Zb`e5%`ot*HM7meWSM6iA#E&jH zGZ(D1Tyoh==L@!uj^JO4<{F;@ew`GrogdrXU3hq)Ldnez@g`~@J-i^_i6{#OnG^%m zz5!rFO?Om?E}phY*Wg|O|)SoE*>ztRIc$pgVexMImGf7>D@k*IpI0Ky??KKj#4xx(CIsNdR(~Fh|uMcx&10Yc!|p>u2HZHcccCh;OnVZp|j83&n=7tu4jUIO;emygkJ%s?iv0fY_6Ag{K}KUtPilD` zT9e_hoe)(6BnOh=#(zoRU<{e{`$bU^dlCOJF2Pd04e7{h0kjEsHBzB9p>6B7ILoyu z7J<;=c`V2u%xh^@-&HpxYWhz!M+84qmN8?@{=uk8G0jF3u6^pi#*kqIf%(v0)XlWR z?JYDZ$jM1pHcO(Eai~9amj*xc+O=bb@tK#WYO*hS{Sx!05fG*kU%uQN^yVE-8yqcs zqau`SC#hXDU36lVZZ-W+&L11P0AmtMbm>?j&?Mctrde)Si1+6wQ_aR6qmICtL;XQA z!RJ>~v7G?{3>qXbhJ=WcHSj0NjIA1JZU5#B51PEC!}C&+#`3V_ggI$OwTa$W)reVkMd7>MKxZdw66lSEqOw%@i~_z(<0Q!y_WbheKd< z4!GzFGR5G#A>Q=bQ)2W^^`|LBlb7Y$q3v(Tz*P);athgC$-D$lc+|ioct$2$k?NnB zClL>`vn&k2#7)l%Q!b;IjL%Y-@&}@xBy`P)l`B^0YgBd@YmNkjpFLYl0O6zRKKIXi z`H}}*4Ee`q$O0jRNVR_Y_!47%XHs|z@j>>ltL0X#!0=>79`r2*k>Y-mBmI%Nt@ z3j)H~K0LG>{7K+XH0czdFIi!cM~*CAzT9t&xx?%0i`Byg6Lg4o=LQN@PM2oIYoy7M z8Nycyl*o7;lzDr`G*PaY2DFEuoXBqN4;|+h}2UU;-IW(8|hc&YW& ze0Xw3D`yGB#gid6I;%3>u0Y-e55KZEbeH}hnA$(MYQvLY)5s_G*9hFE{xXTxdwKV6 zZK>BvKw0V}ku~W62ap$u>@K>$*k29Cs1eHL*vQBQN;2x|24~$L|4$2`4!SW7 zw3P-*pR9A@Nsu^zt8qm%WwuJa={!r&$f;8{kh>67-2A9AhM7(!@s@aHAUNR+1q6s@ zzXNc@#OO0?BlAaKA;(TOK0SiiraE5WB|Dhh=B!}KBHVntmSBM|B=z;5W%c*p&J?Bm za*!f=Xy!&sO+2^*OB)4)y0UUL1RPlj)515r6w{9Cy!>PqvrdxMwVA;|&pzc6h|hUSA^a2J-TqY9Sl6H4-fJ1{&cEAYZ|~O4x`gO_ zbS8c>Aq68`LWeIRy+QkP^$MUHni4}YhYWBV>hE|b5ttu@=xos<Q>Sj+Xk)?%_D6zQ{~ebMzp68C zTm!KwDBS>y*<>*WUVG4hI*1vLr8g&^VZXG&zkKQBa$IwJT4NHdN3qS?hH9amDL6EI zZv0YladA3h0nq2UQ=WmtR_}lxoiKtKAPT#4j*p&I_j9uudY6K z5+1M#!L_;wX9v8cb6u0g(ix4{`~2aN#zJc zWoSsTCUOdL&lxNy9+>(VA$wxk$38v886k&RI4=EDbv1)`IHyIBAJM^{X>ox>Xr4RH z#AA}v0kbf+6(R1^Q;_z4&Y6C7n;87ii(eMUJ0pMf5wU=V;H0$=M~EpXMi*O&25CQu zpBqRWOoleOF)LQB8Y>9x(P)BbO|?!|R#90`OdxL>6)-K9+qdpJQQjjuWPE*(eY7Q> z;xVQn!NIM_V7FY`RJ)0;iXcLOLOSl-KX2e4XHEs_RcHcM_lz?6haV+MTSkKCM2TLo z;UKLG$s>F!4p9*Ld)pPRT@t{48C)!bnJY1M>gX|JpfQSL^+!}X0>ieB=-PYG_uT+B z#-(#gf&c=0{w0B5g;5@OBjR>BJ2Pii$6~72g2@Q{3hJs8DCC2TKn%g`7y;awEGE)g z;O!eTG`L08O+28X2YJCzyw1Y?u{mQX4y}l0{x$_}XZY1^L7Qgi#FH*-ISu?z|Isgl zzkkSxB*kKS^W~-NIGDK_SaA;@KgJab4$44Kt$JbL9_No$TmTT#@w)Q&Zn70{$;iu5!5%a^4G)I4kKTt+ z#96pQTM{LN-=(GXBtKusKj4E;k9)obD5kueoe6b=SB>;#5=x7$%Xu=aaUl}s?%ho& z-T~UDo)zbodKjz!IxkNdUk>u{xrq|oh~vT^00xq;16mpRATMZPce$n3)?FTru1iJy z2v3}NMx>UU#cUE+C-uKLaI8{*aVB_4^1@JGABD19|E=g&v%jLBrvfADhm(-9^u`* zI(UbIfu{~B0yk~f1$j0L=l^k z8>|*?O!z)rBaMdB?hNCzx1d#xjUVuVns>WUtDL*DSfnkGmy`SO?j6|)Bz@WpUkJRy zQUk`|t_1yN2d=*YZa@S_P{jLe&^02bI~o=0G?FDGCGpaNhj6$5B>|AjiD+jRD8pae z;^2U@KZkf8445394BGB}u?XosQ-Zl*_=(ce@ce3sg{(-^fJ5&%-BGQCE$EDs?}kGp zqNu6-CW~|1h7Bkinjb_;#1r7ex_>KqFoz%l1~q&8+{DCTYkRKYBu6yOFf0`R!*{k% zFx+a=Y$Zl7Q6SR4U|%;k?@7VSNBfCFX(^;*>~r3ebV;xOxWc=A3j!< zIm8fe1b5Osl7h|~5vovc7 zPh}aGBDAkhQ5<(cRa0zYvahdSX=Y}4)(oSvlg+x-ce(dyeN*W5Nm_%5&&-5%b68<$ zC~h?#4(Kcw6!ARj;TF#~G3pKD6{%|opVBijc%o~*F25~>?TyX4Ch&oq5tWK?JB;aF~BqNC~5Q>?O1L`J?R47hX)<%+*y^{EwP zGpx;Yn`B@Rn_tbRf!`zYK~JKf+KJ%Z^JO1-_WV-hsYtmutrhn@m7Q&%srd*)9^%tH zo2meQB`$(Tg@s8>=XJl}+^u_|swK`4)Ue2|5=yra4y-y$)4=lb+^H0t&6ymNxE&1Q zQp}SPB>1!rrztgkKwNtV*NrI_nO#%~izur0?9r9lz8JR_CIHhrXUvk; zUr9fAZr(-nveMGO?++&B?yJqay6&*RM^Nj-nNqoLa#NO*_tyq!C~)pw}ifWfhJl2|GE(SjAWZGNFd zK$vfBZwq1a7E)4+*pB~(%BXZ-bR+^o6wA)TR14MN`!;Cx5Kwr!f#e>~7xWS--o?Zu zzp6W2pFe6Kt8k#Kti;4W3llEOieS}X`320Uq=5DQ@c#K=`LG3NFs3r>{umiUMAoEFwIE zTfR;1^UvZ~`&nDS3b-ans9>M~G7&+5vtZO-xGT|}d@eq|8Vwad437_299Vh}aa0;@ zl&1m?zFOlshx9~p=bF5`X2PE@R|{RBhfn}D4jM?9RSwAs{eb)c(uEtH4clvbWJE+6 z;cP=>WnUnckr|wNR_fYw>=W(VBklQl@v+n3)^> zWt95#>7k{R+*dtV>5ygebMu+6ZkbqSJTgg~{8%IAdX8?w_QWC=uU)T9hk4yv?RwE- z>yWf`pVRxUs{V{U!Xq5WNlD%E-Ou0oWm8X{^k^*Z-|^0=amF42m*z|s zRP!W~3&F%&?d@f+%^Vm6Z2|R6Suqpwy^C(|73u5p141)Sj~o1eM@p2h5X1_UT|=su zkSGk5tw-Dw1S@fC{c(HIrc!?X;#3sc<1g<&-}?-dF9hr&v||JW$wGhzRVsc0oiFAy zF3>QZz<)m(tb9WL+~Z%W$))E@8p?03PALm3d{=$@ifC|#+ngpi4)7T2`3J}3nY1Yk z^F<%U(L!gUJgJ&WhBGXCM{Qrj1GAMFQ7kB6Mh(QX1|y3ww6t{e;J$>+nI|&{11kd- zs*x6{B@KE&D{%Z5%*Pwr+xx3Z`$dAF;$mmAJ0K1wHSd;SYxedB3aA@)X|%It!@7WQv?Re^2VdB9^~)rdV8l2f@UGto^VJr?KAY8Vqzwnx1^mARS0dWn5mXJC~RD+ zvwJIUfS@319!~@BCn1>!A(i^|`&qSL+Nm7%rH&N`{ST7&i82F#5%--vb})uRlD*o9 ziDu*gC_$xn*RT6iXU04NF{(bGckMPIW}(ZjqUma&9^NIPeAa9=mhK}*w60l{e?s^K z6b*>zx0D6Def!0-?+hleyl5U>tQJuO{kg{N2w*I}^s`hupwKFzvNAFtGbEfM$hfj9 z_NMzgr%&LC0b}+DA2=Yp*m#<(B=s&T3}25Umw-|UnP&G8FptKwPmkN^5%|= z(ZUMLuWcR_W7>t>QE%my?{c**0cCw1Uumg@A!7yw^_`8Wrq}*;OVb>15@K@tWzCf} zbzer{)|oM;Dg(}GXQ(>;5T2;k9L8fW2vNEY5M}m!BxUkKy zJ-MX8X*QGYmaBg|H!}<2m}m?dGrjmjTP^PEQ&0ma59CPC`SuyP!5s6b76~_Iq2jp# zI2vHc#1xg#a+is7$7eR&-b;mGp8O4ICIT#qe!5s3EYF8Zb^%sMOY-b*c7?`%FmcJw`Ps!qNz0=?O*r&a{84L z3sFOzkNBpj7MB$>7I*5#a1GP^l5}{{90xC~<>7ggH{6qqUrKpNH4DjubSmyKGaJq> z9SddxAsSmsGklq-iIlRME~$%`k;-5RIDHyr>donTTd}0^qRe>RHtoi>YgGzg=ak-x zyKkF{DF}f8$CuFY2oecSx!9h1ak4{({B_pM@y8I+JzApss1bLI-`zq7i`?;{3zlP0 zKY3EG?(=XA<`t5fDgO3QVP1)ZC=1#JD^lKR$;l6S^dSJwYU z^|(K8)y*B7D806?@u)hUdq5bj=Q@LL73hes`n9H3-sj;pOZI5bP*b~yl>bAf3YOBz zlLtA{4=*=2e=~1%yFxaKVHH0IUGertqTGJNvAiuzob}_qYD&o(u?0d_i`ea!v4DXB zHwlEjb?a+1YGAK%k1GNAC>CL|x1_$@iv*EsJDG2uik(bE^sQZtN;X@&7EBwxQZ}Wy zLP%ggB<0}ASbWyg0m7fK+=WD|l7@FJZV13h9KBY!k)809r4Tq`Q(k>5yW1I>fjL7% zgTGOReYf%=>B;<&kD57D`1G7*YM z(1lUgf`l@_>kB}v&_|Fp0H({J6S9t(WCcfntO{(qqjM!OgS6#fw1uE9aUP8Vy&|ss zPLpO9sW`)&p(MI1gtTQ;{<=g5C$a}RVte4EMPxwt&6JV{=N3n2dS5t-ZMTpt1Fo$w zqXpMHw=ZJ)E$kYs#NMB;E8lRK_m+}RZ)jwkZdXf)H&Q5O#}I@q*uC(j?|gBoOq{xP z?p6Wi;hm+CESvM|vm;0Sj+-a*Y}{hyQv#ug0=K?EX!^3E;@5}dl($hsd1!k|p1;)y z9@aPL&>@CJ-wojc$p81HB}36t)@Jmo?`DQoK+I{7k;)_@Hpo2$k-lgcPt>i+k|ov@ z^)#{Ur@Z~WCt77PX_3_AM=6v=aJ{U66^vFqyMxbawg0|v_uk0;2mmE9o$~@Tl9_#0 zf%|4FF&GA^NtJG3ur|d&4$dFi>>vFh4!Jt0ov z##wA)r^C)SuE)oBb3`gqaOY0d%nlhtcn#ORWva28Wfw;e7$zoq3|i{ccaQ6t%E7!k zKQJi#JD7I{8*;Q%yG#CV^_{QQuLtM#;lu%?aMDK1{YYLZNDY_W`YVJo3=Q^|;Wl~= zt;W#-rTK%@1^h*fqSr{tCGsa)+nw*s6b1^$S`#Zoz_gaOn(1f-G67yGF=HjG}kd+ zh~3S7=b{Tz;X7N|*?v}upO5%8DwoY}} zq1UtC=Vk@eKm&0)=(j$coSx!kzrt$bm;)6h-7aMH<`!B>?cv!}#t?(MwL9n@BBP?* z>mCkI@h1_LRPS(O!nEhp9r6cj+`k6ng6<%MCn!zO@xO_MrK;Ug;P}}(x_CD+0Eg#= zMMaGZDwyRB z|G7^MUo$KeK?4$PtCXgX9|>Rh#i7K04IIxrbmsNMQETm3!CjqP3cez}a>u_+&w@1q z40a1N6mrYN)E3jqvOfi@34o>^o125SL*mBtv>P`B#x7d1vXg`8f>qY7)2G)@8R;h# zn1EL0+BFw+I{<``1o8@1llEed6NHbz2AH85U!{$oIyH?EjmP`C0eI8oye8BeIHfVC z>-WqV*FeBIGNuAP((ur5sA=l$*iha*o#ac6`)kd*1l#nadS*(GAR{!yJ~@4eyJU(-_sa3Z6%>ep|Hig~z~I5Zy{p6b>lBjWu_67uR!X=` z&>i?bMhg+9S%c&Wo{hIokIh_h+tiyF0!eQlHEzGEB6YrFhdDqYRaBJ&!#A& za%8DtmdHzfuk6?E`5V3752hgJS&k4M;h=~_;Nci%n$@%@U=adwDzpf7(1l@L^>JOg zWuF4Q(KTKS1b}nf%~cfJlb2w6h0BkxoAp{+7ccw5p%l1VLHhhVvK|qR=*B&c17Q?| zz$bm^IzC*&K7jrVPOsI|z?%(Dvka{ZRY51q;Z1}OcQZKh>C+hCmHmWdRM5?un(Bck zT(sU&Ugf&@jydsy1&BBzorO-Xn4FZ<$F#_es2SRvxe|~no%iog|L^b+gSZ@^RsJBE zA~_}8}dFcT=<0IbL8?q z;l_)Nja6hM;GDCWdQF&ty1EdbY4|U^BQt5R(=&7fq6gv=M*-FpoR)k|Nc^0JEY=l` zna_1W$17MF$y85xiVVjr=}zkBeUku`gwUYvJBdI*-p+{3+utuOR~NP}tj0xBwRkf? zKg4UW$Z~!%MC)6!H9Q_6KxRoKpIcm1SiFL5ncp?iVxg0hmPo*&YGOyU1E(Z&*t7p5 zXy5oO&SYoDn)NUXv`^gJyGTdpdm`(%clwcY;_n4eM(c27aUD!1$SS7Bgr^c?Hg01M z9W|LeDbe2mxGVbXP_dhbpO*X<_>N|xHI^hit$P&n^T}Lk+Eq=8HKs<#FFjIt_-S7i(#{g(4bFRZ19>uaWAkXe8S4AVMIJOj_uEbgvvp?dh7 zJ*Z{qe*;5xt_@@~<%$(&OB$>^K9iWD%|7Jz@22cG<~S2W63vnr=*3KGhZ|kB6TOMz z5zsMq581fYX?bjk3OaUR@&L=ry=IHvi%$sOC5m*ne+JxKW3_6Pj{WENK?@5xob|;l z;+LMJ>SZenP=KZ;ev$KHtDgQ(3s8GRi!6Qaku3FVjE#8XQK_Z?y7X?KYv(ktwL-B$ZljTuQpxbWb8ko2;n{W3+Wtcnj zTlrd4nrsf9JmX{fMwAThmf9jT-&Ox`)F9u?w6})Wb9TP+b3VtFAS{oEC+z>gKX*{L z@mfAPchY2S?T5CLPaa7UYkVW<<8wdPw01D8-tCMdCClc`N%c#{q)&Y5$`y>Xtcwy} zCNxn>R)`@J^LagLaNpFcSDlO)S0Ot%RDao*I}dx*Cy<*%%ILk?9Rj1TGh$iquSidM zTf6!0n52}s*3xq3?JbXOHj-*et?{V)ABdwplj8@>GLr&zEtytnvWL>e{tr%AOG-Z2 zUiD|(X)M}kgPvyEK7Y`kjF&VVm_S;-|0!dtPySz*6ZyP+a)z=WQBe>G^E8)?x~|zk)>M(l>>1E=a%?77mEI!&m=}LPc9FkYZR;RZE_izF+wIkj4tU&cP^Zk zR1!W*~pa7Kd> z9v~wpE1T}pfPio0ubs-HyNlW}HD&pbHC-?6)sml!zow*QXGynnxWjR2@p7e0pG``6 zQpWRVA>*m|&P@CK5S}ET79}Kl(vkXmt^rhB_#RF;8XHZ9a7!#IgpNA>PCS5$r9i<$ z0}+>vmY^~w?4#Wo0Y7n1WQ`$n0gR!+C2{`HQU%Gt9*KNYY8vunP_cd1dEChWV`?+G z1?EHyO}f0^?Iqac^rZqz@M&LJHSbLmnxF-VCG2oU<3!5ek6fwqz_F+5$-@f~4o`D@ zb4Z3ZjJGonO72kCkCd(t_D$``B8UuP8LwKs+P&WSxRJ^PqJl?_>Y)s6MDg!fvyFJ+ zo;`YCorEK3AFXwrQQ746ARM6aqv)z|-`K%WAfF_2S2+8$lM`?q;AT!4uo5glem= zKlJcFOrmITVIzQ0fRX7Ro=2TjCiITTWTr)Xu^G`w;orrZc@1uWS+jE0mc0+pBj1}U z*+_y(?6+?lGLLse|9g}3@)Wz>3wmn*?Blm-(Z#KEm;&I@=0JO}<0kQ0gg-7F`|0Dy z5tlL-j1tCnLRTPGE8O^nqX;<){C0k|(Xl&-xvTnr0(nqLVwVRA+4*3_hRUB2Q zbHTrY@0uotp{V@%^Tze-fD;Au5|RO;G0-+lZ`F>XSAb@68W+e2n;!c}zZLg=Qc{8> zl_tv&8U$RnyD~rbZpVy&Dy)+4mZ&XFTCN-8Fhvf{=nQ#NI%0+heELK>wcC6`0oB8A ztBLLxLEFLs=52L3y&5ElP#O8NKW^u$exXd#$s``bF+z5R_WwySOQWX<7s3xf( zrbibu=wSh3@x6Pq%d$w2;K*ahqYMB$ld}CHKhEWO{N68(ZCN{t;7*bX-#Ka{fq*s; za0w%$JM`s90$CW~)c|4!47@>{igGGfP#B9pkcX^Bx=%XvHZy!OHzHSU& zCShGtz(sAJUhZE+$PLHV zd}JXS#|7sw!lN{ci&n|Jd^F{4u`OAasAy5ZQo6W(t6?teJIH<=8RktRs0bl9DeaX2 zyUcWbwEoJSyLXBIBcj#cL=Cx+7e+YdPVATJ8?rDrVEhsR8I7>p8lkyb4%8X=t@;r} z|B12-QYboYu>`KvpWq$YNLJd3aJ7!H#qi$UlDL10fs;rgRGlA$u!t7ME9| zX$*dvPh=mlP`M&fQb-q|zRU0bo6C{{hMDThkd*pMs$$+vYxmb`!P>{gDrn>0?;CEM zSTbbrU}9bwAVa^-&Z9NQCI$iPSyT75$N0BgL+S5ol7@y3Fywb32YZRShs#Pxgvn%x z>R3qO`tH(tRl@_AMUqeRLH2o-Fbfh)4WOqis_(zcKX6cvpz{QW*iQY42$me(E@8qT z-=Y_YB}#Ncott}qb}Gfl%=!~1R6xP-!`$bq5p1(ocJIFKv6OtUH=wGjA$n${SOBln z&M-p&smt*^8{t9@(f=OFAUDcRo}5$fsfHc#7b`j{9)SsG5(+{A|7FjmfSJeRSGemh zQk`Pk1M&BP*a2Q2o+ZuMugKmI`YOH`v=v?8^;}A5Ok4|=AtjQ*8Vk6c@Dz5GUL5Uy zXJxyjqRi^{{P}8%^y`TtUYP@a?7%RY&J^7KN@Lls5Gxq+{ zHM9rMY*ggRM&`<`L%$8x-81o0^?AnLqvpdLNPSLy%#RR<&-!EbvGiUQ2Rp_nm?h~^ zK{6u=E@Z;cMfj@089`VHEo2RunGK9RocD1KD0=k+kL{nA#+Lr$q_O|}mp#WJdCTre zmf3~W*e+kboWF_; zaD?NlSAK?$3~fvOO1XzUrjO&u-OGexM#i8Zrw)Fqch@VX%2TplMu2ZjGCV(ZbRfRl z1IRt+hST3k&&b2ku+CmHb}*YlC>d6!+?pAr4==5CF|84S^<|{8*0_OvOA~Y0A{8)!GQ)-g|9U zJ*N1|BBlDjwV&<(xw|&JEOm+@2~O2}0lD-Hk!v$4Pk;TDc1qvBBnAZC0aHvof8IEy zjO+(YO2Rbf5B|!7`@%!C>;^m9v}tr^g4f`X0uNXK=7d9JSDUU89diTRms5gemlXYy z$R7PDRkG1v7cL73j;S#OM{K+CI_tXx**&U$xa-Nb&$A)V`MLd+{Nh1l2KSx6`r=Aj zIz5FY*hs*`EG4-li0`1=+|$|a%zRzjR3X48`AXTN%L5*GY`XCp(=!a;!=&_Ambybz z>>A8R>=yu<$vA=-$Tcmxe`N*h713neo)1YBFO(_MK=!!#DV#oi+7?kHgsREldJ_mQD%q^#vpl|Q zbbb#IipUau6;*7^E9~vqT(Zv29wQ&loUoR!4jvZyTR+D{2~8YMTI=$ z{?j*iclbOZ9FH2T+JYrZQZ8M>w|2uzLv%3?Uw6lUOk#=qrx%k3Hp~iEfQ!uT`u!7L z&f!MQ&yfQR8A3{q#l4`HwyE1Ivq!e=tnmUyR~Qz2=+K?iB4s}!eK_FmH6M*OQ`Kzj z^wK>cr>3!xO9S~g72`9n)hA5g>{hH^-2vIN$LbQKS^5`IfFz9QFzYc0Lr@-0%{JwD zVzVZ;Mh;)KqKBB+xkX0vB#3$hct3d(Rf&37A;99wl@V(+Z@m7@0E1#%)J@3Y`%6k{ zSX{Gb523liJB91MOjsp3nG*X|Af=0P1KgHT^oaNkEtC_IxGhc99$x|AhgIHo>4Uop>DHcfkNmG)>HCxZ-AB z`%S}r`@{DI`w7X(sA%kvP$--9{$G7-4gTR$-@wy^O*;j6}Zd*RQA>xO84s?3|BVlki zF$kbLJzXagXs?{C8U>FQ`5w|udGY-NZoFRJ%S+&W2Dodrzl&Ydm?O-kI$Wa3TIW)E z!w4<6@(*q#qd=E3-sl_wrG8OBfC%FA$<-=o?FifD5jYZ z|Nh?U)+YIqUQv2#1tdgxugrc=nt+>thTLfR3NLeegq>dQFe%}$ed_5lW{Ll)OvIx) zBV3(z6!I?K+vK`5AbX?eAR0G(-w50h3^~vyvaag*kKbXrk_X($&^JYMN+y@%i6xvI6aN z0wNxMjCTIoG(X7bE&6CH>$>B6e_(UsuL*wf;=0hbFL2m!GEfiI=H;0yHds}URJpwg z$XcwNKrIXKXdoWL1sBZ;*W3G6^}hYBUORW}s5kpQZsrr^Et^f52 zKp=3~$;Y920&AG=|2VP1{h3zdZCB9P|EDfKS(US?W}TR7)8N}JpMPn}H6AXkZ>6VF zQBlDiOH2oe7!0~FJV5?c0~){Yq8rjS%WC4#V8$hK^u64Mn3CbI=_?T|{jY~OWu6>v zJlg6;NS+O~dQItXpyd`Vg_Kf&QNT+?hc;T|(~%MqnBPZI67c~*FHzYeZ;nSzi0yOb z`!_R#j5unWK73%}Csl83Z?ux4KH6xb*gSvcwc6SOOAE5W97aIhxv|bK?`m_bT;c_c zV{h?rAa$dq1tSt>%%Q;54lCsMAe0v7OcMtK)t|wF?ycZvL;FVWl2S$@zb&jMM6p#H zlTUBZ95P~2CDaYi1n!lpP4<8pF-MLl4GrEt?R5@w>qv;S)EJsln%()Oi1kQmMkq4+ zO5I(?8bgU@ik_|H31CD1wT=vPuv}^0xZIOV_i|igGqp{f0q(~JVS_?qb5- zsr_=lw*QmRTUF44Pdnp|0R zXgtygS{?6qq4R-}%~PyK4jP1^0!dI5WB-U#?O<3r5$hDk6W@fE?U$P?sz7B1&Diigo+!g}8x6H!gym>M&28J4(6W)L2XRt-8!)IG|@fiWrfbs~Z zTfO=vXj{epGq|93VNUZmbpHDOX;)jr%se^0cJ9N7~G^KaX zaU`_n8hQ81f4|ZC|EaHPM@_podGuVP*gF?4lr;3JNCOt7+P%J`));j{!c*qHQ6gtP z`j3HIK(vJ$<$kDAG>-X%GUiMdGA{L$(p&vrf8M=6uj&ngI-|E;ha8+{IcUk=->9un z@X$sp*6nQ&;&vG>0pBO(<%fzS^~VYar&b?DJ+XaPm&pA8eD-ymon}X`6xDutSb#e~ ztE0szdj;cM<(RLeIOuZxjoO~Qdy^ku2PDiAVp5}!fg*Kb?s(E$cHdmX3C;{i8?7Ea zd*WiOt_$KUPPp(~2qaesNi5wWm}a%37>_2*&akV82jAJ=yzYOso|TMUrsH4!B5baF zG@qLbwMEFLM%lJ8?apKsB(O^!_EpQhNVY1a67ma5S4PE70P}`1XJi~fplHho+n}b; zI{xXy2fjcHVv{Ny=%@L4wo$%UH}oLFyMjFAIbT4iLM{(iC4C<7A<8Cz8_mSzWzS(- zOqB#KbxhTi+vimTj{5RF-w_wIw8Iyk{SQQ}N&po(g%>)S(LNI5M9P)LMNTZTV1*f5 zqreK+Xu1M_9n|ocB)>mTDcrd1qUErWBiGWn3>ZKG{Pfu~zrbb)Ao8_nG!mXhhJ~e~ zIu6VyqIH>-m6f%%;*cSuBZ`=Dr5}PP=D2C1kT%78gsGubLaW4A)wrd@GnbVr61^plXlX*rLl&w-7yc9n(|qc;rWUoapLkB&!E(Mk>S zRL4b`a1O-*%)zbbOK(||{V6YHh%j<#gM#V4F|sa`rgDc*#%b;dvXligcQDfx!`l4B zl3luM>DiXVRUc?RaJK09!Mkrkp+K`L`~GUao~19kYsH$k{{fZVu>8v|**NoTwxwya znOe=9f#VRH5mO^+TuIMxBNjeu~*zzNkiihZnEX)k;UQC{4$8ATx`3OhLbU$MYVJsoXi?S z@e$+W#<56eWY9Ns!7+~efk8+cCkzeFucj$Pus4tEF>U|2 zV=N)5gd(XVMbd_%g``NyF0_z@kRqf-&4{Fu3T>)M6l2toR6%7kMI6lW`;aK(Ts~udho$S`kRfeaPut6+xIMx@nx#CNL|XftGK^_`=8m2br^*U z6nzB=9C60T$ao^2$+4A}l&t?}hMA|*?=w-_6S&h!DgkXQtEj-SsH6mEQ@X)x7N=KT zbed;td%{*@n~Te>t@w7p%5uD*2TFeWbYDf1LG?@`)rZfA-NhKds}mmmJ5ni_gn2^@ zrrsdojQolvq;gqU9B8{D%q-SiHR`;1D%-8T_w~=gz}pVjtLxof#1t?`oxI&|mVy-I z=eOJyCvEP(@`13RqX`KbQb7_XtWo$^7~C&uk9V9Jha*{V0X(QjE}3j=3HdSxef#y} zOlAneilu*By?Q>pLSrsB7#~59dUE95IBEA}ib8=K9y~`uf}NZmiS7%YCuek=(S@WW zymX75F2Ww{w@*?=MrQbMTD=;j1!|h)wary-SQ{^z#G|Xd-xkcq!ND35zLR#sP#5?; zU`wJ&G`!5Y08uD^*}r2-Uw`}d4gIr2UX1#T8KZV*qTiswo_8YD^VQ_Ye)|}EOeIH9 zAmWywClsivkzwPP-kG0SKiJ?=ubuP1$y#P|F;n4%hRwkJe@I-kQSWNJ zGbKAQIJDgq#(&mxNfQ8a{(fShsS*}=pB+_6M8z)H@JzYxfDhH-AYj|Z2AJjD>(^&} zPh)bUl>R|K9`eJ26M-acL=}H8n7sD_J1|uPDx0f^`=MzK3L;h3kvfiAi*jKHkT5D6 zQU_F2lrg%Rm)N)AG7b%(3aFR@j+iA2A+*X1s=X`nvhX2P3V&K%{rA!dq|S(^Vg^K zi3fcKWpK%)CKgrfN7jKRn*w2{M1CHLjfll-?t)FbXgSC&Hp5VUr3b1>Tj0dPp*2_e zq0klf?(L&H4ksZ#EKHh!BdgBizH)nX+7caLmSztxQ`0`d1^BEWdhgLV!Mx-N!lU^V zf5eH_ep&p26W6I--nm<2v~^ayGbvjTh`&^q>|#m z)g-o;`jBFk>L7eBWi+C%YuB!wK0OCDYUV0~v8$OZ9?2$XZfQjebrkRyY~U?Zu}!Qm zddAF%che*b$BHgSBZ~QYxh?=k&d>R&6_DInfFnaWY3C|azFD2^^-(IQ&+;d71BKbz zi%+vfqUSQC+?6|exANEkX2`Ao>co|5L=F1+=6WO3pL%1zTmi9edGlL1ViR}&OH8?Z2;b#;DT6;<0t9IOQTJda23~_Rn~vom)jh}pOyBrKG0!Zz3P*_4xzW4Z|S}tZ;W<*dygROZ1?;j zUPpVzX6=SxmFmN(o!BEk??cW536#^gXM_@7^osYB`~E3IaVicIN`J^^PXn*d6rU#T zd4@KhP&n#B#|wRW;~rJ?>i*VghTi@EJNjR&OH9GEhogyN_P*l6WvMss=lqWgu&lsI zo8}!ySqu&ZnP`1V>w7yNBV$9u(bhWEJ>c}e6T6>QZ2a;b=IBDz6H_)14>G;t(Xx2Y z9)0;MA_}V3!+W2A79;a~rlyr;yZPC~#4+&;h=xm0lQou?keI0=)GKVl#yhR!{0G4* zq?K^fgBeZf6IU9kH~ID?@=Te|c;+T0-TV;t@-ebmGQO{_4I4r@MzN@w6TKilP*zOxjm<-T`m@3t)jc9)Q{PL(mhxIj;xu3%hj67=8->|?gWAcH^ED(bsm&)ciO>@Ze-u}$Fag%({ z&AhR6_m6LTu68_HVm&zYz`o1<#}y~3RlLbC_&LJ@It_1i-@cqyKK#o)ZuDQ~H)Spy!9)^NY>u}9~lT0*C%uZG5povHg0W_+A=H8iSQ z=A-D3bno9n)3!~aywcD(+#~0N@fR#9yZB;y-0PYm;S{{FV(6(2{vWOw+Vy9_&Cr&eUbaF!RJ_}AI33u0PbGP&BDT=e#;&AMmMQa%zxsdEy z1`s$@TxARm(s9c_v>cf$m9F~mV)IA|59K=FS$kFuzScw3Fp7)-oktcSX(Av?9)CS{ z$*tK78y7?`hvc1G&pzDvZ3raxetrAa$20AW1b}Aq_6U<`ccE-_((XPTWq*Zi@T)1w zrz+pG|bkvjV@- zK%)*?ehvb{yIr{pRZksJ>!U<)9V!-dnVK>TjTz!~crqnsfaY@H*=Ss8bTl<*DuqL` z;=qfs1XBcXM%A);97R`t{fi4jQ82TawC;NEX@Fna8con8(u_82ELu_8p<}vn!+-Hvwr9{QcDCf?+2@xJnLeM^ zobzUEB@R9u%FNO@dcm*J(T)tYPt==Z@AY}G5481HlNFa}DtZ6$A0N4T97mYu&VKdv z0b14BuOQN34$`2onx`@`adK2K#HZ4x5AN2=#JCj~=lXPC1OhpH7+bMTo}Sqx5rxlX zq|G?!?{nvd?ho3&eFw%^Ac3pS3mqox>ubxku@T@HhMNlz$2QD*5E4Q^e_o<`6w%dr zw6aA`_Zr{h6zh=fcGFN?nJb90TU}jUODh~a|Li01`mh2=ia|<0h7?n#PG#$9Dk&C@cVNkSK< zBxJCo5k(?T7L@2WLsRe0Z_}5B_%f5;h6phD43;V!KD)zKQTEzrx-6q+g(1O&nZbAJ zQqu!ReWGHFem&xA{up{PrmaMw|(QwI`7N|Y+$kqA}{#a|(CM2^50-%m?VHy33P6hBh|^Mh|NZ|}`5 zyYc&ERn?7jiwt@(H9*t^Gmg%j*Y|EY9rYy54%f+_Utw)lVj9lKVF`2`;aL6f&xhRj z8F|n3P2v}%3i3lH;jlcfSgL$2-L^M_N|z>c% zgFviwa7ZuTNN2|-VJ0ZQdC}s<51&1QG{XnrY?d_nb(rtmNkhuy^_u+{uu>_hhRlfi zk(;z*nET=ya`>>j(6wVM%|tu;e)IPi30TNygCm#Q{|eH#OP7|O zU5ZAIdY>T_gi;QlAk#lSe4q3ftVWhppB`cH>BXDi-XTsP)>Qt0R&x+bbV>bb7(=Zq za?;XTk!@mu(BjeXCkkdaiv^aJ)i~v%qg!>JfX-N*4Tnj46&J^3V27}{ZRL(x&{dJR zEl^EyH%&`3!Yar9RbP=`4c3jUCECBwl<^t_N|G>bo4uJ?zo=s1WAKaf%b`^U93dpD zYKy31VE!xx35ZOgW+=WHrmmocbjP>?d;sbxkeXXlqW8m)q+?>0cUgKojp;TQ8U%ng zt`wSQo?HDNl}VSFt>gUwLvUPT8JtNTK^iqqaz$E(_Ua9-uR`M>dW4vbbq%U|Kcho27&Ec+ccfl>b$F^41)YUnhd(1#UBMQn51uO^zuJNkU zju&JUA&k+x-CWa!F0OCyoA_9Ou?bO_1NVc*M8?Lh!o2g&p1=E6U+X_hwg-4{w=UA# zA!boTwzr|7ol2xHN9nHI??~8gyFb}%xa#~9D$&CmqKd~s`!>A_tN!$2P>^?UZ+KH> zWtg|ghTf@vzXY4%7|T0`>8@h^lHq_Lu0=N3lA0($YtVP~w*ZzOfd#ONikBl#@$`&rO{q3H1HRNYY27Nl~-7mJ>rP`;%Q{ji^Y46@_+8Od-wRr7k$ap zsmF2l?1K=VUGYF7*xtUqt+tXC=y3(A0DcYU9@8sepam@iNj4(P>C>mfohDIl>_DFU zV;09i-$TvLE+q^Qau*D^bB_=0*nw@Or3wXAPu(EIb*4eWdHYscw2%En8VWuG-lbR{ zF_vp>Ycm&x*1!(FvEwAuSpiM+lGBu997Z2Ar+jF4Z zA=pPT3)8Q)6*tWMDqn6^>rZsxxi#R!IAH=@p$`JEr6kDRZt!egHjcEmU;$sjv(UMn6R0tSgruvKwz3f~HMk<&J=bAeZu>((x3#z=h|ywzseKoR(mc#4J!L0OxSS)F=IR$K-bI?k&`>{%7yEd`Vn1 ze7BK3-o>=JP@1sk0&ila4z^49P3is7KT>nB^x{m-&R?w*{^Iw$f;J8^Edo+R<;%~n z>TC#}%=N$MuNZLXL{2n(V&lU41LQOhzHHh{uz?Rl)?A zh_QobgQ(_uVBOrB1uBtvSwal`cWaHUij*>EWC) z6D8|H-oR5LOuF(-Cjj`FGotq=`Tsg0ed4|zzBYp#3mGvS0LW77b7I*T?jcBvs`Nd# zeZ%2yF38^}?-6#IG|g7ykvy6*p?88db^E=}cDp4v;>PD2i6$gfDlbXe?IgD3y`9_C z6h(vL=MVNT6^UM)ayL}22eTX*GhI#Aa^TekQ^M!6)gM_W@(TmgTxRLj|6l2OMGwLa zR#tEJ^-i1AFm%8j!-iYF{U`jQXotjTUG*N)rBof|)19^cDVT z(4|$AYHv(DG<#PsiT^q{tsGg&JL9Z(hzI_buCimi+MdPVHXI%_qQt#d&)~ODW3I-y z+2-$0j*E77kwuY658Yq$ z6NvlIZ}(JgPJXxPUQLwGxoPb;4?1ZE`FwxB&w&Nzwpr!6_v7!C{|=Ck5*uB#BoI_? z)b-2-Q~pO)|Kq)0?9trM3QE>fvS)N3AAa`0VJFQNpD(|>3YHj}F8EgC4TWBT>7S2V z_qAD-n!hgKAMb5c#N@P9QYOnPy?+$2;sumkQs8tOQ%%Om^Xhk_a(mvr|ByZw2F;GW z7i*ioQXBMn+}Hrd5@i8mHj|{fw6lF^L;E{6-ah4Y@a~crZ|&@I%N@POM)c}w!77wg zt*N>W8WiND=43xEdXdsw#$BtlySZ|zfONk2O>$`JtBzdnA-sVNQ{&$4nlZ9qu+M{V zu^ug8w8pGGwr#ch4!)h}x=uS=t@oFR-zV?gvrm}x8A(hl+;qDDT7-hqR|x~%s=(pH zf7jRWxBm6FM`jd`9XH$PlYaJOB9#t5h&MRx^;Tuefo(lBdH*t}Mzw?pMWwG)aDF7# zz#D1E7W3cMkuz(AlV>UG_8V8ncS1;gjrWl--xatXZkr-wW-F4|!)wuOUD?l$q`4w+QeJaRwj;WV}aZlk$1O;A`$)P-#+8VNM*%G zD9Y8HpH-E$YLJ?2R}c4BbAWsI@{Eh#wnELd{)NAf!DTpJ^jx|lOOI}?->C5ybFtd% zIPvLeT^RN{Tf``|PY-S0{_9U1J(JbS8n<^G+Dt7v_Ej8)CE1%3?z=y!ccVUjPUtk` zI!*&vX8HBevHgu=H@6&HVNa+VLOI9qr!5|gZrry@?Yjxl&PDyCNEdd_7%d*S_$<6$HW4Il)8h}9 zIs@Y}`wKJrg0xLvP8_;d_}u88_7sG$Y6~DS;|9AM-%Z)n$R~gH?9kZcr4#UoA{dX> zDkEdbxyRy_@e()B*f~8O?%d!F z9=(5;plOhh@RwM8?s4m_1aowl{ojql?`_Q*|DM_kSq-U9SSnI^23I{i-F(ZOatH&E zA43C{pL_S_%?`hbqJsy+A`yZDveMIsDC%BvGxfafzsbMVI`!chI`)?}9lw^hNLERW zpTXiO^4R3nZJc((1Z6zI;%3_if~ zK4`0fp#$IzlnH0xXpJLLQCVni=p}&tf1mkh(c{jf94>n`1UY63jmm{je9)!F``D^2Q)v|5LSv!Nl@& zL&g{h+^h>WTQXM(QB#B`=GMrM9*t$~`}TIXfQ3wF>+=I)`M7q&HiA&VS(!<`CytL` z>m+3+?f?1ZmIEVh7D{k|S5y3M0H@Y(z59tM9wp3ss9&FbF)tuc^w0M0g%`{v!vWBY zX)bwRF!}Ea`a(|sSFc}x>F!nxU}z*rTz$H?(`#zLwwVm(D<~hw_)UKybg^%Xh1Ksg7{K^WdZ zv?M`mWFn*QVNimX(7G@~Ou7U#Pq1n!DC1Kd7r=;4@uE zNkkM4MD-8@8Nuq1e>!k~0~C4kAExfR$@;+s3z7n*I@ZU_mXVG{#Hgoyxtn)Yo}V9P z+LkKkAPcFfVW9c=O($!Si$hwix;mlGQ>INr7mf?t!-pV5mg#jv+gI$bAIN$ELwqiO zWAMv}->vu2({TD!YptIyAOTd?HOcE3VcDqT)YK!1n>7#OF)dVX&EcQqDApLBP59_# z_@f^RK;*$J--W7dQOzt%ZsUFvr0|0Wh~M;It=a_U4CC1)}?RPOtH}UMLwdCQ-(tSI;Tr-y}Dp;*&BixJ2g4 z%abt)wU0*3QYF88yDHXCYc!#ZTdLq}nn(ZL7L210z4*0lXft z5GUOe1A8W+t{nM2b7dy*NE0_`#}EsGkqGckzST@JMprFbbOBpOqFe?wP}6TdI(h{* z>{yofgrJg}lS3Nk%#TJ})_^q)8m>+T`@=V#0LCm<@xYe^u%I;ti2!;98k?l9?h)m1 z)zckcndRr~aGwFxMI^KQ9C2Q&*b5k;Ek73;6r?pWluaZDBt{E|+T*$Eoje_~+)GsW1AD%IMR3o|;;pi%lwAS5J&PiJ%#?Koya&GK?lRXMYM0(wF{ioM+&&aEHBet$Q?)2{K z`>*T1RCH#B&OW$R`NQ7tQzFcD%*`);Oc2Oul6FbVUyY6HT!eu($4SwO12JKDB?f6~ z$ciY4o6dKy+%YLe;yrBs!9#`^e|ox(t6GOp2F@Cu>Qo4iT|%7;0ftc-bb0}l?@6!x z+VwK5QseAaY@FjtEvIQu4B3Ot3-o}!d3nI-Z)U9CmeY7V5y~UDGgu!t7p&&XnT_jk zq5=Sibb1{D5$6vy?@GrD->?Z03(OaGJaCC!ZM8M!<&vY7-lMasfk%nH)D!!3+?Qf} ze%=~1nVB7ie(fM5_c1fw`z^c1bxh4a13qhe^(92-g3Za_lu_#O?v!uDwXg%u4~d!=~BNkee#WjvNUd!yI;o_b1qUrdcJ* zSMAbob?V6Jf3kid@OlpZrsv%@0qn4SpPuRu=}6mB_$>FnAjKsGTeRBd9p$%C=Xs>5T9Nu^zoTy8_U83xfH3v>p!+!GSqF@KZ}J^mf*7x}4^L}nVC z8ay0g;Xw&-N|Irx_mLq<=R)<>J}XWBF!){KA)v^$Rb2s+JSE7ku2bAp)(7Lwmqk$m zBLic<^rVFwDQEEg5*&o=(I9REY~dxICmL%k)$wwJhVtFrEq$98V14HZC*!jkdkgZIp=wV*_34;c> zoC_>0-hcfLJ}o^^%Rz8}m!88$J4&qI$4Rz3Dt8zi{;|*eXJO&wuJ1qAXhxqnfw%Cj z`hOq?edsQ!^3=7OuN-mn=B(J1O|c2Dqz#i6+F*UPbb=qI41mTwRV<#@n$90<^Db!# z2c*P~TlFwA4kcVmT{@xS;X|s7yLY*?{NjMcr`rC<1)yBPFW&Kj$F5x|?ARMNu)D*8 z5_Ve-H!20JMv@eR3ow^elM14QCfD~y$P<>0TPgEN46b zCxhtKTx-6~B*oX)@XPR&q7{SJO3%UZC8~JJvJq3| zaNAKxFXmmK_!1necvyxDlO$*SI?-1$X%*6sKmPfuW6@VOD8b3D^YMzo{cxHql+*_8 zxuFiL0bDmSf}xC;*Olk*THn1}e^O-5A{y@QWx8_W=C{I|OYT9T$;^E0nl&o)4xUU* zbhWjW1X4fwDzger-kx#-j3<&Q@8iSyR3l3sifBaT;yS`F%j zy90LZS%7Z+LRUz^eEjl|uN;?{lLef_-U7(@R((5ef+!;M>op;VnZiI^^G$avjUfw^ zMnNiw;@bdk6#PTJtgQe16~?Vc+lr?$LiK-eQps)uodzY|hML;hYuDntwLjnmkPdr+ z8$#sZ%MLx;!-WlN)JM4H;+jQ?7BA0al$xngM^+FUgOY7R z;VGnGuvHifJ>5o4htwl@YsA)4EVq z*V!CGDQ0Hh_p%76-v%f}oA>SAi#t0ye+SKzhg2M&`FwI9C^Y&p|z=r;r;zq|Q{pgb!X1MiQ{}ktNSm zxDm5QVn&*#El&d6Zs9tCb_*&5DhjyZX#LoysG`5Y7+~j!D}9YtgAR)wQ0QE-TAK&h zPIN2dR6+PqJ}V}rWz6?xbG}R2nT-*+AM#Hj^n~N4m*1b(m7l4L0NV%bzg0fNt08E- z;Hg?>{1XRAq8kz5{9xG103Z>3{mq%pLBMzEo~jW_Nd5Lv(w|NGN*Vc!`X ze~li^nj0R>gphwrTDuVO;=F$SH6~XJQa_{`e{Z2Wr;nX|Rkw18;1{fLc(FE@aeGdF zNf{kPA4H1a~`l@K2w5eyXk_n@iG!Gw|@}8Hmk*6;Sj! zJHLMQ{1sd7(wnS63KD#&Venc|2#|rdrN-V0sH+i?!(le^l6%Vl{{E`?(w~E zg97py$DhF9>+hHU%sAP?#K2lU9P7eQm}_!0X#^HGx?}GT^Vh??{JU9J%jd69PBJ*$ z(z7wP@aTIxexjT^IYszk>ExuBIuF|abIM>m$ht{| zXpe96X_}VT9a_Fouv2{c{5kf+g@5bRPoBiJYx;n+idu?0{G(w+o?C7WCTR!nD;ao= zP2OgvBpFp)V0n=v=c;|1a;4vasy)$xNR$o^f>F$*_vRWmYNY1D3ITuHTu{1$myONc z6N)1;hBla}AFRsi?;<54($Qj+cymokr;!3z%2N?$@WJcw&~(MXzh=2 zUVCgFH7VG?xYpBaYQBqJr5(|qA%HLkxbgGQlL&n|OY1%zeb7e8&Q)18k0*V{ZFGJ4a$Nuio>GXGzD!+y`bl3COCbpUv4^G}QjU z2wrTBOTFnPe;YEFqtsVkgpmosr@Z_EA;-i$iPmq=L7xFmwp3M`o5P&SmC&bvl0+=t zHY5ZYRd1OJYVsA-y1B?RaFzCLHNbkd@n|pUIdtLRM&zq?fB!+TRG;A83I)0ti6KKo z7#CzN)WE=AwrnSGQ6Bj|X2};$pKin}Z+yT%Rc|@AA=SF*j{Nv)%b}4hcvc+$yKbk{ zLs`~z(BW~wzMQNb_*6^(a?|_9F$BAvCz!JzLUoFB$v54};d7blLR=*cT=5iBO&kWG z2Z9m?9HG)>9E^<#XLUTL*zBtV`}A&Zw;A4Zx#UJSB@Y#@cgnPhI(5_ND1D6ToU|87 z7kqmw&*Ag!cGCw69W!GDJQOmxkecSDom#9mXA%3`&m}fCo;WN+1rYcs@PDS&cr2MS zrvTKf{IPp@M7V*1yzxzu2JwDby>5q6p)g;6R9QJH)QqY_Q)XkV;ADo^Ei+4Uo^`xN z+OZ)Btf(p+ACh;I!90ev@im$sJ3BKkUrsRgyUyg^&28bi$A{-VmDuO@N&fa|x$>J) zhZ^6!{PVbB6nw|nwNh0l7#sgwUH4@3l`LOhThfB^9mzG|{P3!E3p zu7yO#;Uoy1O_1SP2^ko)>Xc;H==VGgXz{Vm*s^6yH3geT#36`U43^m|Emv#BnL1d} zdE2kK-S_tAQ8IQS5+hdb`B=KqD!g0jr0N8zppdgqGhY1^HETG%$9mj4dLwV}D@oEYR+N{PI9W-)iJLO|E|z0PTyqnFbt=)1iT z`2rr7wmR~()3=4aMI@HEUh7;=K#1;0cm0l-n5oUs$@$=PZiG`TknA~ED~}~h-dA5oPnvq_l!S01zPRz? zhxKRH{?%6^5^cY3>w%RDcdTo^4pfUdd1icqrxI-c^h$ts&L@-{&5drdx>kit_rHX{ zgO5GcC9w=V^%G~jftFVoCneG_!p!!@F@$wM!F<7Ar<;X`vMQ5(RAGVLCJXwFIz|%P zu5~^E(Eg*WiP?nCm?z2IK1Q$aGkW;O!-qhW?if|dU+L)%{F-Lekvzpgg8c&6b-#VTZ~6cN23Ua(L6~c4O&hu?Tp0AH)6(G zp!;I}>b@dwI~nde!wF$*bM*Lez;5scfG#@txyq1fIP2cLd4oq~qx|Y6ODKO?m@|r| zFL|?leEWJQCpusxmS9g`?z#X_ve&cAbCpV_y{FjcO`!H-U#NWZ{h=YO^a@=-gw_th z=3K4Y!8n#)_v})ao*HB7-uPzEphUy(8P=bC8DvQhESLlW(f(qViD+V(Lxywc%{6+^ zx)fg}1I9EoHTahzCld9z@3tz5Ge|PNnf~GOUA4 zFESwcl?cPQZ2$S*B3*wioSz^I)|%URd^OEpsWbPNma+${5K^P*JJ170^eLgAKM|ds zwiUuzSm6w}7c+P4_|SZU(%a|x)-1Zf`x|xt^_U);f=gm+_RGW65H7m0z)moD^6PDj zHV)(=f1@;R$l$?*L2t$?eAVSGF$IpBwrw-^Y+s|h`!GzTto6miV^fC6=`CEf>Yj43 zpyphi)k-E3Es61$@-RX9DnzIgra^N?E#(QxE2eNkS0x=S1k2_^h|};nDGV{gnm>Qk zjYZ96z4RIEj%cGp3Sw;;2V{m;%GG8*bY*=1C=O3!O9ea20@_R!;KT5%ENP z@L<6IadP4~hV5jeLUWrNvAmav?dV4l8<66xyx`b3u5>TeKN#o3iVBKDM1<_rU=DCo zl%@0qzd;8d^pA*tRipYDR&Z>?4(HK70fQJDJET@1pwSt;CgNk474;-1D_#&3fcV_O z^r4Xx>qm_W$r$h|v1~U4Xh9uiX=4Z2=)A`QpCG|58h<35YRUP<860X}a?Xyc%-U`P zG|L_4cf)>q9dH%HS!ReP!i#ZnxJ109qUnTV8Y?dyluh5XP^n% zZ*-?kgMv2Hq+kF3d6$-nh&YTNmweoyM)eReEa)s`#4rle-pzZ`p0d;^kqJEwaa?K2 zrd6uaeDifv6OQsIwm4RGS^ekFZ-tr;2ufr577!;epU3eEKmgU%V;AsJwo;^UFS7{H zc4Gs?3>#SK?9zWd^dZ{e3BlR8-z2HHae}(~(c(rpfxv3z{fHIch93%#TH9y#On7m~ zrt95c!V#P3@LXO_?vyGPwie0}OzQ&j>p?1B2Zhx=0!&~h8a~;+y0-SdSS^Z8QtDui z@)5cdtPm41wh?b){FSVu#Zjey!S7%BZi^kZVnc=qbI^bzfQf;I zo>z?iE$0h1WKQ z|NMHkz`M)@#_a8)`>IZTH>1*xf&Y2#{DljP&h_{zShkp_voTZ-HK7FF`!RV8Vd4qX zce<3Us0Yqvrl5$Icrz=uD~Panf21a}BJC=R?STVUA~`K3ud9 zQbRfz!MGiBtOK2+EQ&2KyiQ(moY`hck!J3$){8UVvP(?V(144DnxJ14mIy zE|l&REWH5QK!bQPl*6=p*(pnLN}-MV0s;#~(e|G;H>ul4e!njJA#&lz*RRfxUgtA| zS-_gV*4!=#S(Zbm%~`y3!sGYDiU$P`6f#Cx@>=IONI7A!F+%Ul2XMT=w6J*?q&k#$ z-}d@}JTjeu9+(l{oN{N|@1@Su?6yo$pFk>L^363HU0vB<_##&dJBxrLxQv`$91|X{ z^<~gAVM$*$vU5w{7V5`hk<}R5hitE)o_!`w=3q@Y*oJG=e7Qx$3+2mDX_;UB{kohVBr&3Y~)5|`6 z8fIcV5D(`v0T?` z(NX5e=N^~4E37EpG8G>{Dt89>46Zb14;+tFg6H+qb_D9Tlrw zVKH@NC~M{K^XHM|qTB<6#DW78U5>)=7|Tgl+TTx{Y~HjUpS)D23&-JPB>(1D2d1^n z=jAA7839KiL~7+No|C2dBYkh4L()R5nurj7N83Vu^s-I1;x%T~W2a0xmePVorhH@V zo~F^xF9lO#W@T!JrjuiMb4@e;m=&)(sL$Rt?_})5fI22`LG1()|8o$Pb^Obkh=8WN z$y_yBte^SkoVvEA?ZOOLB7ziQ?4B~Ji1jsw4vM*Xik|k`ndn}DKbW_GyW);sTvGDU z)4(;$``d6#3^2i@n&fx=*{ONL=;@rZL^Q)+tyM3{=1{XR9OmX%XUU3C1HWQ47_j{ zxZ5^vv|;eY8310Sv!Q_PZsNp=F^lrR$dSVnNAZ1YR?YFFM?XR*5rP{KMfi2)kBK(T z-BKm={CMn;Th!qR#UvOkKTzIlW6AIzpR)3BMV2OlL`f2Jm4LL4WpOxAO? zLm{9L|XDxV=V zia_o>0{jYSs+0ufaY9dPqy7VbdeBZ$#oaYKqd+WQMOt$bA?+4`&LC3+tesxpae>wQ z()$YLY)lw`g{hqH>mC)Ml|7hi#*?NpZk(hEdrNpkDoH(SZsD}ndyjb+wA)Lf* zpXZ-m3@x^NWAup$5~3XAlkKygdR`X{(Yqd zz{yD96(3Djz2}mN?EP>|eCSD44kqIV#$K*^!-J>j>Y_G7KvCn0E5N#`Tz0r1(Q6Me z8*|d823VmH`^m__-fTKhtroF~Y zdv~j?$z6Cn<{KnMOzy(<%oXM~3^81aG*?}fju@aQQ{9iBJvm~+tp`pAliPob8TP$#WTsR02^sauwYU5J`X`QQEwD!2y*&Es^^B(d zH%X3;3K?@zQKLb$_KZZOfUz}9rpOYzFnMJ7qWS%V*+sNHs7TsEgCAE0d(Ku8pu1(l zn-e{-dgj%h5qaLxkX70~XP=&}rM{6PXFPyGVxTK4lZ<0>y72$UQ# zv%%+}fzbwjmzl?PCwQ&Cc&s;!?)gr?G!hLD!h${##90MjNKYxg)lMq z=GD1oeg61yq4c<87Qu%Oq4hx>1;3w)=WjnNff~>-PgNSvIj*_Z{!-@+&d#Vb%3pRc z{iHm^p!$N%H!SBr)A9d4lc+F_|2$QJ4Z{k59YZ}(5SuL6#^01TcuAZ;Z*_L5(fhk& z0fBhyAi}tQTEoy?Izga!upx~NKe%+l;J|z#XSVqS(`Xo}jMD3%nGD=d#X+(S{DEO& zeS{x?38y9{@BO#-by@1lvz98k%yXdSbNFDBSV-jy;uo-6NyK!}i4DmWrUIvF_TAguN{vai%xNVs6{ zx?ArCn{kdP@4uUvqc4yoe<1Loy&;fs*b;ft*~y!*oHq}9R02#C6=$7ZO#8$|OBrRQ zQh_(EFy=5ybU@A;(W(N3X~y1fo|-Ztrb;EcO_YSwZ7ohOC4f3yzhPK#fv(k{fIlEm zF_Z%3!QCFtDR?tj6&we_=(9uv5qeouu&c|rAr^4XD3CcfF@-{6M`e_F>gLd?vsI-r zPHDdSw*1Z#X|iB8#bQF_=!%gKg=y93@BuK&MQ)D(u}B|!G`(nVVc5ZVUm9AdwS(h! zwN~$WbvGQ#Gv}GOb%T&lps^BYsm}=>X(fLX<@+tn=vy=4g~og?O&O-%_UW*W(;;ta zXYxAP0*oK;pb>Josnd&uY;XX?F8GfyF;t#t{WQc)5Az9}ffg)) zFSKI9F|_*uN6?;YYGT3tv%AcX1TKP)J|ymbS3RZs(}tqrL)L>u{Pq4=r9D#&9?ro1 zCrNFFi{9;{vRn$UM^{>LJvoXETvF(3nGvFJcFvfTw(@PK*M|=uV*ECwSDveUL#`D% z*1wl?R?@ukz+pCeZ%7jU0?4F^lfVq7)H%+lv7=P9-#!NGuowtIfV_~j@PRHQau%h7 z;I{XioX5W_-(XA_SK8X#ye46tx?GS|W$~fiQyF^|-M>#$c5}_b zw%?>DSd+m91ordGpVL8BRc*?8j$;?Ptu0%ySjUP7`bADojI1n%8DvahGgA2mN0Xka z9o}PIz8lWpABujSjQ)>^>?T%=@Z*5H=^?0u)hF6K@qB5 zcgtl~z3C(eMKiUP(T^XFek&gSSScGmz=gB8BRYaZF@4O@^mpserT6E)!|Do7pQ;m8 zS$o}ghP@GMi_OS4P;uOBpB&fCMU`ceBbZ5v+_tb#sjM0|NtLEM5B9)WV~0MIGovxh(wf+(U|GI9QB(SLDcFC zb%ER9HQIVJt9$j#@BET}_?7p%C!!^PbkY1fqxuOmK#?QfKRzE6SCic~OsG9XW`60; z8}M6(*Gns@Ce6aWA~S6*=FQ6iv?;9ScqZecys~oIx>6DaIgH85>H^qNG{rY#<;7Ph zcB!Ew%u%&7qmI{e|6|W1%h>mJ4*5;mRFn? z0l87q^8hFYER=?YzR1oFDL=sw5JtdRH*JDQKYl!cb}1>Rrk^-`{P=Kex9YJ{ng{jK zBuSt`Oc^;iV`V?93XHt~={Wj-IgWZPg`5RlMLul&l`70+s4~FZ7;Nyt`>gz9{cXCQ zz6f@5K84j*QZqpM-;p<0neGtAH^=46#JgQ~4z3vi5;?YeUxrR26aaf_h!AjZkSY+K zEFpSn)jJj?Y?)(Q++pK zor~|^KYnl=2L%{Wayc_Z-1N&{cFzRvlGjunTBu{2Q z7-y0qf5$kS`CD_^_&RmPfT~Us#X($Ftr`i+zVlytHu9-X2`$M~;M=_#LtWi&{m_*W ztF&GS4lAj@fZ|!?#@n@;GR#n_+I-u*fnQ%i6mProM&s<#d$*6m!G}`PO3o{EB$oSW zRVEJ2adqEbqnHqE$KTfFCCaMITC{ZS+IpcNViKI1;Po$>h)oe}n|S$JCbAZFm#~C z{2qMT5OVO;Vr;1GJHxqAYt&U+Kf(RmUGiw&f;sxJ5sGPcWh3H+J|}vd>LGMoo^0bB zb8`^t+bT$))ru+}HgLaz{E&=Qnld=+1V~H`94*@C~tas=)*2!6o|Y2hR%jcFfB%z`&8PSw4NsY#?4@-zKSyoBWq3$thk ztrHA#;P|q9P#FnxlZf%!UX_vCc9l%0C3&~YJn>~$XW_PXi&(ATecB32V}+(Lv#@bH zs$N>4>r4|ccmfo3p$LqM%Xso`7k{P(6^iDaJ9)0ymohJ1%B}$bXk|tL;w!+cDVt6& zmc>V4THtIN=RUo)W43&Z2Sz)+xTW(8aazC_-=MP)sM^@Lc)PAsZjEdFf+*;RYi|C* zt1$VdUgME>DB3}+8Xub#wmYvQblcs=_vGQ&w+6L$e~a;1q){2tb#LB^5l6KedP@on zHW5b?RcXjlj8R9`bsc#APGjQ4xK|g&@w2*i?K_Brao5cuh(a!AWc2Mli^R58V*qK= zDzHeV5+W7@mic3sc()mx@tCw*_wLs@em|VlQ-bmB=6o?fRiH^(#XsS0+Y^&5E&_ zwa0QJ=4(`OCBcs?D)Ot#94~N~n5XOOX`XpkpOy)IMR`Oz1rElEjAxIJ={7>85sRUKF6zaypKlmG`+PtW}nS$nHx}k zIb|$usb~X?NMsv<+pg$~Zds473@hCz)*RyGbQJQxvy3?3WoN=vr|x zdXyR+lfw=gtkb+GFY@9`XeAgqikO!jSojOJMtNTzrz7J2J>mD_Zi`{-Uuy zdQv^uHX$QYCLCTfriL#(N}C1`Opub%W*$08e~N*uF_BuiH3_k?|9<{zr80ERA|6gT z9BwTwfeWcgL3fTFMO<1gEK`cIl}moDWkg)P@IxLlf4_=kM@Dp0nO8 z>*X;M(*&vL$+99ZrX%^psRj6B!GgnP$$OZQp4?$2VkvbhMQwh$t{sM`@qdw81IUM` zZbSrbjev+JV!~zZs#-h}(6kdTM<^f|DwrBl$bX){gR$TlidK4{P?=Vqh5KG!-ck4S zdtLLu>_euVr{^@LFP-mfHYtOyZly28K?y0beqnM|skV-=PAX9k_ss zQrOfU7xa?c({D{r26(ntRGh_1rEtx4%3}R6+DKN#UYer~?yZ|1T+NCWHgrqDZ-#g# zfBm6O@vNru7ke5Rn^?Hl%AoBr&re``@RW zDY~y%{!WhyU&MBw?txX>hwU3i-xs3g{MAy3$gw9xNKMgc%^HR2*R*WU?HiswF#D4{ zuVonwZL^`l@#Amr-33|{--`AsIVgOWWG`2KKSUMA2@r z1pXc36Vme{mB|ZL1OD$%?S8gJnV*jw3lHo0oiDCv)>nDoy(d~IRHh>SM=kFjdmJ%7 zWZ%K!yT87rx!L#e(U9yAw_mT8@nl&@_DowdJu^;d5&t*i#)TzLmUWk{m+5I+(sr*f zY-pIT)2RM`uPIU?qxDi%Ypm?Ax7#_<>)o}MGvoX=eGI|s#OBw_N+jR(P}!m5sMWLf zZro0BI~?4yAZ>-@pW*v{U7YL7EfQiRM^s4vS?_hn1a>H20-7sw<5F-YD$D=gzu2$n zybBXP^&j_}?Wegz?1UezhzeV@^nf1ND}N|4wf!#vv1>%nL&B9G@z-79=P1qxjuQTb zW-!%rY5a^z93@SXqPHr~SwyAIvW1DkHre8F$0U!#yXxJinF{{9%R2|W6z-ri(V2fy zYEGCkrE_5NTF}zD%CYUQKSHBeiK!!U%^Y0#*?7}OZh%&zvguV+nr0FV2}3qd8k!Z7 z>FJKU|N8{zE?#_3VS30cUu#;bHAN+*8w#Etkz8e^-{ZnG&?u#F+s=VYe%Ly1->vDZ z*Nlmxe^qEo3-}bWsx_MMVfmUbnk0i<0AO6)G9CG5Q(LrezF1+D*0le&yOK0?st~+f}1&&-0%&{0i z;l(37Mc6L8u6GV&3b!lQ5L@0YQ^6e=YtN7qcYb;YB8TcBGM~twbG*RVqj_L?{|VBE z-1Mj!0Et8uIk7=F@X%}fO#BHW44i)kc)zzucpKGvSRx`eRtO|P#hvxs+slh2uG-T* zcU`jr!V2W(U9<;HADHk8+057M6XWB33rFu(x4#-KoU4ZsNVD}I%z#76$S)o&18wCf z-wHt%a7$}QH!>{lpU`%|@`$lKe&h&z%EvA{F$~8)dD}Mhi};z=HQx!T6A~n8PC*=P zHJJP1;2j%Lm|pU>gOPS43@-+|Ome4;EM;VthXTe;&xdScPIb8DZh9r;#3L>fSnt8Z zwI)iB7y%8w8vr;cC5CooGTD!B-Zs2|;>47B9ynUAW1}^TXW&JD;kI?4rsyhNPauTG*CR)owPY=h>84kPUh^EzSOd zV*%n>%sDS^sz_PwDsaOZNmYibj@n;rN#{q#JaJp5ex-sN^^-ki<K+U${W7oIb2@ z+P5EXHyBS;SHH@VJg&Y{d-^!essW$B5*m*H6K!P|vI2)I+KK^%FLA)W3Rb}ckuFU| zF`&5dIH^J_R^%}QWb0;p@p<&_YOPtV^=>RK@afO=8GN&bM+^-)=3!rIKVLT&pdil| zzxQ{A$4*ghm!^~ct!P1ZF4vI)U9kNJlrtdX>Cmu=t~reY9PlMbU>5Vt2h`TqVhN^W z{DwC6pQ~39ZiBM2%CzTHP*M|+LC)HmjT@V{kIJc@9b$$hw4rDFpG8jpzI$g#Rvvf- zvoY$g9!swk7sF|H(k~VAs^B4yT(itq9Je@42@B_e1nH7{T+o+U6eBP?Zs;ycuhbbo z<&fz~@RE-oJp|Pi=NSI8QEvh#`du8_ssV_4=FD7`NcWs-gdOx+i=|mC2af*Q9d*DH zG=JL)_NXB7qQevHtkl`uyp9!;S4VRyyIbu@bLl*JN`xBMX^P5n;@;Pg(B-Ssq=`5E zR1TdtReP3M&bNDXE%k08_OR64^-eS|423CE|FmJ_5@=C+a_|78=NvnJ+~}9>AJFim zNf==;*(u)n`j~OmfZh14;|^e<&9&%gfAOvPc3Wh|sD#@j4- z9UUyk*$b#B?B_*NgitVng_HpaGt%h7f#6fOPsQ$KTce5}122IE7{EW>UF}}T1NZai zkGh{vAw(&Ea(;6gWk?n z{?%7(wVI^(ig^wW`4O=}m_+e3`H)-cUQrX<=2T9{d~@6lu= zms3zb)CE#?P1%my9}^0M3wmG+=a+wFj?MD~FiZFoBXt3`zn|S)3MZ6(tz*c%xpVn# zFz9yX)&K~rN@LO8qx2gmRfH(T+xu(tC(CRH0CEXYX4ZvbAqj4c<=)BxP(@=?pr;@? z;Vp3KU0u@(UkY$@Px3^`Kp6RW(S4ao(75FMFa5tjGkUkn8{fi+E4b#K%FV@Q6ivGp zLX@zPzKS;{?ztG*21Hc6QJ3j61XC{Zcx7eVUz*DcKI=_|1NY!MC~RZL61K^hGq?51 zn{9qySFc|`?n>XZk-sXjEk{nAxXMsVKB}7n39-vBflH%^dZ%tWD1X<;qaRINInv$nA zj1YPTNM|AW^)xqbIkpg|)#@9h|NAz{>flU6O~48tjOu}4gm^N~C`#$v9`xS}2O|%RD-Aeu^{PQ^O1YwY zpI?_^6KE$Gr854z5&BGDSZx7EfD`D@ z#e-J<_p1L-W7i%CW!k={wxZEtDr+Uhl%lrXkk77a=0Kt@CklS)w`R1%Uy4&U{>@9g(|zyE&zYnplA=Y8Jixu5%RUDy2=Fg;tY zOS0d1A2@J8b|FZK^xCpQK?Z4x*Yd^B5RKrvcg<5&(4+6v<6RPyCq-s6uT9J?VP@S418TTn8|g1&+1s>}lfvfUr@t1GIA2d6g>CPn>yVvkKTSP%ku6 zl$}2x;aq2Q9I2Mzjsj@<#DzhQ;EQPny+~fa_&gj0rY&+WWn3sp2Nc#SQ*Ih4Wd_bD z__0IFxG2x0tm8^h8=SFiH4;CjDQ8*ybLSui^@N@Mop>x@83AITO3%|0`mt}tLLX(d zvoCV2=POOZOC`mF%%)~Zs4S9^fT2JC{2aLrRBhs?c?NZFRp5`R^n;tv8*jbg6- z%FZ);CQe?WR#0XEu*EK_8@%tmj{|=LKQ}T*5e|okhJ|jT<;BOxXBFhp!#FYBU2wtS zZfBeLK1Sv!7(z(~GVx)Y$g;P7I%1W zIglv?4okX6dc&`|z!U8Z<_b2maHPbgFj zg<}F@wV!Z}c+?)X9|n-AV)4OKB%SJ5gDZkShi8+>r|J% z6cCa>dFpE#zvv=YRM`#}961_4;S72^D9ZQ;1RUqEIUAgu79DJrlfO#O92ZAAwiz}X zXjIs2-2q=J%z=-=#S~3}4O5enP1NfbPn{J7!az?UFzW#Z^DTIyyjr~co`w0+`3J@y zpeU)6ER@-%O=ynPd1gK(ro&*U!5gBi3p5{JzgE=Y?IKk+aMbIOIC$={EEFs-;mTMG}o$QHSS9U{Dx`0%SRH2&~l(H2vp__@@Q& z?*BVEU&L4OG+t$&c>a8N^MCF%v!n$L>2|6^PY!?xl(9Rb-RRwBq=%a7E*VuFDp`xL z71H}SmFI9UQ&0rf;axz55hE~4m_B7PBGpjV$;*I))w1g+(Hlw&d;_20xQ#opmvP(X^m^^P`8sqxr zlTU7$eZOJbl<&_C9gFbau&_y%%bmBiN#VD~dzp1Qv6;sNqPiP`eS2D!ibVlj-HM1+ z9fRfb*;mY(uDHFe{5H_Iy1Zj3u-NQskHNuFd3I$^eU~(6178Encdg76UDEf?_cyTY zzKv0J_iTF=;Za3m#D#%CDi;?!E&LW_!1Nsxk}QI6GX&M(Ad?Q3MNXU6W-2&fPZ}9) zmM$Y&n`j#`Z^pFcLjT3o1td_oi7@7PE4`e|raqX*S3)%O!S6O!#H?_9@B8SvxPO}j zL^XcB8p^_gv2N8lOWfkN+2}^%-~d%BK&3u^R-GEJnFH_S@F;e2!=%FKta;R($2X+b z1M!uSQH5;CgEt5CEG^!Juf*0;%<|j6KmEJ6Z6DT>m4C*L=a2`eM7jtlrl4Z;e`cheSk7_3%0?k|+sk4=Akfy7_wgl6~u`Ti48(whk1OTGv0yxHyQV+vMn3TAkl? zW7di2j!^2Eldo=Bp8I^8qoX?^0(t8#N?qnhhKO!joSA&69RQWqpzsQkA3)vjfyC5` z6|hFpSpf1OW(@hbLjk3c=$+a!krqWji+eWTDM0=3#jC#Q)H>;VNp-Zp#Jca9kG`@N zANB+BhMEfs@u{`02Cl57ZUCgfD!2=f0O~`95*^*PW|36DZiD`K(g3`VKbq3ogVm)- zA_b(&NyI&Z8snPbk>1vFk&{DK{l)))h$B)!P79@UD8~ri zG~aXsI}H2TB=y@Rw-_a)8Y~b4zIW=V*q8@xx#!PO<;t-AfT%qP&AUM3Rh1?A1(XXb zX6c6i-HNw#Ns%nEW7-P?2#FL+04SdE-N?Sc0()oN&gV+eo_ zZPFizY3cFW>-MTv;HbHP%x`ov*rkDJ@|tr9~xiJSW zfZ+hN*o!O>=v)^rtGtiQw*Lsk75X1QjuHxd0!L8#v&y%9?$*cZos67UF0|i}=R;sY zM}G(aj~}0Q!?qB#v#9cVAcWN8^Y%9F5;%Sv89}fY#zI^;FdBX^8D&Cn2Z(}qw7v!z z2*X!|gcSsxmzr%U6G(sxTPK>8+%sjy%zpau9A3;^Ks5mW~fvn0U&y=*w{G_Nw91h3|YU+|V%u}(%L`lcsgyi$*UVLl|-FRkL=4j~Iqb6i)ZuSOft6`FlIyF=SXa=C+ zMJTKC{Hpxt9@1^TltEE3uHe-nm&Zoe)fZ|zR#2vlgOpZ7{|g9&LDPL|{Bg2{FB#!* z1<_QBEp38>QkqA9b+SLwgAnEmb@lM5YLu|3SwNYSH+-e**RGlJA=|uW_^zHLeOUwx zJO>&1>HFtX|2~RNL&zP@nD(Hv6EV_NMf#FQ@il3aIi(d7k<^VZp{WtHqo6T`qR}Z+ zQBVI7>hmZ5v>i30j9r)~mrLMnyK>LP#6Xh|V(IQ2G7SvKETq5^b&)mr8D4qY;I}Xq zib`o#TTC_$L?)@EuzkzyAskS*-hOg^C2VP+ZLZ?1%*eEp)t0)X`qxNcf%IC7ch7Co>C)bOrWf?<$8?DkXAhPwFw{q zk+A~33N0g{0~lvQ-(v!z3-2$Xk%LPlW#11^oV82^wguh^q}-v2U*0;^ehM{A^G1>A z>MSUnOXW_ch7xBPyAwKb0!LKm5SDdzs!*?Ak>`MRA{ zRLmpGwzyU*6Vr6Jiat%K*$@O!x2dZ7Fp$=&d53tDsTa~y)uvT0r={vtIMOeOIbMHM+{84l25 z85e?XataEmWHbz*Y)X;p3}xvgLaj%!j z{#3q z6}p7_sl4un8v)RK0K*BLcc8M^F8-#bXFg&jDKHM@7fWdqaQ2bUg$O3v z_3(H~6HhmgHGNq7!>3QS62rH3=||reYrYJfEMeY#e0(UKrocQSdRXg2$}1>a9*061 z5)Ue&XpL3?xKpkuo17Jl{upZQvpD=KSaCs)a@1RVhc3|9qguW_2{(YF*yrX(FRl`> zpOKIPwhY;dw#TTyk1GBLkRXR75Z#9v&;@PQAN#lDejMQFXFmmdZ(Cig>R8u_aAe0rZ4NT?c=@ z!6ww8>>1IsGr-Xk9&0~COr*;sAhQ85!1)A)G^A^FbS~6aQhwgu`(P+pNd*3_8&72a z7*e2r508bgjp00DO1cEJJ&;-!Z>}Scm>b<7VbL_k zEb9o%#B-+^y0Q|Gga0v&w~2`}66SpL(+Oia-Uz@>2;Wsr?Sz*qXjs%UD8&X=AKcYh zj}z!i0>uctmt8qBnReGKs8zVY6&=avMo6+@e~@6zG`~oHHTJR!G@>NINrio}tfJSb zrY&tFwf&Xksi*POG9v(IIDhK#KpJVQ{wriRMs=$eJcSkK=5mk)YAX$;Hqzg+Sa+xl z!pkggMWVvN;5TY@uLKJ{WtJ~@zdAiX>vR&)sexp`b5VgSZ|(I+`icG@GipM{#S;=~ zEH;vWFoVtsHXLOeFimJgC;6wy9c7E0fF#;TzMT#U0wW_yl+=;qeOet&J!IsHL`xW) z>1+tpwe-|foAIf4Vij1_jIMg*IBamw)woGp3@!lXl;ppO6=p=H`V4&ubTG8jf4z8* zf;~N-OBKzw`1=dcka8!KiFmKR49$cUa_KF21%=w5at5X^p_hjoFxHnZ-*?q`$jye| zmvMnQbQ!S#AK6Mc$cmST8-!Q>kI!9`R6zOWE)~$k_CK>Xd^^2bD}*`3Gbhy8O7NG5 zhP3D-gI{q4{ccG}rll3PG?z>26^vLu;1gjSj$~@R19WSb=28*W-MQ!yh>Qg(dI7lj zi+Kd~gc~#d<3Db()%c4Vy^sl#7GDW#*Z;eRb@Z?6h&h{mONb&mH6+C!JN<5_rL8D( zGBhw)6k!}2pf3I`m?AYi^~JhLs?_0rIOCGaNs5|)rkMyo-VYAPGRYe^@@zuo4(t`( z0VTnYO{fa2o2_rXb?fTXZ#6w>!(E7WBsLb)W`H;Klqs;RPVMSY_zHroHMhErAR{5n zzU*Q(>5LBGpfjBB#W!;sn|pq?Ne)f{lNeY|CL8{X%$0Zj*IHYY5S>jsc^Np$Llfg; zoD&4r^;0_oKZtY`>)$QD#RIg-&8 zU(RI(wZmsDX15Tn%i)Sm)dA6PT?upjd2T8MwA#-*kC!SNsA)%|HKldcwH$wFP(usbh0S+~h14bd1%j{1;T1GcWwscujFvxfI$?=(xSlDEEU zX!ruKb59@N0NQSZ!GL<>6M%cJt_tqzi=qOi;T|;c$;iq=Hw>^_(baSKy8F=<|vDoTD=dt zwWJP9P_2-1-5T3YBwpR63LzSm-}DOK%}pLN4^A~riQk*F_3^E{&(nMge$RhN1VtfN zi>8(1q~BJX$L>4bhm?P*v>}O(-m#uTC3o(O+Ler)Yw9lk%6Fmjm%>FWW6Rfcot^L;7Y(dJl1rI&is zl)A1jrKRLpT(s!x!6LwP6GrOc>EdF*J(I3_Rxy~!yrQD*-NA%j48o6v{#ZU@^@o9$ z`@#ygJDqZa6h@x=KpK8PWT3W4mqAjraxb7Ay9&Vu8+$Eb2)-m{79xlJKX?5x?_%~r z;LSYqB_iQrEziKF%I$DJ8P?o9(2mbwnJ_tlX znfnS@_U-~E8}b@+BSKk={*(rq`;M8t>zoYqLWRy$nR|;QL>x?nui|OnByDC&;@AFcS&9-JNxN_|;n3^V81* zHYjJIx5yb-gPVLyTMu-6jW@5hpF|GqGc~{@fJdCB@~B)h2o)@XDn@nVVVh0&`qlB~ zZf@5{l5Nuk&?uCg6IljFKLkq{I22?K&FbLv&VnFvQ+V3$J=?}dz#KRPB?VBbdQm*Y zmTH1DAb1J(E5QfCeMI^>D94+o_~d9O1>_qqR%_M!;cq#d5y*a7TUWW&crHF0*PYf8 zrGIF2s23oLiPO5D;?|pG>ESUte17LS{Jea*LxC?Y5${-6aS|9ykOPjLwz)=*+UJ!n zsxa}$b~(gqdOUm5+#C{i+}G9hF8~sOs(|>SQ_;bJ4#SCIh(xoE68&vzU+)>!-3%-FvbBLAvOV> zuRunub=3p%~`zEEMuHLNstr<7KZ5*Idj12{onci$D&3qgnf@Oo;(D=gz zULRo$pl-15r>=9#zw8)r3hovG2!XBuTr?tpm`9Rh9a;wg_AKi13;DD!IXx>Y`9?MI zzZJcPgNhG9Z;x1RbO-MTtO{_0Q!#=pi!WSz}4$^ z3k`yXK;j3&W|i~r-RKqN2X)Eo-wvie;;H98#`^; zzR!fbXq5ifXSh~ze`3$efTq3sR&?1Iav#1YWNKK?V&GVCYLuA&biJvYkOWBk1sFH- zKz)yX9Nh@_<(6$zzSkkhJK+R~+x-c^ZV?o&^SxSdK#1&Qy4M~~@WI#m%E4=aDQO0R z10) zhzop|2~D_Huk;K0ho1iI3b~7 Wk@h|^r)oC&g_((^aVE#*=)VDw@NX;t literal 0 HcmV?d00001 diff --git a/map.json b/map.json new file mode 100644 index 0000000..1dc29f2 --- /dev/null +++ b/map.json @@ -0,0 +1,78 @@ +{ + "nodes": { + "zombie dungeon": [116, -29, -32, true], + "Rail Factory": [30, 66, -14, true, {"offset": [1, -1]}], + "di'Luna's original boat": [26, 64, -36, true], + "original mine entrance": [16, 63, -13, true, {"offset": [-103, -10]}], + "Ky's houseboat": [-25, 63, -30, true], + "path origin": [16, 63, -18, false], + "factory turn": [28, 64, -19, false], + "b-side di'Luna's underground base": [44, 62, -51, false], + "di'Luna's underground base": [52, 58, -51, true], + "path beside supersmelter": [41, 63, -92, false], + "supersmelter": [61, 59, -92, true], + "back entrance to Battle of Day 194 Monument": [7, 62, -142, false], + "Battle of Day 194 Monument": [-4, 62, -133, true], + "front entrance to Battle of Day 194 Monument": [-17, 62, -123, false], + "non-dock to Battle of Day 194 Monument": [-12, 62, -110, false], + "tiny waterside farm": [-43, 62, -99, true], + "library path split": [-87, 65, -91, false], + "Ky's iron vein mine": [-75, 63, -64, true], + "Bookcase Library": [-97, 65, -91, true], + "first path split": [22, 64, -17, false], + "Birdie Bridge path split": [-14, 69, 17, false], + "original supersmelter": [-11, 66, 37, true, {"offset": [-90, 0]}], + "sugarcane farm": [14, 68, 8, true], + "iron farm": [21, 68, 47, true], + "mob farm (AFK spot)": [62, 183, 43, "mob farm"], + "Tangent's farm area": [-90, 64, 18, true], + "Tangent's iron vein mine (surface)": [-88, 69, 88, "Tangent's iron vein mine"], + "Tangent's original chicken coop": [104, 70, 236, true], + "coop path deadend": [52, 63, 123, false], + "east rail origin": [40, 66, -22, true], + "east rail deadend": [1707, 66, 31, true], + "Ky's 2nd iron vein mine": [-102, -23, -43, true], + "nether portal": [-131, 65, -35, true], + "Ky's 3rd iron vein mine": [-160, 70, -160, true], + "Staircase of Moria": [-33, 65, 64, true, {"offset": [-7, 2]}], + "Slimeless Barrens": [-304, -58, 64, true], + "end of original mineshaft": [15, -61, 112, "bottom of original mine"], + "trial chamber entrance": [-15, 63, 4, true, {"offset": [-133, -18]}], + "trial chamber (top)": [70, -24, 4, "trial chamber", {"offset": [40, -1]}], + "ruined nether portal": [867, 66, 2, true], + "east rail ice point": [1067, 64, -1, true], + "east rail mountain point": [1651, 66, -1, false] + }, + "edges": [ + ["original mine entrance", "path origin", ""], + ["path origin", "first path split", ""], + ["first path split", "factory turn", ""], + ["factory turn", "Rail Factory", ""], + ["factory turn", "b-side di'Luna's underground base"], + ["b-side di'Luna's underground base", "di'Luna's underground base"], + ["b-side di'Luna's underground base", "path beside supersmelter"], + ["path beside supersmelter", "supersmelter"], + ["path beside supersmelter", "back entrance to Battle of Day 194 Monument"], + ["front entrance to Battle of Day 194 Monument", "back entrance to Battle of Day 194 Monument", null, {"offset": [-10, -2]}], + ["front entrance to Battle of Day 194 Monument", "non-dock to Battle of Day 194 Monument", ""], + ["front entrance to Battle of Day 194 Monument", "tiny waterside farm", null, {"offset": [-10, 0]}], + ["tiny waterside farm", "library path split"], + ["library path split", "Ky's iron vein mine"], + ["library path split", "Bookcase Library"], + ["first path split", "Birdie Bridge path split", true, {"offset": [-16, 3]}], + ["original supersmelter", "Birdie Bridge path split"], + ["Tangent's farm area", "Birdie Bridge path split"], + ["Tangent's farm area", "Tangent's iron vein mine (surface)"], + ["Tangent's original chicken coop", "coop path deadend"], + ["east rail origin", "east rail mountain point", true, {"offset": [0, -12]}], + ["east rail mountain point", "east rail deadend"], + ["Staircase of Moria", "Slimeless Barrens"], + ["original mine entrance", "end of original mineshaft", true, {"offset": [1, 50]}], + ["Ky's iron vein mine", "Ky's 2nd iron vein mine"], + ["trial chamber entrance", "trial chamber (top)", true, {"offset": [40, -1]}] + ], + "areas": { + "spawn village": [44, 67, 55, true, {"radius": 54, "offset": [25, 50]}], + "Tangent's farm area": [-90, 64, 18, null, {"radius": 22}] + } +} diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..6ec5e02 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env zsh +source ~/.zshrc # why is this having to be done manually? +cd "$(dirname "$0")" + +love src diff --git a/src/lib/dkjson.lua b/src/lib/dkjson.lua new file mode 100644 index 0000000..3bfbec2 --- /dev/null +++ b/src/lib/dkjson.lua @@ -0,0 +1,752 @@ +-- Module options: +local always_use_lpeg = false +local register_global_module_table = false +local global_module_name = 'json' + +--[==[ + +David Kolf's JSON module for Lua 5.1 - 5.4 + +Version 2.8 + + +For the documentation see the corresponding readme.txt or visit +. + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + + +Copyright (C) 2010-2024 David Heiko Kolf + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--]==] + +-- global dependencies: +local pairs, type, tostring, tonumber, getmetatable, setmetatable = + pairs, type, tostring, tonumber, getmetatable, setmetatable +local error, require, pcall, select = error, require, pcall, select +local floor, huge = math.floor, math.huge +local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = + string.rep, string.gsub, string.sub, string.byte, string.char, + string.find, string.len, string.format +local strmatch = string.match +local concat = table.concat + +local json = { version = "dkjson 2.8" } + +local jsonlpeg = {} + +if register_global_module_table then + if always_use_lpeg then + _G[global_module_name] = jsonlpeg + else + _G[global_module_name] = json + end +end + +local _ENV = nil -- blocking globals in Lua 5.2 and later + +pcall (function() + -- Enable access to blocked metatables. + -- Don't worry, this module doesn't change anything in them. + local debmeta = require "debug".getmetatable + if debmeta then getmetatable = debmeta end +end) + +json.null = setmetatable ({}, { + __tojson = function () return "null" end +}) + +local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end + else + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 + end + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max +end + +local escapecodes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" +} + +local function escapeutf8 (uchar) + local value = escapecodes[uchar] + if value then + return value + end + local a, b, c, d = strbyte (uchar, 1, 4) + a, b, c, d = a or 0, b or 0, c or 0, d or 0 + if a <= 0x7f then + value = a + elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then + value = (a - 0xc0) * 0x40 + b - 0x80 + elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then + value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 + elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then + value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 + else + return "" + end + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) + else + return "" + end +end + +local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str + end +end + +local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + value = fsub (value, "\220\143", escapeutf8) + value = fsub (value, "\225\158[\180\181]", escapeutf8) + value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) + value = fsub (value, "\226\129[\160-\175]", escapeutf8) + value = fsub (value, "\239\187\191", escapeutf8) + value = fsub (value, "\239\191[\176-\191]", escapeutf8) + end + return "\"" .. value .. "\"" +end +json.quotestring = quotestring + +local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + if i then + return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) + else + return str + end +end + +-- locale independent num2str and str2num functions +local decpoint, numfilter + +local function updatedecpoint () + decpoint = strmatch(tostring(0.5), "([^05+])") + -- build a filter that can be used to remove group separators + numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" +end + +updatedecpoint() + +local function num2str (num) + return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") +end + +local function str2num (str) + local num = tonumber(replace(str, ".", decpoint)) + if not num then + updatedecpoint() + num = tonumber(replace(str, ".", decpoint)) + end + return num +end + +local function addnewline2 (level, buffer, buflen) + buffer[buflen+1] = "\n" + buffer[buflen+2] = strrep (" ", level) + buflen = buflen + 2 + return buflen +end + +function json.addnewline (state) + if state.indent then + state.bufferlen = addnewline2 (state.level or 0, + state.buffer, state.bufferlen or #(state.buffer)) + end +end + +local encode2 -- forward declaration + +local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) + local kt = type (key) + if kt ~= 'string' and kt ~= 'number' then + return nil, "type '" .. kt .. "' is not supported as a key by JSON." + end + if prev then + buflen = buflen + 1 + buffer[buflen] = "," + end + if indent then + buflen = addnewline2 (level, buffer, buflen) + end + -- When Lua is compiled with LUA_NOCVTN2S this will fail when + -- numbers are mixed into the keys of the table. JSON keys are always + -- strings, so this would be an implicit conversion too and the failure + -- is intentional. + buffer[buflen+1] = quotestring (key) + buffer[buflen+2] = ":" + return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) +end + +local function appendcustom(res, buffer, state) + local buflen = state.bufferlen + if type (res) == 'string' then + buflen = buflen + 1 + buffer[buflen] = res + end + return buflen +end + +local function exception(reason, value, state, buffer, buflen, defaultmessage) + defaultmessage = defaultmessage or reason + local handler = state.exception + if not handler then + return nil, defaultmessage + else + state.bufferlen = buflen + local ret, msg = handler (reason, value, state, defaultmessage) + if not ret then return nil, msg or defaultmessage end + return appendcustom(ret, buffer, state) + end +end + +function json.encodeexception(reason, value, state, defaultmessage) + return quotestring("<" .. defaultmessage .. ">") +end + +encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) + local valtype = type (value) + local valmeta = getmetatable (value) + valmeta = type (valmeta) == 'table' and valmeta -- only tables + local valtojson = valmeta and valmeta.__tojson + if valtojson then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + state.bufferlen = buflen + local ret, msg = valtojson (value, state) + if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end + tables[value] = nil + buflen = appendcustom(ret, buffer, state) + elseif value == nil then + buflen = buflen + 1 + buffer[buflen] = "null" + elseif valtype == 'number' then + local s + if value ~= value or value >= huge or -value >= huge then + -- This is the behaviour of the original JSON implementation. + s = "null" + else + s = num2str (value) + end + buflen = buflen + 1 + buffer[buflen] = s + elseif valtype == 'boolean' then + buflen = buflen + 1 + buffer[buflen] = value and "true" or "false" + elseif valtype == 'string' then + buflen = buflen + 1 + buffer[buflen] = quotestring (value) + elseif valtype == 'table' then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + level = level + 1 + local isa, n = isarray (value) + if n == 0 and valmeta and valmeta.__jsontype == 'object' then + isa = false + end + local msg + if isa then -- JSON array + buflen = buflen + 1 + buffer[buflen] = "[" + for i = 1, n do + buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + if i < n then + buflen = buflen + 1 + buffer[buflen] = "," + end + end + buflen = buflen + 1 + buffer[buflen] = "]" + else -- JSON object + local prev = false + buflen = buflen + 1 + buffer[buflen] = "{" + local order = valmeta and valmeta.__jsonorder or globalorder + if order then + local used = {} + n = #order + for i = 1, n do + local k = order[i] + local v = value[k] + if v ~= nil then + used[k] = true + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + for k,v in pairs (value) do + if not used[k] then + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + else -- unordered + for k,v in pairs (value) do + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + if indent then + buflen = addnewline2 (level - 1, buffer, buflen) + end + buflen = buflen + 1 + buffer[buflen] = "}" + end + tables[value] = nil + else + return exception ('unsupported type', value, state, buffer, buflen, + "type '" .. valtype .. "' is not supported by JSON.") + end + return buflen +end + +function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) + end +end + +local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end + end + return strformat ("line %d, column %d", line, where - linepos) +end + +local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) +end + +local function scanwhite (str, pos) + while true do + pos = strfind (str, "%S", pos) + if not pos then return nil end + local sub2 = strsub (str, pos, pos + 1) + if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then + -- UTF-8 Byte Order Mark + pos = pos + 3 + elseif sub2 == "//" then + pos = strfind (str, "[\n\r]", pos + 2) + if not pos then return nil end + elseif sub2 == "/*" then + pos = strfind (str, "*/", pos + 2) + if not pos then return nil end + pos = pos + 2 + else + return pos + end + end +end + +local escapechars = { + ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", + ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" +} + +local function unichar (value) + if value < 0 then + return nil + elseif value <= 0x007f then + return strchar (value) + elseif value <= 0x07ff then + return strchar (0xc0 + floor(value/0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0xffff then + return strchar (0xe0 + floor(value/0x1000), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0x10ffff then + return strchar (0xf0 + floor(value/0x40000), + 0x80 + (floor(value/0x1000) % 0x40), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + else + return nil + end +end + +local function scanstring (str, pos) + local lastpos = pos + 1 + local buffer, n = {}, 0 + while true do + local nextpos = strfind (str, "[\"\\]", lastpos) + if not nextpos then + return unterminated (str, "string", pos) + end + if nextpos > lastpos then + n = n + 1 + buffer[n] = strsub (str, lastpos, nextpos - 1) + end + if strsub (str, nextpos, nextpos) == "\"" then + lastpos = nextpos + 1 + break + else + local escchar = strsub (str, nextpos + 1, nextpos + 1) + local value + if escchar == "u" then + value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) + if value then + local value2 + if 0xD800 <= value and value <= 0xDBff then + -- we have the high surrogate of UTF-16. Check if there is a + -- low surrogate escaped nearby to combine them. + if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then + value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) + if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then + value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 + else + value2 = nil -- in case it was out of range for a low surrogate + end + end + end + value = value and unichar (value) + if value then + if value2 then + lastpos = nextpos + 12 + else + lastpos = nextpos + 6 + end + end + end + end + if not value then + value = escapechars[escchar] or escchar + lastpos = nextpos + 2 + end + n = n + 1 + buffer[n] = value + end + end + if n == 1 then + return buffer[1], lastpos + elseif n > 1 then + return concat (buffer), lastpos + else + return "", lastpos + end +end + +local scanvalue -- forward declaration + +local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) + local tbl, n = {}, 0 + local pos = startpos + 1 + if what == 'object' then + setmetatable (tbl, objectmeta) + else + setmetatable (tbl, arraymeta) + end + while true do + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + local char = strsub (str, pos, pos) + if char == closechar then + return tbl, pos + 1 + end + local val1, err + val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + if char == ":" then + if val1 == nil then + return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" + end + pos = scanwhite (str, pos + 1) + if not pos then return unterminated (str, what, startpos) end + local val2 + val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + tbl[val1] = val2 + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + else + n = n + 1 + tbl[n] = val1 + end + if char == "," then + pos = pos + 1 + end + end +end + +scanvalue = function (str, pos, nullval, objectmeta, arraymeta) + pos = pos or 1 + pos = scanwhite (str, pos) + if not pos then + return nil, strlen (str) + 1, "no valid JSON value (reached the end)" + end + local char = strsub (str, pos, pos) + if char == "{" then + return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) + elseif char == "[" then + return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) + elseif char == "\"" then + return scanstring (str, pos) + else + local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) + if pstart then + local number = str2num (strsub (str, pstart, pend)) + if number then + return number, pend + 1 + end + end + pstart, pend = strfind (str, "^%a%w*", pos) + if pstart then + local name = strsub (str, pstart, pend) + if name == "true" then + return true, pend + 1 + elseif name == "false" then + return false, pend + 1 + elseif name == "null" then + return nullval, pend + 1 + end + end + return nil, pos, "no valid JSON value at " .. loc (str, pos) + end +end + +local function optionalmetatables(...) + if select("#", ...) > 0 then + return ... + else + return {__jsontype = 'object'}, {__jsontype = 'array'} + end +end + +function json.decode (str, pos, nullval, ...) + local objectmeta, arraymeta = optionalmetatables(...) + return scanvalue (str, pos, nullval, objectmeta, arraymeta) +end + +function json.use_lpeg () + local g = require ("lpeg") + + if type(g.version) == 'function' and g.version() == "0.11" then + error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" + end + + local pegmatch = g.match + local P, S, R = g.P, g.S, g.R + + local function ErrorCall (str, pos, msg, state) + if not state.msg then + state.msg = msg .. " at " .. loc (str, pos) + state.pos = pos + end + return false + end + + local function Err (msg) + return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) + end + + local function ErrorUnterminatedCall (str, pos, what, state) + return ErrorCall (str, pos - 1, "unterminated " .. what, state) + end + + local SingleLineComment = P"//" * (1 - S"\n\r")^0 + local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" + local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 + + local function ErrUnterminated (what) + return g.Cmt (g.Cc (what) * g.Carg (2), ErrorUnterminatedCall) + end + + local PlainChar = 1 - S"\"\\\n\r" + local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars + local HexDigit = R("09", "af", "AF") + local function UTF16Surrogate (match, pos, high, low) + high, low = tonumber (high, 16), tonumber (low, 16) + if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then + return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) + else + return false + end + end + local function UTF16BMP (hex) + return unichar (tonumber (hex, 16)) + end + local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) + local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP + local Char = UnicodeEscape + EscapeSequence + PlainChar + local String = P"\"" * (g.Cs (Char ^ 0) * P"\"" + ErrUnterminated "string") + local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) + local Fractal = P"." * R"09"^0 + local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 + local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num + local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) + local SimpleValue = Number + String + Constant + local ArrayContent, ObjectContent + + -- The functions parsearray and parseobject parse only a single value/pair + -- at a time and store them directly to avoid hitting the LPeg limits. + local function parsearray (str, pos, nullval, state) + local obj, cont + local start = pos + local npos + local t, nt = {}, 0 + repeat + obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) + if cont == 'end' then + return ErrorUnterminatedCall (str, start, "array", state) + end + pos = npos + if cont == 'cont' or cont == 'last' then + nt = nt + 1 + t[nt] = obj + end + until cont ~= 'cont' + return pos, setmetatable (t, state.arraymeta) + end + + local function parseobject (str, pos, nullval, state) + local obj, key, cont + local start = pos + local npos + local t = {} + repeat + key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) + if cont == 'end' then + return ErrorUnterminatedCall (str, start, "object", state) + end + pos = npos + if cont == 'cont' or cont == 'last' then + t[key] = obj + end + until cont ~= 'cont' + return pos, setmetatable (t, state.objectmeta) + end + + local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) + local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) + local Value = Space * (Array + Object + SimpleValue) + local ExpectedValue = Value + Space * Err "value expected" + local ExpectedKey = String + Err "key expected" + local End = P(-1) * g.Cc'end' + local ErrInvalid = Err "invalid JSON" + ArrayContent = (Value * Space * (P"," * g.Cc'cont' + P"]" * g.Cc'last'+ End + ErrInvalid) + g.Cc(nil) * (P"]" * g.Cc'empty' + End + ErrInvalid)) * g.Cp() + local Pair = g.Cg (Space * ExpectedKey * Space * (P":" + Err "colon expected") * ExpectedValue) + ObjectContent = (g.Cc(nil) * g.Cc(nil) * P"}" * g.Cc'empty' + End + (Pair * Space * (P"," * g.Cc'cont' + P"}" * g.Cc'last' + End + ErrInvalid) + ErrInvalid)) * g.Cp() + local DecodeValue = ExpectedValue * g.Cp () + + jsonlpeg.version = json.version + jsonlpeg.encode = json.encode + jsonlpeg.null = json.null + jsonlpeg.quotestring = json.quotestring + jsonlpeg.addnewline = json.addnewline + jsonlpeg.encodeexception = json.encodeexception + jsonlpeg.using_lpeg = true + + function jsonlpeg.decode (str, pos, nullval, ...) + local state = {} + state.objectmeta, state.arraymeta = optionalmetatables(...) + local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) + if state.msg then + return nil, state.pos, state.msg + else + return obj, retpos + end + end + + -- cache result of this function: + json.use_lpeg = function () return jsonlpeg end + jsonlpeg.use_lpeg = json.use_lpeg + + return jsonlpeg +end + +if always_use_lpeg then + return json.use_lpeg() +end + +return json + diff --git a/src/main.lua b/src/main.lua new file mode 100644 index 0000000..a433404 --- /dev/null +++ b/src/main.lua @@ -0,0 +1,240 @@ +local json = require "lib.dkjson" + +love.window.setMode(1600, 900) +local screen_width, screen_height = love.graphics.getDimensions() +local node_radius = 2 +local safe_zone = 12 + +-- panning / scaling control tuning +local scale_adjust_speed = 2 +local translation_adjust_speed = 600 +local scale_fix_translation_adjustment = 300 -- this is wrong, and the wrong way to do this, but I don't know what the correct version is + + + +local nodes, edges, areas, minimums, maximums, scale, translation +local function load_map(map_file_name) + local file, text + if type(map_file_name) == "string" then + file = io.open(map_file_name, "r") + if not file then + nodes = {} + edges = {} + areas = {} + minimums = {0, 0} + maximums = {100, 100} + scale = 1 + translation = {0, 0} + return + end + text = file:read("*all") + else + file = map_file_name + file:open("r") + text = file:read("string") + end + + local map = json.decode(text) + nodes = map.nodes or {} + edges = map.edges or {} + areas = map.areas or {} + for _, area in pairs(map.areas) do + local image_file_name = (area[4] or area[4] == nil) and area[5] and area[5].image + if image_file_name then + local file = io.open(image_file_name, "rb") + local data = file:read("*all") + file:close() + local byte_data = love.data.newByteData(data) + area[5].image_object = love.graphics.newImage(byte_data) + end + end + + -- find map size + minimums = {math.huge, math.huge} -- x, z + maximums = {-math.huge, -math.huge} -- x, z + for _, node in pairs(nodes) do + if node[1] > maximums[1] and node[4] ~= false then + maximums[1] = node[1] + end + if node[1] < minimums[1] and node[4] ~= false then + minimums[1] = node[1] + end + if node[3] > maximums[2] and node[4] ~= false then + maximums[2] = node[3] + end + if node[3] < minimums[2] and node[4] ~= false then + minimums[2] = node[3] + end + end + + -- find scale factor, translate + local distance_x = maximums[1] - minimums[1] + local distance_z = maximums[2] - minimums[2] + scale = math.min((screen_width - safe_zone * 2) / distance_x, (screen_height - safe_zone * 2) / distance_z) + translation = {-minimums[1] + safe_zone / scale, -minimums[2] + safe_zone / scale} + + -- TEMP displaying scale to debug + -- scale = 1 +end +load_map("map.json") + + + +local font = love.graphics.getFont() +function love.draw() + love.graphics.scale(scale) + love.graphics.translate(unpack(translation)) + + -- draw edges below nodes + for index, edge in ipairs(edges) do + local node_a = nodes[edge[1]] + local node_b = nodes[edge[2]] + if (not node_a) or (not node_b) then + error("Edge " .. index .. " names nonexistent node(s): " .. edge[1] .. "->" .. node_a .. ", " .. edge[2] .. "->" .. node_b) + end + + if not (edge[3] == false) then + local color = edge[4] and edge[4].color or {0.33, 0.33, 0.33, 1} + love.graphics.setColor(color) + love.graphics.line(node_a[1], node_a[3], node_b[1], node_b[3]) + + local center_x = node_a[1] + (node_b[1] - node_a[1]) / 2 + local center_z = node_a[3] + (node_b[3] - node_a[3]) / 2 + local distance = math.floor(math.sqrt((node_b[3] - node_a[3])^2 + (node_b[2] - node_a[2])^2 + (node_b[1] - node_a[1])^2)) .. "m" + if type(edge[3]) == "string" then + distance = edge[3] + end + + local color = edge[4] and edge[4].color or {0.67, 0.67, 0.67, 1} + love.graphics.setColor(color) + if edge[4] and edge[4].offset then + -- these actually work better not scale-adjusted + center_x = center_x + edge[4].offset[1] + center_z = center_z + edge[4].offset[2] + end + love.graphics.print(distance, center_x, center_z - font:getHeight() / 2, 0, 1 / scale, 1 / scale) + end + end + + -- areas sit between edges and nodes + for name, area in pairs(areas) do + local color = area[5] and area[5].color or {1, 1, 1, 0.25} + love.graphics.setColor(color) + if not (area[4] == false) then + local mode = area[5] and area[5].mode or "fill" + local box = area[5] and area[5].box + local image = area[5] and area[5].image_object + + if image then + local size = area[5].size + local image_width, image_height = image:getWidth(), image:getHeight() + love.graphics.draw(image, area[1], area[3], 0, size[1] / image_width, size[2] / image_height, image_width / 2, image_height / 2) + elseif box then + love.graphics.rectangle(mode, area[1] - box[1] / 2, area[3] - box[2] / 2, box[1], box[2]) + else + local radius = area[5] and area[5].radius or node_radius + if type(radius) == "number" then + love.graphics.circle(mode, area[1], area[3], radius) + else + love.graphics.ellipse(mode, area[1], area[3], radius[1], radius[2]) + end + end + end + + if area[4] then + if type(area[4]) == "string" then + name = area[4] + end + + local text_x, text_z = area[1] + node_radius / scale, area[3] + node_radius / scale + + local difference = area[1] + font:getWidth(name) - maximums[1] + if difference > 0 then + text_x = area[1] - (node_radius + difference) / scale + end + + local difference = area[3] + font:getHeight() - maximums[2] + if difference > 0 then + text_z = area[3] - (node_radius + difference) / scale + end + + if area[5] and area[5].offset then + text_x = text_x + area[5].offset[1] / scale + text_z = text_z + area[5].offset[2] / scale + end + love.graphics.setColor(1, 1, 1, 1) + love.graphics.print(name, text_x, text_z, 0, 1 / scale, 1 / scale) + end + end + + -- draw nodes and label them + for name, node in pairs(nodes) do + local color = node[5] and node[5].color or {1, 1, 1, 1} + love.graphics.setColor(color) + + if not (node[4] == false) then + love.graphics.circle("fill", node[1], node[3], node_radius / scale) + end + + if node[4] then + if type(node[4]) == "string" then + name = node[4] + end + + local text_x, text_z = node[1] + node_radius / scale, node[3] + node_radius / scale + + local difference = node[1] + font:getWidth(name) - maximums[1] + if difference > 0 then + text_x = node[1] - (node_radius + difference) / scale + end + + local difference = node[3] + font:getHeight() - maximums[2] + if difference > 0 then + text_z = node[3] - (node_radius + difference) / scale + end + + if node[5] and node[5].offset then + text_x = text_x + node[5].offset[1] / scale + text_z = text_z + node[5].offset[2] / scale + end + love.graphics.print(name, text_x, text_z, 0, 1 / scale, 1 / scale) + end + end +end + +function love.update(dt) + if love.keyboard.isDown("w") or love.keyboard.isDown("up") then + translation[2] = translation[2] + translation_adjust_speed / scale * dt + end + if love.keyboard.isDown("s") or love.keyboard.isDown("down") then + translation[2] = translation[2] - translation_adjust_speed / scale * dt + end + if love.keyboard.isDown("a") or love.keyboard.isDown("left") then + translation[1] = translation[1] + translation_adjust_speed / scale * dt + end + if love.keyboard.isDown("d") or love.keyboard.isDown("right") then + translation[1] = translation[1] - translation_adjust_speed / scale * dt + end + if love.keyboard.isDown("=") then + scale = scale + scale * scale_adjust_speed * dt + translation[1] = translation[1] - scale_fix_translation_adjustment * scale_adjust_speed * dt + translation[2] = translation[2] - scale_fix_translation_adjustment * scale_adjust_speed * dt + end + if love.keyboard.isDown("-") then + scale = scale - scale * scale_adjust_speed * dt + translation[1] = translation[1] + scale_fix_translation_adjustment * scale_adjust_speed * dt + translation[2] = translation[2] + scale_fix_translation_adjustment * scale_adjust_speed * dt + end +end + +function love.filedropped(file) + load_map(file) +end + +function love.keypressed(key) + if key == "escape" then + love.event.quit() + elseif key == "r" then + load_map("map.json") + end +end