X | Sender | Recipient | Message |
---|---|---|---|
[pda.sender] | [pda.recipient] | [pda.message][pda.picture ? " (Photo)":""] |
Sender | -Sender's Job | -Recipient | -Message |
[customsender] | -[customjob] | -[customrecepient ? customrecepient : "NONE"] | -[custommessage] |
X | Sending Dep. | Receiving Dep. | -Message | Stamp | ID Auth. | Priority. |
---|---|---|---|---|---|---|
[rc.send_dpt] | -[rc.rec_dpt] | [rc.message] | [rc.stamp] | [rc.id_auth] | [rc.priority] |
S4mj(`%KhJc*Ii=r{P&M6r)z$4CUr0BPLKS4IZe1$4ZrfrD?Td{CRnb9`PEF{kRj$8 zth~IO?DFZsIbqvi?G `9ow^mj*qLM1Kq!%OFkb xozX>1OaHzjG%*)FYWv==xUDUT~-(CZPM9Y@?K&ss2 z*1vk#f%6y5*X%(??aA8KdJ({pLy1b4cUXk+umRRUOP75XRDSokV!E0hqWuRB(f=O% zgGj@}50wPbmS6dysx7jz@|1@6Y4HFY=tSH9d4|wLX7w4EhH1#`L^+3)XVp<%oI19w zlgO*?e}bqcj}E?@P5YZ*q$*^*!TvWu7dl+I{uO-vN?7?Pu#iH+PlaEm#+NAnE2UT5 zbB|@)j$Xw gdry^MjzMu>CF{>7;4Lxc|`^RPe?Tsvg@u!2i`3@6&(Z zxR6$#tH`7mMA<~2`cAgriJ4S-=Nw )&8_*(~3R(pjzdf^HZ@1nHnye^aVyL#J@Q97|{GVFX-#q?QWW=ZcYVG*N#z ziMH(TL0j-Pa3lHsCR$hARgB2CZJ488Q+Y3c4Q0@f{MFY+z7@;{A^=v!e3n^AStGKk zef##bsZ%ei+keQgtRpDI8cwA?LAHc4g@B~lf&vKt#(yosFSBjZ*=iWrf(qe;wo@Fs z-V$YGh&ey_h;yDO7iRZ6b?S5<`@WMs2o4&!pdeTBOBDmTK )dQ>P&l@R8q}T%cx<@-gB8@6>CD84~&KU=bwLG$lWW(36k4WmH_>b?2q zn*kHja$EX$!(Z&!9njXKNs}D?E*OL`(S|Q?p%o)^ohJx7Y#4p?(ML2)@fovc&kl1G ztOH2KnvfAm%@>F!N>5Lx-n|FVYp=bg#1|-Tir+s+ahEFYKSYBrQEagUe>#aWmtJ}) zZQHhu8XJ9KOh(5}zTI9Av4+opfOc&eEktnB76aWcNXR6k5v4(-vm=`kLfR9;B$VT! zwBsIAbUtvY`f4aB{q&F@&LV+W10 Od0J>YJz-kyb;I`c?MleHJgeKNci#;KjLJ(N>l<=y`cWYtc z8>DM>+PHjPUWG1^_vtz%Ay&ia?LRYcn`XA8S7_7QGUv~qpD<&_3~4_KcX5|eaGE}y z+n5{jUuvx6YwQ2~B*XwhPeL=TWkfzRZQ9nz2e=*u#}QfFamO7#J{5t1QLH?W0HKVC zgDsgd0cCsANgJzHt)f5u=}(@X4HGAz$9Uq2CwzP~2VgfKkx8Z}pc{aEIHc&d+ivre zfNi=9{w4t3ExN`{n>Gm&%1iDU4bt{Wqt()KIZn44T{HQ3AF`K-6R-_HCLvH$<9vg= z{$vCzbm$O#Je${u6VPLLeLmmwi~ix8H1P+XTmxjE7Sk}_bWE5q!6*BXM6cdw3L-?h zT{}Q#US7wxtpRVn=ULyA58oKpUuPuRXbTuwTI$O!C@>}AOE0|?@;x#ML5NtR&5nXW zoIlUKmu{K<5QR#hNVMTKK)BlKYPxvD2>N9Ca^dT-apOkeheE=)-u~Fvsgr6OZr}DL z6?N|s<`vsx*N(3UWY6o6*Y-3ZNW Bi>WO$@-(F4o0d0zaz}B6OG;8LqrunnxJSqH}es;T-_RL=%w~aeRtjKrp z;6c+eZ0DiNH3DowfXRn)oF=#7rc0Jd$Uv9uG!E@)1Q5|m8@Aa|aA83zy|-kBa1So2 z5WXHVu{dm$<`iv#d<>8a8qdhdTF=ZbWH=o;>&XYS&k0P^lMHcodUDEOZK&6NAib>y zeC|8n3Ck9`;e!uCHvR`KP+#;v`Po2P`Ro+>6$PGAC~G+**yYQJ1^GVtbU6(??>yVG z7 yl^#k 8=;W!*=Ln*_*wkuAmLi&hhIQf(qjQ-^j)nHoM=IiZ|Kkw zf+W!bG#`_1?b>xTaG*UA?I4eg;e%jIG!C-{v?8BVFlqE@+(v$v!XOeZe)$<1I(RsJ zx^ji^q5b4`Y&9UrcfIoK9XR0roWCVmS+pp`#g8lf%bW)Lm$_eg)niu0H6p^7yJI y}CK7W9NJ= z9b~+1>Muoj(BMJBKYX|@K&InC2o_jkNe|v5%D_sFE3dqg-h1yoEe~noth)Wy_kT-& z|GpajwU+wZfHHcUPfs$lbT7a*-v`55lz)g!4%G@mK$mNfrq_TY{ZmIgcL?wVHW1Ea zbtnMoo|ykfO#@zcje3+Zclm&00%HFw=6@1*Zl~f5bys~=L!(AI`0LlJ-?tbwY81|m z3+bYl{71d8CDYCd%DZab7A^mD!;p3Bh?M`uCT`GVpnV+>ZKXk)43H1gG3O6$)#8=7f*#AG`Fd>_8(MP!F?wq#r+_?!IemYM^%Q+6kL))yh=?8W7q~FUK(f@x}z( zF<1O`dq8@xUcE$K_wL XdB6n=7U1iS%Dt;M-|NC&qU)7c-a`99Q-%r Jf-cp3K zQRYkwJL=0w*@(D0i?dTe4v+~N%*mS#qlxH7|G`!1{-a-*d<-KJn%R0z1}Jiw6DI=q zARB2wS*9x+5CGAz0UeqUKD3{neCT>{pkX*D=wy6k#5uh)Y2tOa__`Gve`0Fl @Yd$?9sR@}#X=$m{(9jr= zXQTmT!aAA x->N&w7Ax5z}}&SdkAtNLOp|-k0&YgECiH%xM};H@TUPu|j%U>ndz3 z4ajiyCIsZW_r-f@@mGt5g9emG#ZNr(gz%S#sWqOyCxzCp{UqW1^9R!1zn&``!gt>H zsK`IF?_fv%kvjE C&nh zoldnFm9?wO8W2Q;FC!!yaQ^)Hk`+w!;h_(y2dT? B{H2d5`}o0VIU)<~pLK4M9F5)G6|Knnm`Bg5QRir8CeCgM_js)RT`9j`ox5 zrlh3MsZ*_|vqy9k3}AjwXgo=iLJNX?ciwrYM!ZOVhjxc#3vt-3?-Y7_FIwa~Ptk;C zBphtwo8{}%Wq^E4LMC5rZEb*U$Ycm*8b*#|6x`C%BBo#%cq3wE13Dgr{8brRUFF|X zs<-VB=Q%$)@8Y~XqLYEAce;hs!9yevt*%!Flg|oz4M!BL5v{bLUK4_RpM3I(7 %ou&IGEGg?0hYi9%65)kim@j7ma+waUO)87Jj)T=q!{f5)%Yauk1@-xuvl+K$O*wtg_wZkX#~BKp*+ zX2lOY%pTn^Lw+6SXc}g8f2B*^xIX0fxwzGER>MRRvRx?y1A%D24|byY0Cbo9+6-Ds zA74CbHC^}i1wp?2I!^D)HXyH@W^HJPyu2L8@#=l*S^;?!tOv-*Bm``yVW2f`c)Tnm z1O9YFxFdHfI3XVZ(#ZCiekuiIGxC|FW 7lf=6h%yf;%8|7_H9*)ZCLC~1G3iey8DU+tiJdZE*czs7+U}4L;rdW z0|-sy`E5G(okG@tMu0}c0Fw87WM&oF#+g{G-KxIY70@P=PY))&qj^BH?kkSapq~Q- z#0qGI80 j`N@P=LvY`b(v202x01&%d>Dg|h?CJ^jr}b)3zF zo2|=7x)oWW1=CDy!#1}m>FW$P K6zfP~s%T?^oim0hwGKI~r@Zrlu2g;kV8S*cDf?v5GP?n-%Te5GNpmr(&Wl!z$lu z`4#2~!`*i7lzcM!_Aho62dSXh%%I;REO*Ck_Gpf85=O@06b3W$tz5m$w|dn|K?-QJ zVc+g4j)}vHrz=KYOX;Z%mT|yr-!BtS2FfyF`=8T4t^wNu+-jysgUN^PvQg<(JD$;( zhAK`aRwfnq8YI# t)|{*Im9zlUl!D)vf0O%FTMh z@%7c#-xi?Fc2>3?V*dQ|&(rUJ|NHoqmV|a*pAYA?d?Vn1Z*~Q_M<^|6lH= T`!bv`Uch%T0q!HQ4ascb4zo|C5V|?$FX|j^vva zk3&Ji^j|*c>G^Q-yGSqOUwU!Bc$erQ0tO*ooDbKQ Sm!#6AS&i=Z; zHjgO8t2lXWKhA#}GiD53fBp52;%4B{kskUJlG=iN86aO>-3P)q$}cG?Dc6@*{mn|f zYlJVS$6*ak1A28nTw9UPfaV)M{5z%^aLt-EL-gfU|7@k+%bErZMT345p)E%6>U_Ai zB40+R $J|E5u`tDFqJ}>1*3h{2&J(9dWAI@v}kwm=Pbq~!3 z^y+-Lwj%#@!|F3A!s*tPuVcS6tzkgkfnw{myt9^7(L9XM>#1@;!&2ctPP9K_(x7 zMYC>?at@k^mJzA*;baZOkY#!;KbnZz$hcw$(el-;aXB0V$mHY2w4t0M9<~k04-)6Y z>0zXS^ykB!?((f*_f{notaxXpsevD>frypnga7{f?>lbGaugg2+@25DR^&$mc5hW8 zh8Su;kZ;$XLv;38kJ68RrTzh5cinZuXYx&-KAq;yo$F|1+|KLs;k=d~EwI*$2Kh8C zh;EqEVmJ*`{;_%{ e|tAM4TjRA}`K|V|Xp!Eka$g6>&fd_6`3F1#STn zmJWZxH{_D7^nTYGSCh{&!rqh=?)h-1qkMJA)z3X$|2JuDXDytaokZh3xP~A7So!T0 z4Hqi;0L#@dPJnuRO1i!a1keq#!x0dCCWyW(^jf}Cux*F6e{$R_WM^l4CL6>HhkStL zG>nt;;pB%3*qsk2!;S=A%QpgdyE!>82gGBIn{K)(0dD;G@xpm=4Us^=o)F-&;pb`R zzkOO>K{wqv)-ulE^WktvHQQmGJLM5?dgf!i)SYm}v~_Ex;b%f)E#GQSh?Sk!W^BOI z903}T&xc#HW)1!8U;h$mc5yzK2HK^lFz`S`!0vpwwj^H Uep6R`S 2;lqc8|Hu0$3bH}-$v&sY5r-SN(tu2|%00(O)_{n|5l1L+ zYy%$K{U18_%v}27%UZhn*qby+sfpu$@LsmN!oPPi$3*Wc 0#qiDZ3 zwRC7dq;nVj%h`)U8Wp!|*Dj>LxR#a{Q+cJ|o8>Eecb22;iL`vk=CG4%h{pYwjT`MN zef_PFo<~8KBI?$y8|~b=lP>($P;vOs$De*mh5l7UlNLNnAD6U#4gQif>*)C@H;VA? zJ^Sd^DN|@;MTKG83`Yb6u|P1k`+!A9eoC|bganX>E*e@A od&$530e?ByDsiNPgebRm-8x$tjtB@cF$pUr(ha$w2komqDB`dUIX_el z2(%x6Kb>_^siU%6J}aZ9MZYr4WAeSTThY)iNEj*k0w5pz55MZD&Faf8qoup69F-ke zSWLM~mm20V`O=1z(sU&r3JF2FE?v54K0Z+R b(eUdj&gz5MS}gmNhV(&DE>A|W0=9_$U%?+r-L>5xEmHLX3= zb8{aOW%n-%ZbK&D)rI7WgprWXIA o55!74r*B|Jb^+1RYAxEdp~94? zGWnXy%R(lhOg`3tmkb?BO(#y!yZ`^!G^waCfP|t8R?^XR7x(DpNCVmqLO`b64h58v znL{1U>Z47$ c=!l|2v#s6UjPXuyDS>= zWoYDE>qcTA>K=GV4JhOj;q2gzS2B#qr;$)eN4`_p|NB3mYUJCsvr0=(OGyy~Lsz@) zqQRO&9O-EW`?JkpsR5aMCyqB#!?8L#+K9gb8~|P^2nU-FBm}Ced(z&6Us7Uv2G#F4 zMyMB#Cd7mOY)b=CAN6mf;$Wi)g9v6MkjeMtz{`l%ekjuAxKm9SihMmlKK7kzKn);T zv-E}Zj5O7U78E25?%HJX&3Swd&Aff4NSEVoG+{&I2{G_s)E+))*`*>Mb=<&K177s) z9|`iwL_&89@&WMKCInCp b~tdLhF08g zK6ROQo(L~|?rGr{7IhO2HX?`R{8tAT(oKWU7hyX%8U HCUzrke} z>?dB1_2KF>M7f`qUh6wmm?_GC_V3r6-lrk(pl3Yt5IuIc+U?|aQ9t8)!;Gnp+X!b6 z5Q=<|8}=paO!AYB2SFUax|2f rIPGi z;kyN1q3PdZ sk#KE|^ PO;^$tPjfq{)HZu8jti!Q_Kq z=0>O=cI1f!YzcT^UzN~kAQi|r?y6!XwTeK(RhtJZ;cbF^NJl=VB*W2wn1jRo8Vr9c z@O+&WSo68jfRT`IL_(+J!yKTP-*Ye-jk5HGxh7=I7m5aq6)Xve5iM)9?_TJmZN8P`hV6Opqr;Z( ^2whnm-w3!O;DAd; zD&6ij5w6*?Rgev&!_ Z!($P3mPhCz@wxu->Q!xVlGeTaDAICQnjx-=h)xJZDm}rK> zRE$bS5 Tx)QEdfEM?~W)GB*Rn-<4Dbk03Sd;%AtImgmE}Q{CvP~IL1ww z?5Uy%`(uT1oc`EWvBnd*z5ABL2^a^_6=QDpOm>`ranQkiyJ+R7%j^qA5QB$aE_^}2 zk4BUR*#X0rmnB3yox}SK^7EDaXs2_y-S@5x%5Qo1y@Y6|bJ*+qXA~Fr(I-U(RaINH za9jh%5rBg3XGOLB>K$7G;(~zdTY?J?Gf2;9pDEI}OCNoa%Okzv$g!Ywv-TOA4f691 z(o+@rNUt)G&*{f?@{QWzbjF5L!3Fvmq^BtI@z#55MtV?rPCucOZ`2N_GrYckJr4f- zF*=@Qz4+KIQtJ;;r>ABFrQ2#ikj@R{@_I6cf>f9C{N65eiaV%9qRQ$ruW}xPOoj zgoz}~2pupHeQQ}+0z1T+Oq1V!$MDsfUAmsU+^!jhDf#RW=O6PT6FGM2`iD2nMt;9# z%M#cj&SdIsvYOZ~T~A(a*9>0YzigWU_g~xB)V+A)K+Cx7tYpMwF@jqy8fm!IB9Vq$ zEgEUq-G4?-0sW=qBFY ezrdoKZanWIz^V0wE1T zQl*Ad4TKmWk|a!)WF|Y5A4w7>Nk>j3IXHd-h$IOkegAta*U%%1fA_LiiAshC{ zF31FuZTsrOfV|#4E(=Pdhls|`{x4B42#SnHy7VNfR42b36VM6KoRTpVIQgNSK8#S{ zglJC57z(0!|6O{P(8OwGbb9%ZP0M`n@Z*ALo9Kv$CmwI1{vB1jE-5RSHXb@a&8hwl zRVnwu!>QEu7e5bfNRe@OdIl{jI7<*;>w=9m6bKb6sm$t{X+opFQ2d~w{-|bk%`}0L zh615NC6!rSGfn8_{R<7)*dVl%8CGvxN>vKqIuv)M%#?IW%MNS@aik+3 r%$1(|@ja@I (Lr zcB)Hkr-iNHwi{2>m2YPvKf$4Vx7}D`T44DE-GhZ(;r!PCX9c(2c&zW=N&_ R6$A2E!0D ^dhj@F(>_iv>E0g!3$ z?j6+k|NWPUC(K)@W?phiKhDw9)VG>_Wck8-vQ~$p>U) zWN3cRqDuqf43tMbJs< ztNVuryzkMylu+<&P#!cGlaB`!NCVkGFm{Nuw&QYL3cClj2N{sH*X=x@jFy%b!>}2S z8%;=t0k`r`Mp(YDxy3MShEpyN?zTgu?;qOe_Obg6H6R0|k_Qza-KV{GQfYlIm1Wh^ z&}|Aa{dqiby-C;o0;~Zae 4m2F?y2>DH$?d1>CPv|DE) zpGSiz)9gULetn#iMFY(zPm0m7Q67(mQ3lQq`t^Zc-@lax1Z48Dc1y@SD9E?u)DGbz zF6T1|^@xZDlu3qgbBwpQ%Re&0EwPp_kqlv9j4vS4_iy(mL?)lC1(zSHq^mmi6#k{D zUA3}Y&hS;7?oEhBKqD=v?>aZ@MrT&(X5B92tf#>BfTFyCe0|rk%|yOYx>>gaIh!eX zJ)Dsi)OVemb)#debhB<3a&TO5KV7<)_iy(u7zomWGv*LIHj_@&9~b01TZzlW)00(0 zsPBTE*m04z^<+4)<05VA$#7!FMcUSr5zYH|@-A2=6Nf=MyD~vA(}2K7e%k;u@>l zJ!GO8kxqW2!f;||vTc&A$nRhwU;d$ue)Jjx`A+OCw#^dTX9T *b{=)(9u|u&ob(2_G;+?1eEt~z(xacLRrVhZ#>)*Pu{x#8wEdH zDC_um#@{XR iP(LkI~*75Nh54Xhg`u-V4dpDv5TN{u` if82=1=IZPB~$J>Na& z`!PK|y &}t1%~-!KyZ@O zV{18;){x95S<&rXUj=>xy;ee|(Vz;Qp;1=bkU9+tN$T$pIg2MGUkOooJMGtzOKPa= z^}dtG-4vyx%n|2P8ct|tLs69)>@dWXAU;W*@{B1-7upb-;dwmO%Ftjo?1E+s6UVJG z=KU-w$$4DA?q@3YGUQ!f`C;5KK^dj-fYy^K&wcjLQ>0>R$LJX$`JM(Ez3plR0IvWA zX$fuL?0?37zC=5%FHX*WD&B-43r1+Icxb`mL~lW?`W6~}U+5Zm6$?P$)bm;Y>ePs! z7L~nO3@wq%H^pZriX}}O2r;MW9UzG!;lM+qr6moa4PG90;%gQN8VyX%T*zEl7<1;` zf6leZ%{;vyKYqM 52aB z+czxq_UNSqD5d+ZmgWKb-u9;}@IPmm IQyZjxe+!OIgB1#uQ#&ajFXEne$oaiuXsHB*ZnbSx@WoX{+aKJP@RHhm z18P8lfjy_brcm>X)~`{yDJCC2Bn(Ze+wxFAREws>Pvr`5Y4;BfBuz}%-;!WvJ tga&%ya2A^ba+7Z>~+I4|N? t}!T}euvTD-;V||jfzJ_>7z6- zf3dQ%>b7s}$$k)3CWq|5{!M1SHKn5!y18lopUc&M8$NirLbAl?STSO<^TjNfOgl*) z?>d-<_FG$|FUYc@pzODB+w9k5#Pe$IXelzJ@v`#238b1_>rIMNdE-4;1UECaipu1H z=V%g^g3XbMcc#YqmTP4NhGGc;wIajh%+B9{PY5{Z8P06QZ^Nz2Aq*o`f8A?|?82=T zKRde%;hVf%5{_P{I2s_n|AIRZaEl6V(aN6N@wr1Tz<%DxQb$OV%NPe37T0k@O_D4{ zy$1&klNvzNye_3R>wccE=9yk f%nNz5g48fKB6M#E0xCQSwZArF!B7-%Jx!ra2vHHRGpYP1k^uiZ+ML^N zSR~TOYQjZ7+Vlqabwz9O?>KFF-flP%{7=ZH1{fKR6|?H4jTwPO8`4{IDX9n3v_Mei zfFVBeZ*xTs!2W5`+MX2Vs+|9^=_hHVrwb9Q2khp@swy_Pqet*JuP0Uk#Db5ZlUDUh zJQUcn@f66FGryUaHo!<$P>kmF30j(O)5JzCuE8++ceR4-PEJl4VnNSIGzKZqFyAnB znd@sk6OBkQh-^=W57~hSNj}FZ2V %SlBqW5U-vyMmr(V~2YC~A%H zJ=$)9n)QX>pJ#gZMR3o=E;t`J9xKLYeX$nDhjzb5@^|v$&PSt&t7jQxTvI fjN9taj7Yz>5@ q5ukXkm z`sEABa_eQ8@*TT?E|f~}nVnNt{ql| (;K{EBvsZ{WS|`Otk=-c2tWw0TNZ?03t6>nuiaH3XRayL$4 sCm$;Ky1<}oj%TaY0=HD~bMR;*y*%Mr_*hKOnp1I#3WUt}^Um>7Z0lBPa(w`| z`xk4EKL+pLq-BkDVSZg%WYMcXVffYZ&oHk$Pi?iQVGhdZ6A1tx@|rO3VpM|`geA_u zP|EMii(W7Cv~~PiF1px}oRM*8?yB
M9_u!dj#*G<`|n57Rlea)i%*+b zFG~N-zol; Tz!G`$AOCgAdouD+vdeBU1ssb)8f(6chtslAV(5uQ+%wz&X9s zTZ4*bK&fS6GVI0knH^Ynt6#7p@HGgWaWgR7_bd?a#fFSZNb$dU`G_hgD0rN52lILo z+5ZzEwPp#(PQ_}GX7z%AYi1E~4<-sRi~$uTFo2=Ad&j3&Zyit9Ygvdf^z>Dk$aK^C zH|GWsu$^&mrm 27VS8JD*Fyhh1^>vWGJ*{=& zvK=gxcU^^Oj*srSgY1{^d+K=kyAzd$we@7JE);FUPX&}KQHzwUlh?PV9aZ2=#!7sQ zd=5bh;s{6_XM9M7)W0j2B(w1X3}u1PiHq;#&}axX^11+F5%!McvpG`k4_$cDxNbHu z!{U4uFpjV=(-M;K1xQ<=imkH*7;17@`N5>f*`POU&ZVc{IJ$Lu0Gaif>Ue+Vwr4}` zT>q)p8M8&R8y-$Po|)Zf=;+-gXGjezR6TTIRNP#qh2kLfj9{DY`4H&8dx52PVH*Tf zZ@rSX9X)nO9im-oYil9zleOpuQ^Z}_L0Z;%=zsRL3;!4yx%^mV(3n7HQ{nTre6?0y zO)YZD)-*>YDwey)lB?U6hfZXrPl0hKMd&52Ss8LzZ|S!cSUj8 IE}xL(W3 ?`c!2krF|o_#zZ4wDPZH>4gKX2BGIaHyPl zluE?IkzBZkuMPUp9273)v2g?R_cKiPyFnd>z|uibJ7*vCy^; g&+XX(XPFVn_?l0 zVpW)A5kfbJ6=N#~mEq@R>5RsyFAU3f_0}~yC`8uw_HHtMTy6JTkrhQKj0P|b3=clE zC?YS(|BCsXs# v=~a2ZWNTVVzi7? >dr@=qAOs~vd8R{n*`UsThN*rmAa zciLWFUM}v#w?hDnFcj6tW%|q}EiUJU&MNI%$`*o9ub*tjr4eR1#Iuk*j=vDL{wKy( zQqp_=^QY`V#Z_|Pq@*n3c+&+JCZ+$HW0{HLEAhlq6)}X+$~xRQe8f*O1(#YU$s+1@ z9k1DWsva{#) yEswIYY=sohkG> zS#QF^!X~+?_%8*4{GT->U5wfMf&TLHXcQC_vTgp>2k+%j>-F0zDikM4wxt~3#o?jB z`WzKp%%llj_r`N#u8-#GNxV`ZCm-eq&$kq)4UM16aMJxb3F>G|8^eYeOI743U;yc- zZqXLf&c_{g=1t;Y@v><*K?0r1_euO8KYpY_sQafN;FT@lff95-o#)?S)-eo+{5>T~ z=@}}U(u6T~Z@#Hw3Vo=+fkI3)HUHq9EQ_=_@s G!p#7Uq)MjhXxZfrxHRyk#e`q(uZQR<~k-GQ!S(K0rZG|hJ zgF;5Z_iSAvJuf+lgXeE+u#=BzpMBR1<&ApK>OGc@%ly_w=QOpKFQvY|zP3Yvh_#h< zVPgjkGc&U{>hPZ8Q+#|wdnTM6E33*R%8_tjLjw;jk0+62ucTZqie>7KO_K &fMAdoq8R`Cx1v#pKlwK; z6`Wpk+td9t1`la`*Xsz8zc%30oAS5?4_$so_~uC1=(-`9xFTh2%<|XXAb|`W9R`@{ zSVQ)d#fKOpzJGtKs5-qoKOcAI`ZH1({`$1s_L0!b%ZuO8bH+>$t6Oj}8Il);&38!l z4>gVh0YMzr=)CP{#F~{`{}B~fkIs9}j`rJPuNJ@>>32=8uC3MJ!L?IW+ug$~rHs3} zL6huAg~61me&N_0OY)+lzhS8w!eM-k;o^xqYsb*I5!DDPidUo(`bjgGP&J4tr{s3L z|LK>oqj|DJ0e>qQ`Zm41f !LTwW074}gT!_)&~K0Rv5!Hj77S32j09s} zyrAvv4ZcRnq#|UvJ>Gd(Lk ofSlj!C%|21^NQzfnpUC|_Fal%cwYGo+m9bY71L&& zv9Vyar7r?1&8!|luB+gZI&NwI%`&k)p26c9JY@*sXXdG}R?PVLcr;wx!7>=^3WqE6 z@hhAAih)UDYSRWery+ilm?)F9G;I%hL2WI0H|^DKxkeFv#@+827QJ7Gx&YK}x(oyu z7@!37hebvXpTbj0SeJd{;*WeLcJ{$gT+t|t!30jb9e4m4cs=|$eWrQ2V+(j%Q$9Ww z_;|+)&a*T>Uj=GmqB$afKSmV)OdVp`FmcqDLsx540X>&D8WxTzq7>zKZ{+Dxwce(j zYAy=b^F&+Sf#PA*?0w{~$`EZGot_s1hE1NUs{6w#=Lk;)1%(#VhqK{F*k$t9uSb7W zz)ySu7IJfO%}QUJ+v5~ESCRssg7zqB#&4nmBm7y7z>#Q zK(5g&L?8eCyAVs3{;DbW P`&_6`m sVekQqC3DFC-b{h`{_6myuT9pVBW@G|-10WNxo;Gl3M{8g!axg|8K5tJ&vCAprS4 z%2}jt$`p}=3!tI* |_FX;LJ0F(WuH z)FDqiK9M6W@cb1)zXbq$dwT#_YI2g&*C(Hr{uQ%A8ItteqF?*^!RBxF=^vcqi)R>w z3tUK@8K;z+kX1ntxo7Bfg}ZWbX`)$BJ-p<<#e8ud?eqNjF=1Fd>(7brd8@IF>($dO zvVxxOT+EO>;!uP@s}!r`N9xaBmWYBsKt3nS!*wCSTPlN`Bo-i`Qs+m{Vxt4dbV$YZ zek+uH_o82dF-ccZO~?ykC1vuE+Cxr2;HyZPD;D&Dqk~l}kKEcU5A?8rSL+)BcXIF7 zueY}>m=+U_d6}5d0s=x;7giQAQTETJ`N5+v?=Gv+P*PG-bz|da3rYg67A~w_9K*LdSXfwH z6E3bFCE)2;fl}R@al3kDCBtTEbFldzn^8+w9ISTQIy?fQef*>Oo|N-mH}#qJ^au)V z9?=~IPR(4D=3-Jc`W~xMR4SvI`RsRnTPG(O1qF2Lv8;F7HngwCTuqb4_VP aEknTJN_AUs5gK4pZ&X%5==$e|p7$Y(dJ_M+aDd z-kO)Bay($oUO(~vD0Ok_r&}Ti kF@^e{Cc^!@hz&rQ7P2}G4%@|9-G=uO7M5etqwSdG)$Xzn~ b@=gBz8^&Fxl+0`O z-7>>;@yhP5;){@Qu_ki2`9X+ciP`vP;u(4QxKutom4=AsuhkpCB{37O_-`c;C}=lS zteu41t}7U3Q_FwLHc(1$)B1`+{kyVlISo8pkLKe3U>h*Yz&jc};LhL;ps1)0?EjXU zl!#kay4jb-4mLsz!otfRrsegJKuL%mphq$%j85ChTfnOWq2o(RUGsa~V_63YeO4f( z!=Zfil 6x3gd z9WSDy)+ zjCwc^c|?(g`ztZx`2{foB6 zUnV#9mq(Ev#~%mu`Pe7!KmWa<`Vo>`uPBR=FjS^#=Qw6bSY^_g@bjl&vFeN<7NU{4 zqL+AxmjQeQH?$* 9z_GeiSPF%EO9^OSEL2`sr zWyN4~7@^w788IrrS4-67#wGBQ(*EF2_o;4t^cr%g^Vj1Chktq_cE79NV+(SwHM@YH z&mzU{A{zA0E}Me*T}DVlRLd-IiZ)2JK}uF@c@oVkW<1LX0`p!l(?p=_*Yk#Kh`%Tu z9m;Lf!0V?!-d+(1P?q%4X#1MDxH1d}7g15fI|jlze-_ENzi QiHnhbjiWA$F1@%L!Cae(k7| ^uSqFP)qWwy!iiEE-i}6$PPS(BO@Cg>o66Uln|2; z6PMIqk`g&8OboRc&ClM@TUxdm>#1|l=Jds`=Hmi-6zaC2(Nr(4+jybsx<+WLU)0pq zYZ#2HJHh7LW~qtWWfO#duJ4;Q{v)A24duxQw$**=#pib)n^~P=-UmAu583p-zkt7S zyaERTbNvXE?vK6rcrUKz>kf3Hur}s3W7iq)M@a7k_45W=R1iNePTLa-9PX}nEx7>D zod5Lf!{|R&b(!QC64@)ZET0_oi*{>c>}uVY>mN~t>B!axYSqOiCrWO;PVavP3%8q7 z*HBBF$INM)8mGO=(GhSJ>8UEr&d!d$^y5w{F2I%hX~bboasf_q-Gj*vrE=sF46$U- zz-4}MMOyj7X1yPJ9G3?V<)Y}mZC}F<`ZK@I)eveJ*J&ZHZ)8}QKQ=Zt6BCoTloTS3 z?>cDVHB}1A_Gz3==s$3R?W@|l=1TwSURmK^M`j3L{ekxFHGSRjQSk=6i2sC7I#uv> z6#tp#jn $zjS z>ptBVJKOA!1!1MQto-zv|LIlvJ-2qv^)52CuDgwOP3x7x1mU(t!z;T8&bD5Ta7Np~ ztJ8L&J4%8Jk+Uwu|8O3hy4LsH+~2ER!CepX|J-SfkgQ4Kz^v(Mw#v9t&k)Gq+NZho zJB`=~?!@B#^|;Fiu_uDdAsd~{(X|9OE1Z>nT*eGr$-C_Hu8ok^b+>TlE!H tCnnCrV4UEQyP@au!Cm)E |(m;575AhbP*zvG2(lRcr$72cRKJ#U0 GAeCT#y^^YMc3e4A3_ZC=;h@R{&y z-QJ;dT)7VwMDTn$Uizw4rLcMDA v-xn7RRPRD_Dw%R1c+|%2zLqoN)%|@fxV^aP zJg;cMz&=m#o$1~ilgr~!r6iRu_-A&YtMcm?bzpKzCMYf|TcHhClpnDSK39L{TD9h# z>5SJJUKDPXL=*PB{)0<#0(btMA`8vQBQT8swaeeLvJeWUUz8K}Nf-4+c9{K6?q1mx z@ZdfC9IWLhDerOAI|qi37VsRtBBQt9upT9Q+>6!PKRl68bmvMfySFbfZf&VW_r8`- zp8N)BIsC-9S=8XYcoKDk-zR5xept%zSKU!UOib+eXQ>L+X(@pVk$0{79R~$eF;uIc z0xt`Z**bA0I~?Y!Hcni|kbrD{FN_&o7L9;L0wte+5}SKvnHC3CBFc-=T4fyX2humK z?7DRMSPS4>;#N?B2bU+Zi6u1j$tKf7eni2apZ=F)b?(~f)#T{$LVbZ|Owg4o8c8Lw zdA&esq `G3; zk604*QmM$CK(NI?oP@D4wFe4xhDCQ145k_QW9;Wj=Y)Z?*wn2F&?tQkDfx9{y7qfV zuic$tJV)$QJ>o6G^C9dBL$c=$$-$pfidf3)_^8A`g>dc5|76*wNBihWI55F6{M+!R zmHf+7C!sR^wvZ+!zm%lUFsgWJVzx=3u1?PN?Mh^#L#5SD4ka~e?;sen6#wGe9~C{+ z7U>@N4lgY$%e7Zm$8|j4h&iQ!Z+llkB2W3(6$3cC?7gerKi{PO*qAu zSg3xit~!*!M$DnN@a;y)@wtXU+f%hDplHeZL4sPiW{H6yA dx-|f;|H)O4M$~7kJq}aS~2LN!)|2qqiq{dO}#wweHoDUkoQ%a z`gr`1_Y%R$h2myGXlW-N$!j{hNt{MIiWaYvSaYs0nxquQNfmBWP1i$)18u@Ros`|c zaUx2AlrG5&c|N|0{vbFrtGuDBptIjw93)12;U>Qyrm`R7&r5cZ;~`5&>NL?IZPG9| zJ=ZNe{3y9?A@A$+Q;T;asDr;h>9o>eM!D?`6B?X%jmwU^_Grd9)q@V3-10n9dC0MD zR;&cPi0Cge>+I>(R8Vz}QE{}T;nhy~pEzFGAo#5i?)v`?&iNwq2~`(0?Gj*_4bUd5 z!&PW!!AA(0a~exyLZ9DW;=C56svV=L>vdOv3nvOu*~!h3tihiwN9o=E+5RCd%<;l0 z(1~0700OLs<S&cW)J1v+hmnz;L+2@^^VZcCe#Mg zeH$$Jeg1LL8c!&3 %d8zJ%WD~vHZWn1m*k*rtBY%zH@AndoDs{pg@9o+h9T3&wkHFh{N0F3wf)2x# zL#&*jMm4Pl>E;O~L>BGHqvAPAk9V{Kz)Z2_TVGQ)YIaG{|C^77jXlPs=gmmhtR^8P z#eLJ!N(mo{N+1v2C;Q}@WmG@CmO~TISh^M+fihU7W_cX~etb6gFT{}ldeb@j6I2`p zKb-tTucJD;TMz;OF( z4S4@|H3nWdidi(I-rccyxcP_ites8ELuI_OX+lY?2)@u>-jCuh6?pmzJB?W}%*!87 zm)?TQb=K}@NOqy3@U1^)?xkwTpe;o3CecYgD OU=(EqD|M&%0WYS;F_{ z*W0&!R;h{M*(D3h)^ouqmZ3@Ptn)xUx1^J+G>uOeEr`-td#n4jy1zEavG?SMntJSR zKd5{FmSUUTDoiboTw46se5#HAc=a9$nJJ zD-MJ6;g^EP)R)xsIgJQJp^R@e8($_M1>9LKJ?SRDQ?P-S1-i^fH+^P!DSlwkszhC3 z_lm~yG-j#&8a)QjRXL3$GU1Owl5M9$6X=jVhN8xNg_7-?vmD`(IT;xlUfSS^({Ebn zubouCBqdRw5u3!@RxVOuDpz|$h+Rmv@u_8$Y?Olh!5f(WopsN*Yv^vTF;MYuvy;ib zi&O@i1ANjG )rS>@YZ zLuld?DaIHm&{I9Z{z;hWO;#UV%9F4j) 6Zq!19; z804S-!@cQd9$JNepdmrJ;eU02{guS_cmQO)Lb%z>hl}&>f2r1n;|2;py4@Apo`v=` zXZi8lKp|`Y4X9c-s1e@3!W4ahmqFhC{(VzXNOm!Z5zz9ap<%F1+!{S>CyH orO11v|1NmYKR7PAFa TFc^SBPLpAokt?m z&3MrKc!Dd@1mmfFy?$-4rAs2-= 5i&(-r8FU%rkhzld4@DDA4KbC2 z$^18>5G;T&57ufd5Hcwrmwr4^?c`JTt%;=iSjnvmC}sV_sV*RmK3l zO(z6#Evo_Jd^?(Our&9cc!L;F+iS-xwwf<7Iczd7G>E?Cdt!f6MYC7TkkCHChQkiR z%zJ69ksb9Tt(G1@e1Emx?1=E2Hp__XCxU=j!*cot#x|Y`8@X{XW1bk7D7Mn!2vk6f zXzcP}q`F}9(9Ng^UiU=mdFA74T9~vGOD{3$PL13Ctb_~N zyn96nk^qIw^&$s-Q4Vq$!o?zLqv3@)2R;wo@gOaK1hHW3EtRM=B;S4dZM09JnlGq~ z%>W!-la&8`WBb }gIjOTWV76BY^h<|@}Yf)9Q?#O*IF>=aCXF{vhQ9qW^8S~Yo+piWQ$e^jm8y6$U z4mQra)%*0Ad~Qcp&mpdkNYKy@!x3CEbc+n+I%$)n{U?iuCDHK9S}KXgjxWS@j~K&e zpRuUEo!H5xgI*7J3JRwLd{xdEmZDnSftFZe69|{PHiV;xEnErbB7Nx(;>QaXnQdJG z=FVk_=Rcwb>f$fJK;6hS`kMI1P7kF6MVUjh!^=OHN_OJ#g0Qg@cFN1q902s+v@0*h z@7715QMN4&y;}gFf7V{u&vu0-+5OVA(%)h;MSxF& g z^A3w2$=QA DfLX}) z%l*4Udxzu0qQms9{xUjp>>*lH_=+;{tKY( 7 Ct`sjH^&&b`&pUUgzBoMLSWb4INWX(x5nHOPhi-j6y+EwNK{ za%qCS1O#G(MR{x4QkordtlOGkD^sH|HN WSELG`xz-n(7YDcGVeDM#gC zGZiEMSkY0vWNxTZew%CZg;mZrAU}y@|6h}PRw4#IO!EA(&QU<}mZcc^0S>dr_N ?=bwjf74(7SvAh{na zn}0=$uod72`PT&57J%FJ#XG2kV&|=&X(`~G!H$LK$q6DwlCK8GuVycO8fUf+6u(mX z%-WOP2#Pk-o*D~do6fzyzpM_Sm|St#%QnWGKeC36i>kf+V1uj3n>D{V*91(0ZgK1j z-?D`3Ugr(Lv%STp0FvdBb-e`P;Aby?%Yk0~&042av`MjSmZQ6&sLuL6izREq|L@n2 z4>0tw8)sh(94B!}x>ifi+ut@9^uGhhPi9xcq}_-2Pd-n;qx{j>Ava=zZ &>Ke*!qT4GdpobowMO_}9A+CCz%o{H=3CsQ>ckc Date: Sun, 16 Jul 2023 16:05:35 -0400 Subject: [PATCH 03/24] [GBP: NO UPDATE] Fixes a dumb thing I did that broke the malf ai emag ability (#76875) ## About The Pull Request Youre supposed to pass datums, not types ## Why It's Good For The Game I like my features working ## Changelog :cl: fix: The malf AI emag ability now works /:cl: --- code/modules/antagonists/malf_ai/malf_ai_modules.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm index 2af72779dc36..95fa34d31a9c 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm @@ -1063,7 +1063,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module)) clicked_machine.balloon_alert(ai_caller, "not operational!") return FALSE - if (!(is_type_in_list(clicked_on.type, compatable_typepaths))) + if (!(is_type_in_list(clicked_on, compatable_typepaths))) clicked_on.balloon_alert(ai_caller, "incompatable!") return FALSE From 1e9edd6a49665dc9ae5e857e72455961be4f8230 Mon Sep 17 00:00:00 2001 From: Kittynoodle Date: Sat, 18 Nov 2023 16:10:57 -0600 Subject: [PATCH 04/24] Refactors vendor tipping and adds 2 new malf modules: Remote vendor tipping, and the ability to roll around and crush anything in your path. (#76635) Title. Vendor tipping code is now on /atom/movable, and any movable can fall over like a vendor does. Things like crits have been moved to type-specific availability tables, their effects are now held in their own proc, are now random per crushed item, have probability weights, etc. In the process of making this PR I also had to fix another issue, where a bunch of take_damage() overrides had incorrect args, so that explains the take_damage changes I made. Tipping now also attacks any atoms on the target, given they use integrity. Adds 2 new malf modules. 1. REMOTE VENDOR TIPPING: A mid-cost and mid-supply module allows malf AIs to remotely tip a vendor in any of the 8 directions. After 0.5 seconds of delay and a visual indicator (along with other warnings), the vendor falls over. 1.1. In the process of making this I had to expand a arrow sprite to have orthogonal directions, which is why you may see the testing dmi being changed. 2. CORE ROLLING: A mid-cost but low-supply ability that allows the AI to roll around and crush anything it falls on, including mobs. This has a 5% chance to have a critical hit so it isnt THAT terrible - plus it's guaranteed to never stunlock. It's real utility lies in the fact the AI now has limited movement without borgs. Also, the psychological factor. As a bonus, vendor tipping now uses animate and transforms instead of replacing matrices. 1. Generifying vendor tipping code is just good, period. It's a very wacky and silly little piece of code that really doesn't need to be isolated to vendors exclusively. ANY big and heavy object can fall over and do a ton of damage. 1.1. Also, adding weights to critical hits is really good, because it lets things like the headgib finally be a lot less terrifying, as they're a lot less likely to happen. 2. Remote vendor tipping is a bit of a goofy ability that isn't really THAT practical but has a chance of catching someone unaware and doing some serious damage to that person alone. 2.1. Atop of this, vendor tipping isn't that loud of an action as say, blowing things up, or doing a plasma flood. Even overrides aren't this silent or a non-giveaway. A vendor falling on someone, though, is a mundane thing that happens a lot. This is a decent way to assassinate people before going loud (or at least, damage people) that isn't offered yet. 4. 3.1. For real though, AIs rolling around is just fucking hilarious. The ability to move isn't offered right now (which isn't that much of a bad things), but with sufficiently limited charges (or limits to how many times you can buy the ability), this can be a funny little t hing that lets the AI potentially hide somewhere on the sat (or just relatively close to the sat, such as engineering [it can't go through the teleporter with this but it can go through transit tubes]) without the need for borgs. 3.2. Also, it lets the AI sacrifically execute people by blowing up their brains. --- code/__DEFINES/crushing.dm | 15 + .../signals_atom/signals_atom_attack.dm | 6 + .../signals/signals_mob/signals_mob_carbon.dm | 2 - code/__DEFINES/is_helpers.dm | 2 + .../vending_machine_behaviors.dm | 2 +- code/datums/components/sitcomlaughter.dm | 2 +- code/datums/components/tackle.dm | 2 +- code/datums/station_traits/negative_traits.dm | 2 +- .../machinery/porta_turret/portable_turret.dm | 2 +- .../crates_lockers/crates/secure.dm | 2 +- .../crates_lockers/crates/syndicrate.dm | 2 +- code/game/objects/structures/window.dm | 2 +- .../antagonists/malf_ai/malf_ai_modules.dm | 210 ++++++++++ .../industrial_lift/industrial_lift.dm | 25 +- code/modules/power/apc/apc_attack.dm | 2 +- code/modules/power/lighting/light.dm | 2 +- code/modules/shuttle/shuttle.dm | 2 +- code/modules/vehicles/mecha/combat/durand.dm | 2 +- code/modules/vending/_vending.dm | 386 +++++++++++++----- icons/mob/actions/actions_AI.dmi | Bin 11221 -> 11853 bytes icons/testing/turf_analysis.dmi | Bin 1144 -> 1804 bytes tgstation.dme | 1 + 22 files changed, 533 insertions(+), 138 deletions(-) create mode 100644 code/__DEFINES/crushing.dm diff --git a/code/__DEFINES/crushing.dm b/code/__DEFINES/crushing.dm new file mode 100644 index 000000000000..1261b98e730e --- /dev/null +++ b/code/__DEFINES/crushing.dm @@ -0,0 +1,15 @@ +// from _vending.dm + +/// Set if the tipped object successfully crushed a subtype of /mob/living. +#define SUCCESSFULLY_CRUSHED_MOB (1<<0) +/// Set if the tipped object successfully crushed a subtype of /atom. +#define SUCCESSFULLY_CRUSHED_ATOM (1<<1) +/// Set if the tipped object successfully actually fell over, which can fail if it couldn't enter the turf it tried to fall into. +#define SUCCESSFULLY_FELL_OVER (1<<2) + +#define CRUSH_CRIT_SHATTER_LEGS "crush_crit_shatter_legs" +#define CRUSH_CRIT_PARAPALEGIC "crush_crit_parapalegic" +#define CRUSH_CRIT_SQUISH_LIMB "crush_crit_pin" +#define CRUSH_CRIT_HEADGIB "crush_crit_headgib" +#define VENDOR_CRUSH_CRIT_PIN "vendor_crush_crit_pin" +#define VENDOR_CRUSH_CRIT_GLASSCANDY "vendor_crush_crit_glasscandy" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm index 5191d7e8c68d..b9e090b66b72 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_attack.dm @@ -52,6 +52,12 @@ #define COMSIG_ATOM_ATTACK_ROBOT_SECONDARY "atom_attack_robot_secondary" ///from relay_attackers element: (atom/attacker, attack_flags) #define COMSIG_ATOM_WAS_ATTACKED "atom_was_attacked" +///Called before a atom gets something tilted on them. If [COMPONENT_IMMUNE_TO_TILT_AND_CRUSH] is returned in a signal, the atom will be unaffected. +#define COMSIG_PRE_TILT_AND_CRUSH "atom_pre_tilt_and_crush" + #define COMPONENT_IMMUNE_TO_TILT_AND_CRUSH (1<<0) +///Called when a atom gets something tilted on them +#define COMSIG_POST_TILT_AND_CRUSH "atom_post_tilt_and_crush" + ///The damage type of the weapon projectile is non-lethal stamina #define ATTACKER_STAMINA_ATTACK (1<<0) ///the attacker is shoving the source diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm index adae4324d59e..b6741ebb01e5 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm @@ -22,8 +22,6 @@ ///When a carbon slips. Called on /turf/open/handle_slip() #define COMSIG_ON_CARBON_SLIP "carbon_slip" -///When a carbon gets a vending machine tilted on them -#define COMSIG_ON_VENDOR_CRUSH "carbon_vendor_crush" // /mob/living/carbon physiology signals #define COMSIG_CARBON_GAIN_WOUND "carbon_gain_wound" //from /datum/wound/proc/apply_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L) #define COMSIG_CARBON_LOSE_WOUND "carbon_lose_wound" //from /datum/wound/proc/remove_wound() (/mob/living/carbon/C, /datum/wound/W, /obj/item/bodypart/L) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 37611368f8b1..0884919a54f7 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -216,6 +216,8 @@ GLOBAL_LIST_INIT(turfs_openspace, typecacheof(list( #define ismachinery(A) (istype(A, /obj/machinery)) +#define isvendor(A) (istype(A, /obj/machinery/vending)) + #define isvehicle(A) (istype(A, /obj/vehicle)) #define ismecha(A) (istype(A, /obj/vehicle/sealed/mecha)) diff --git a/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm b/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm index 5bad78a35154..b4e5609531c1 100644 --- a/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm +++ b/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm @@ -22,7 +22,7 @@ /datum/ai_behavior/vendor_crush/proc/tiltonmob(datum/ai_controller/controller, turf/target_turf) var/obj/machinery/vending/vendor_pawn = controller.pawn - if(vendor_pawn.tilt(target_turf)) //We hit something + if(vendor_pawn.tilt(target_turf, 0) & SUCCESSFULLY_CRUSHED_MOB) //We hit something vendor_pawn.say(pick("Supersize this!", "Eat my shiny metal ass!", "Want to consume some of my products?", "SMASH!", "Don't you love these smashing prices!")) controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESFUL, TRUE) else diff --git a/code/datums/components/sitcomlaughter.dm b/code/datums/components/sitcomlaughter.dm index 8dfef21b749d..04a190d15f01 100644 --- a/code/datums/components/sitcomlaughter.dm +++ b/code/datums/components/sitcomlaughter.dm @@ -1,6 +1,6 @@ /datum/component/wearertargeting/sitcomlaughter valid_slots = list(ITEM_SLOT_HANDS, ITEM_SLOT_BELT, ITEM_SLOT_ID, ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET, ITEM_SLOT_SUITSTORE, ITEM_SLOT_DEX_STORAGE) - signals = list(COMSIG_MOB_CREAMED, COMSIG_ON_CARBON_SLIP, COMSIG_ON_VENDOR_CRUSH, COMSIG_MOB_CLUMSY_SHOOT_FOOT) + signals = list(COMSIG_MOB_CREAMED, COMSIG_ON_CARBON_SLIP, COMSIG_POST_TILT_AND_CRUSH, COMSIG_MOB_CLUMSY_SHOOT_FOOT) proctype = PROC_REF(EngageInComedy) mobtype = /mob/living ///Sounds used for when user has a sitcom action occur diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm index daa66ea9fb5b..bfdd2ce481fb 100644 --- a/code/datums/components/tackle.dm +++ b/code/datums/components/tackle.dm @@ -365,7 +365,7 @@ /datum/component/tackler/proc/splat(mob/living/carbon/user, atom/hit) if(istype(hit, /obj/machinery/vending)) // before we do anything else- var/obj/machinery/vending/darth_vendor = hit - darth_vendor.tilt(user, TRUE) + darth_vendor.tilt(user, 100) return else if(istype(hit, /obj/structure/window)) var/obj/structure/window/W = hit diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm index a328506bb959..0eb7668c6d8e 100644 --- a/code/datums/station_traits/negative_traits.dm +++ b/code/datums/station_traits/negative_traits.dm @@ -381,7 +381,7 @@ if(istype(current_thing, /obj/machinery/vending) && prob(45)) var/obj/machinery/vending/vendor_to_trash = current_thing if(prob(50)) - vendor_to_trash.tilt(get_turf(vendor_to_trash)) + vendor_to_trash.tilt(get_turf(vendor_to_trash), 0) // crit effects can do some real weird shit, lets disable it if(prob(50)) vendor_to_trash.take_damage(150) diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index f3066839a08f..198bccaef762 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -388,7 +388,7 @@ DEFINE_BITFIELD(turret_flags, list( addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), rand(60,600)) -/obj/machinery/porta_turret/take_damage(damage, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) +/obj/machinery/porta_turret/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) . = ..() if(. && atom_integrity > 0) //damage received if(prob(30)) diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm index 4698c8469d24..25989b2bdbc9 100644 --- a/code/game/objects/structures/crates_lockers/crates/secure.dm +++ b/code/game/objects/structures/crates_lockers/crates/secure.dm @@ -21,7 +21,7 @@ . = ..() ADD_TRAIT(src, TRAIT_NO_MISSING_ITEM_ERROR, TRAIT_GENERIC) -/obj/structure/closet/crate/secure/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) +/obj/structure/closet/crate/secure/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) if(prob(tamperproof) && damage_amount >= DAMAGE_PRECISION) boom() else diff --git a/code/game/objects/structures/crates_lockers/crates/syndicrate.dm b/code/game/objects/structures/crates_lockers/crates/syndicrate.dm index b18321338c49..9d4d5e6c8ced 100644 --- a/code/game/objects/structures/crates_lockers/crates/syndicrate.dm +++ b/code/game/objects/structures/crates_lockers/crates/syndicrate.dm @@ -18,7 +18,7 @@ laser = 50 energy = 100 -/obj/structure/closet/crate/syndicrate/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) +/obj/structure/closet/crate/syndicrate/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) if(created_items) return ..() if(damage_amount < DAMAGE_PRECISION) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 1d0848e0ad28..4a0631594c46 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -327,7 +327,7 @@ return TRUE -/obj/structure/window/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) +/obj/structure/window/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) . = ..() if(.) //received damage update_nearby_icons() diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm index 95fa34d31a9c..576ab890a9c0 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm @@ -1,6 +1,16 @@ #define DEFAULT_DOOMSDAY_TIMER 4500 #define DOOMSDAY_ANNOUNCE_INTERVAL 600 +#define VENDOR_TIPPING_USES 8 +#define MALF_VENDOR_TIPPING_TIME 0.5 SECONDS //within human reaction time +#define MALF_VENDOR_TIPPING_CRIT_CHANCE 100 //percent - guaranteed + +#define MALF_AI_ROLL_TIME 0.5 SECONDS +#define MALF_AI_ROLL_COOLDOWN 1 SECONDS + MALF_AI_ROLL_TIME +#define MALF_AI_ROLL_DAMAGE 75 +#define MALF_AI_ROLL_CRIT_CHANCE 5 //percent +#define MALF_AI_ROLL_MAX_DISTANCE 1 //anything further away than this, and the roll will fail + GLOBAL_LIST_INIT(blacklisted_malf_machines, typecacheof(list( /obj/machinery/field/containment, /obj/machinery/power/supermatter_crystal, @@ -1047,6 +1057,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module)) if (!isAI(caller)) return FALSE + var/mob/living/silicon/ai/ai_caller = caller if(ai_caller.incapacitated()) @@ -1100,8 +1111,207 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module)) return TRUE +/datum/ai_module/utility/core_tilt + name = "Rolling Servos" + description = "Allows you to slowly roll around, crushing anything in your way with your bulk." + cost = 10 + one_purchase = FALSE + power_type = /datum/action/innate/ai/ranged/core_tilt + unlock_sound = 'sound/effects/bang.ogg' + unlock_text = span_notice("You gain the ability to roll over and crush anything in your way.") + +/datum/action/innate/ai/ranged/core_tilt + name = "Roll over" + button_icon_state = "roll_over" + desc = "Allows you to roll over in the direction of your choosing, crushing anything in your way." + auto_use_uses = FALSE + ranged_mousepointer = 'icons/effects/mouse_pointers/supplypod_target.dmi' + uses = 20 + COOLDOWN_DECLARE(time_til_next_tilt) + enable_text = span_notice("Your inner servos shift as you prepare to roll around. Click adjacent tiles to roll onto them!") + disable_text = span_notice("You disengage your rolling protocols.") + +/datum/action/innate/ai/ranged/core_tilt/New() + . = ..() + desc = "[desc] It has [uses] use\s remaining." + +/datum/action/innate/ai/ranged/core_tilt/do_ability(mob/living/caller, atom/clicked_on) + + if (!COOLDOWN_FINISHED(src, time_til_next_tilt)) + caller.balloon_alert(caller, "on cooldown!") + return FALSE + + if (!isAI(caller)) + return FALSE + var/mob/living/silicon/ai/ai_caller = caller + + if (ai_caller.incapacitated()) + return FALSE + + var/turf/target = get_turf(clicked_on) + if (isnull(target)) + return FALSE + + if (target == ai_caller.loc) + target.balloon_alert(ai_caller, "can't roll on yourself!") + return FALSE + + if (get_dist(ai_caller, target) > MALF_AI_ROLL_MAX_DISTANCE) + target.balloon_alert(ai_caller, "too far!") + return FALSE + + var/picked_dir = get_dir(ai_caller, target) + + new /obj/effect/temp_visual/telegraphing/vending_machine_tilt(target, MALF_AI_ROLL_TIME) + ai_caller.balloon_alert_to_viewers("rolling...") + addtimer(CALLBACK(src, PROC_REF(do_roll_over), ai_caller, picked_dir), MALF_AI_ROLL_TIME) + + adjust_uses(-1) + if(uses) + desc = "[initial(desc)] It has [uses] use\s remaining." + build_all_button_icons() + + COOLDOWN_START(src, time_til_next_tilt, MALF_AI_ROLL_COOLDOWN) + +/datum/action/innate/ai/ranged/core_tilt/proc/do_roll_over(mob/living/silicon/ai/ai_caller, picked_dir) + var/turf/target = get_step(ai_caller, picked_dir) // in case we moved we pass the dir not the target turf + + if (isnull(target)) + return + + var/paralyze_time = clamp(6 SECONDS, 0 SECONDS, (MALF_AI_ROLL_COOLDOWN * 0.9)) //the clamp prevents stunlocking as the max is always a little less than the cooldown between rolls + + return ai_caller.fall_and_crush(target, MALF_AI_ROLL_DAMAGE, MALF_AI_ROLL_CRIT_CHANCE, null, paralyze_time, picked_dir, rotation = get_rotation_from_dir(picked_dir)) + +/// Used in our radial menu, state-checking proc after the radial menu sleeps +/datum/action/innate/ai/ranged/core_tilt/proc/radial_check(mob/living/silicon/ai/caller) + if (QDELETED(caller) || caller.incapacitated() || caller.stat == DEAD) + return FALSE + + if (uses <= 0) + return FALSE + + return TRUE + +/datum/action/innate/ai/ranged/core_tilt/proc/get_rotation_from_dir(dir) + switch (dir) + if (NORTH, NORTHWEST, WEST, SOUTHWEST) + return 270 // try our best to not return 180 since it works badly with animate + if (EAST, NORTHEAST, SOUTH, SOUTHEAST) + return 90 + else + stack_trace("non-standard dir entered to get_rotation_from_dir. (got: [dir])") + return 0 + +/datum/ai_module/utility/remote_vendor_tilt + name = "Remote vendor tilting" + description = "Lets you remotely tip vendors over in any direction." + cost = 15 + one_purchase = FALSE + power_type = /datum/action/innate/ai/ranged/remote_vendor_tilt + unlock_sound = 'sound/effects/bang.ogg' + unlock_text = span_notice("You gain the ability to remotely tip any vendor onto any adjacent tiles.") + +/datum/action/innate/ai/ranged/remote_vendor_tilt + name = "Remotely tilt vendor" + desc = "Use to remotely tilt a vendor in any direction you desire." + button_icon_state = "vendor_tilt" + ranged_mousepointer = 'icons/effects/mouse_pointers/supplypod_target.dmi' + uses = VENDOR_TIPPING_USES + var/time_to_tilt = MALF_VENDOR_TIPPING_TIME + enable_text = span_notice("You prepare to wobble any vendors you see.") + disable_text = span_notice("You stop focusing on tipping vendors.") + +/datum/action/innate/ai/ranged/remote_vendor_tilt/New() + . = ..() + desc = "[desc] It has [uses] use\s remaining." + +/datum/action/innate/ai/ranged/remote_vendor_tilt/do_ability(mob/living/caller, atom/clicked_on) + + if (!isAI(caller)) + return FALSE + var/mob/living/silicon/ai/ai_caller = caller + + if(ai_caller.incapacitated()) + unset_ranged_ability(caller) + return FALSE + + if(!isvendor(clicked_on)) + clicked_on.balloon_alert(ai_caller, "not a vendor!") + return FALSE + + var/obj/machinery/vending/clicked_vendor = clicked_on + + if (clicked_vendor.tilted) + clicked_vendor.balloon_alert(ai_caller, "already tilted!") + return FALSE + + if (!clicked_vendor.tiltable) + clicked_vendor.balloon_alert(ai_caller, "cannot be tilted!") + return FALSE + + if (!clicked_vendor.is_operational) + clicked_vendor.balloon_alert(ai_caller, "inoperable!") + return FALSE + + var/picked_dir_string = show_radial_menu(ai_caller, clicked_vendor, GLOB.all_radial_directions, custom_check = CALLBACK(src, PROC_REF(radial_check), caller, clicked_vendor)) + if (isnull(picked_dir_string)) + return FALSE + var/picked_dir = text2dir(picked_dir_string) + + var/turf/target = get_step(clicked_vendor, picked_dir) + if (!ai_caller.can_see(target)) + to_chat(ai_caller, span_warning("You can't see the target tile!")) + return FALSE + + new /obj/effect/temp_visual/telegraphing/vending_machine_tilt(target, time_to_tilt) + clicked_vendor.visible_message(span_warning("[clicked_vendor] starts falling over...")) + clicked_vendor.balloon_alert_to_viewers("falling over...") + addtimer(CALLBACK(src, PROC_REF(do_vendor_tilt), clicked_vendor, target), time_to_tilt) + + adjust_uses(-1) + if(uses) + desc = "[initial(desc)] It has [uses] use\s remaining." + build_all_button_icons() + + unset_ranged_ability(caller, span_danger("Tilting...")) + return TRUE + +/datum/action/innate/ai/ranged/remote_vendor_tilt/proc/do_vendor_tilt(obj/machinery/vending/vendor, turf/target) + if (QDELETED(vendor)) + return FALSE + + if (vendor.tilted || !vendor.tiltable) + return FALSE + + vendor.tilt(target, MALF_VENDOR_TIPPING_CRIT_CHANCE) + +/// Used in our radial menu, state-checking proc after the radial menu sleeps +/datum/action/innate/ai/ranged/remote_vendor_tilt/proc/radial_check(mob/living/silicon/ai/caller, obj/machinery/vending/clicked_vendor) + if (QDELETED(caller) || caller.incapacitated() || caller.stat == DEAD) + return FALSE + + if (QDELETED(clicked_vendor)) + return FALSE + if (uses <= 0) + return FALSE + + if (!caller.can_see(clicked_vendor)) + to_chat(caller, span_warning("Lost sight of [clicked_vendor]!")) + return FALSE + return TRUE #undef DEFAULT_DOOMSDAY_TIMER #undef DOOMSDAY_ANNOUNCE_INTERVAL + +#undef VENDOR_TIPPING_USES +#undef MALF_VENDOR_TIPPING_TIME +#undef MALF_VENDOR_TIPPING_CRIT_CHANCE + +#undef MALF_AI_ROLL_COOLDOWN +#undef MALF_AI_ROLL_TIME +#undef MALF_AI_ROLL_DAMAGE +#undef MALF_AI_ROLL_CRIT_CHANCE +#undef MALF_AI_ROLL_MAX_DISTANCE diff --git a/code/modules/industrial_lift/industrial_lift.dm b/code/modules/industrial_lift/industrial_lift.dm index 35525e7ed247..368f3dbc9a97 100644 --- a/code/modules/industrial_lift/industrial_lift.dm +++ b/code/modules/industrial_lift/industrial_lift.dm @@ -1,4 +1,14 @@ GLOBAL_LIST_EMPTY(lifts) +GLOBAL_LIST_INIT(all_radial_directions, list( + "NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), + "NORTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTHEAST), + "EAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), + "SOUTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTHEAST), + "SOUTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), + "SOUTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTHWEST), + "WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST), + "NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTHWEST) +)) /obj/structure/industrial_lift name = "lift platform" @@ -742,19 +752,8 @@ GLOBAL_LIST_EMPTY(lifts) var/starting_position = loc if (!can_open_lift_radial(user,starting_position)) return -//NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST - var/static/list/tool_list = list( - "NORTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), - "NORTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = NORTH), - "EAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), - "SOUTHEAST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = EAST), - "SOUTH" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), - "SOUTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = SOUTH), - "WEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST), - "NORTHWEST" = image(icon = 'icons/testing/turf_analysis.dmi', icon_state = "red_arrow", dir = WEST) - ) - - var/result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, PROC_REF(can_open_lift_radial), user, starting_position), require_near = TRUE, tooltips = FALSE) + + var/result = show_radial_menu(user, src, GLOB.all_radial_directions, custom_check = CALLBACK(src, PROC_REF(can_open_lift_radial), user, starting_position), require_near = TRUE, tooltips = FALSE) if (!can_open_lift_radial(user,starting_position)) return // nice try if(!isnull(result) && result != "Cancel" && lift_master_datum.controls_locked) diff --git a/code/modules/power/apc/apc_attack.dm b/code/modules/power/apc/apc_attack.dm index 7a82785da4e7..3fffafe82b04 100644 --- a/code/modules/power/apc/apc_attack.dm +++ b/code/modules/power/apc/apc_attack.dm @@ -261,7 +261,7 @@ /obj/machinery/power/apc/blob_act(obj/structure/blob/B) set_broken() -/obj/machinery/power/apc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE) +/obj/machinery/power/apc/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armor_penetration = 0) // APC being at 0 integrity doesnt delete it outright. Combined with take_damage this might cause runtimes. if(machine_stat & BROKEN && atom_integrity <= 0) if(sound_effect) diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index 0cac3acbfed9..da375a77e61a 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -431,7 +431,7 @@ if(prob(12)) electrocute_mob(user, get_area(src), src, 0.3, TRUE) -/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) +/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) . = ..() if(. && !QDELETED(src)) if(prob(damage_amount * 5)) diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index ad97d57fc6c3..af8cf6f33d8e 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -70,7 +70,7 @@ /obj/docking_port/has_gravity(turf/current_turf) return TRUE -/obj/docking_port/take_damage() +/obj/docking_port/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) return /obj/docking_port/singularity_pull() diff --git a/code/modules/vehicles/mecha/combat/durand.dm b/code/modules/vehicles/mecha/combat/durand.dm index 8d3af6041c22..4de7cd72f65b 100644 --- a/code/modules/vehicles/mecha/combat/durand.dm +++ b/code/modules/vehicles/mecha/combat/durand.dm @@ -261,7 +261,7 @@ own integrity back to max. Shield is automatically dropped if we run out of powe setDir(newdir) -/obj/durand_shield/take_damage() +/obj/durand_shield/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) if(!chassis) qdel(src) return diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 80beab48015e..83d0b499e7c9 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -74,9 +74,14 @@ ///The ref of the last mob to shop with us var/last_shopper var/tilted = FALSE + /// If tilted, this variable should always be the rotation that was applied when we were tilted. Stored for the purposes of unapplying it. + var/tilted_rotation = 0 var/tiltable = TRUE var/squish_damage = 75 - var/forcecrit = 0 + /// The chance, in percent, of this vendor performing a critical hit on anything it crushes via [tilt]. + var/crit_chance = 15 + /// If set to a critical define in crushing.dm, anything this vendor crushes will always be hit with that effect. + var/forcecrit = null var/num_shards = 7 var/list/pinned_mobs = list() ///Icon for the maintenance panel overlay @@ -629,10 +634,8 @@ freebie(user, 1) if(26 to 75) return - if(76 to 90) + if(76 to 100) tilt(user) - if(91 to 100) - tilt(user, crit=TRUE) /obj/machinery/vending/proc/freebie(mob/fatty, freebies) visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"]!")) @@ -655,118 +658,279 @@ R.amount-- break -///Tilts ontop of the atom supplied, if crit is true some extra shit can happen. Returns TRUE if it dealt damage to something. -/obj/machinery/vending/proc/tilt(atom/fatty, crit=FALSE) +/// Tilts ontop of the atom supplied, if crit is true some extra shit can happen. See [fall_and_crush] for return values. +/obj/machinery/vending/proc/tilt(atom/fatty, local_crit_chance = crit_chance, forced_crit = forcecrit) if(QDELETED(src) || !has_gravity(src)) return - visible_message(span_danger("[src] tips over!")) - tilted = TRUE - layer = ABOVE_MOB_LAYER - SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER) - var/crit_case - if(crit) - crit_case = rand(1,6) + . = NONE - if(forcecrit) - crit_case = forcecrit + var/picked_rotation = pick(90, 270) + if(Adjacent(fatty)) + . = fall_and_crush(get_turf(fatty), squish_damage, local_crit_chance, forced_crit, 6 SECONDS, rotation = picked_rotation) - . = FALSE + if (. & SUCCESSFULLY_FELL_OVER) + visible_message(span_danger("[src] tips over!")) + tilted = TRUE + tilted_rotation = picked_rotation + layer = ABOVE_MOB_LAYER + SET_PLANE_IMPLICIT(src, GAME_PLANE_UPPER) - if(Adjacent(fatty)) - for(var/mob/living/L in get_turf(fatty)) - var/was_alive = (L.stat != DEAD) - var/mob/living/carbon/C = L - - SEND_SIGNAL(L, COMSIG_ON_VENDOR_CRUSH) - - - if(istype(C)) - var/crit_rebate = 0 // lessen the normal damage we deal for some of the crits - - if(crit_case < 5) // the body/head asplode case has its own description - C.visible_message(span_danger("[C] is crushed by [src]!"), \ - span_userdanger("You are crushed by [src]!")) - - switch(crit_case) // only carbons can have the fun crits - if(1) // shatter their legs and bleed 'em - crit_rebate = 60 - C.bleed(150) - var/obj/item/bodypart/leg/left/l = C.get_bodypart(BODY_ZONE_L_LEG) - if(l) - l.receive_damage(brute=200) - var/obj/item/bodypart/leg/right/r = C.get_bodypart(BODY_ZONE_R_LEG) - if(r) - r.receive_damage(brute=200) - if(l || r) - C.visible_message(span_danger("[C]'s legs shatter with a sickening crunch!"), \ - span_userdanger("Your legs shatter with a sickening crunch!")) - if(2) // pin them beneath the machine until someone untilts it - forceMove(get_turf(C)) - buckle_mob(C, force=TRUE) - C.visible_message(span_danger("[C] is pinned underneath [src]!"), \ - span_userdanger("You are pinned down by [src]!")) - if(3) // glass candy - crit_rebate = 50 - for(var/i in 1 to num_shards) - var/obj/item/shard/shard = new /obj/item/shard(get_turf(C)) - shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult=1, pain_chance=5) - shard.updateEmbedding() - C.hitby(shard, skipcatch = TRUE, hitpush = FALSE) - shard.embedding = list() - shard.updateEmbedding() - if(4) // paralyze this binch - // the new paraplegic gets like 4 lines of losing their legs so skip them - visible_message(span_danger("[C]'s spinal cord is obliterated with a sickening crunch!"), ignored_mobs = list(C)) - C.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic) - if(5) // limb squish! - for(var/i in C.bodyparts) - var/obj/item/bodypart/squish_part = i - if(IS_ORGANIC_LIMB(squish_part)) - var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate)) - squish_part.force_wound_upwards(type_wound) - else - squish_part.receive_damage(brute=30) - C.visible_message(span_danger("[C]'s body is maimed underneath the mass of [src]!"), \ - span_userdanger("Your body is maimed underneath the mass of [src]!")) - if(6) // skull squish! - var/obj/item/bodypart/head/O = C.get_bodypart(BODY_ZONE_HEAD) - if(O) - if(O.dismember()) - C.visible_message(span_danger("[O] explodes in a shower of gore beneath [src]!"), \ - span_userdanger("Oh f-")) - O.drop_organs() - qdel(O) - new /obj/effect/gibspawner/human/bodypartless(get_turf(C)) - - if(prob(30)) - C.apply_damage(max(0, squish_damage - crit_rebate), forced=TRUE, spread_damage=TRUE) // the 30% chance to spread the damage means you escape breaking any bones + if(get_turf(fatty) != get_turf(src)) + throw_at(get_turf(fatty), 1, 1, spin = FALSE, quickstart = FALSE) + +/** + * Causes src to fall onto [target], crushing everything on it (including itself) with [damage] + * and a small chance to do a spectacular effect per entity (if a chance above 0 is provided). + * + * Args: + * * turf/target: The turf to fall onto. Cannot be null. + * * damage: The raw numerical damage to do by default. + * * chance_to_crit: The percent chance of a critical hit occuring. Default: 0 + * * forced_crit_case: If given a value from crushing.dm, [target] and it's contents will always be hit with that specific critical hit. Default: null + * * paralyze_time: The time, in deciseconds, a given mob/living will be paralyzed for if crushed. + * * crush_dir: The direction the crush is coming from. Default: dir of src to [target]. + * * damage_type: The type of damage to do. Default: BRUTE + * * damage_flag: The attack flag for armor purposes. Default: MELEE + * * rotation: The angle of which to rotate src's transform by on a successful tilt. Default: 90. + * + * Returns: A collection of bitflags defined in crushing.dm. Read that file's documentation for info. + */ +/atom/movable/proc/fall_and_crush(turf/target, damage, chance_to_crit = 0, forced_crit_case = null, paralyze_time, crush_dir = get_dir(get_turf(src), target), damage_type = BRUTE, damage_flag = MELEE, rotation = 90) + + ASSERT(!isnull(target)) + + var/flags_to_return = NONE + + if (!target.is_blocked_turf(TRUE, src, list(src))) + for(var/atom/atom_target in (target.contents) + target) + if (isarea(atom_target)) + continue + + if (SEND_SIGNAL(atom_target, COMSIG_PRE_TILT_AND_CRUSH, src) & COMPONENT_IMMUNE_TO_TILT_AND_CRUSH) + continue + + var/crit_case = forced_crit_case + if (isnull(crit_case) && chance_to_crit > 0) + if (prob(chance_to_crit)) + crit_case = pick_weight(get_crit_crush_chances()) + var/crit_rebate_mult = 1 // lessen the normal damage we deal for some of the crits + + if (!isnull(crit_case)) + crit_rebate_mult = fall_and_crush_crit_rebate_table(crit_case) + apply_crit_crush(crit_case, atom_target) + + var/adjusted_damage = damage * crit_rebate_mult + var/crushed + if (isliving(atom_target)) + crushed = TRUE + var/mob/living/carbon/living_target = atom_target + var/was_alive = (living_target.stat != DEAD) + var/blocked = living_target.run_armor_check(attack_flag = damage_flag) + if (iscarbon(living_target)) + var/mob/living/carbon/carbon_target = living_target + if(prob(30)) + carbon_target.apply_damage(max(0, adjusted_damage), damage_type, blocked = blocked, forced = TRUE, spread_damage = TRUE, attack_direction = crush_dir) // the 30% chance to spread the damage means you escape breaking any bones + else + var/brute = (damage_type == BRUTE ? damage : 0) * 0.5 + var/burn = (damage_type == BURN ? damage : 0) * 0.5 + carbon_target.take_bodypart_damage(brute, burn, check_armor = TRUE, wound_bonus = 5) // otherwise, deal it to 2 random limbs (or the same one) which will likely shatter something + carbon_target.take_bodypart_damage(brute, burn, check_armor = TRUE, wound_bonus = 5) + carbon_target.AddElement(/datum/element/squish, 80 SECONDS) else - C.take_bodypart_damage((squish_damage - crit_rebate)*0.5, wound_bonus = 5) // otherwise, deal it to 2 random limbs (or the same one) which will likely shatter something - C.take_bodypart_damage((squish_damage - crit_rebate)*0.5, wound_bonus = 5) - C.AddElement(/datum/element/squish, 80 SECONDS) - else - L.visible_message(span_danger("[L] is crushed by [src]!"), \ - span_userdanger("You are crushed by [src]!")) - L.apply_damage(squish_damage, forced=TRUE) - if(crit_case) - L.apply_damage(squish_damage, forced=TRUE) - if(was_alive && L.stat == DEAD && L.client) - L.client.give_award(/datum/award/achievement/misc/vendor_squish, L) // good job losing a fight with an inanimate object idiot - - L.Paralyze(60) - L.emote("scream") - . = TRUE - playsound(L, 'sound/effects/blobattack.ogg', 40, TRUE) - playsound(L, 'sound/effects/splat.ogg', 50, TRUE) - add_memory_in_range(L, 7, /datum/memory/witness_vendor_crush, protagonist = L, antagonist = src) - - var/matrix/M = matrix() - M.Turn(pick(90, 270)) - transform = M + living_target.apply_damage(adjusted_damage, damage_type, blocked = blocked, forced = TRUE, attack_direction = crush_dir) + + living_target.Paralyze(paralyze_time) + living_target.emote("scream") + playsound(living_target, 'sound/effects/blobattack.ogg', 40, TRUE) + playsound(living_target, 'sound/effects/splat.ogg', 50, TRUE) + post_crush_living(living_target, was_alive) + flags_to_return |= (SUCCESSFULLY_CRUSHED_MOB|SUCCESSFULLY_CRUSHED_ATOM) + + else if (atom_target.uses_integrity && !(atom_target.invisibility > SEE_INVISIBLE_LIVING) && !(is_type_in_typecache(atom_target, GLOB.WALLITEMS_INTERIOR) || is_type_in_typecache(atom_target, GLOB.WALLITEMS_EXTERIOR))) + atom_target.take_damage(adjusted_damage, damage_type, damage_flag, FALSE, crush_dir) + crushed = TRUE + flags_to_return |= SUCCESSFULLY_CRUSHED_ATOM + + if (crushed) + atom_target.visible_message(span_danger("[atom_target] is crushed by [src]!"), span_userdanger("You are crushed by [src]!")) + SEND_SIGNAL(atom_target, COMSIG_POST_TILT_AND_CRUSH, src) + + var/matrix/to_turn = turn(transform, rotation) + animate(src, transform = to_turn, 0.2 SECONDS) + playsound(src, 'sound/effects/bang.ogg', 40) + + visible_message(span_danger("[src] tips over, slamming hard onto [target]!")) + flags_to_return |= SUCCESSFULLY_FELL_OVER + post_tilt() + else + visible_message(span_danger("[src] rebounds comically as it fails to slam onto [target]!")) - if(get_turf(fatty) != get_turf(src)) - throw_at(get_turf(fatty), 1, 1, spin=FALSE, quickstart=FALSE) + Move(target, crush_dir) // we still TRY to move onto it for shit like teleporters + return flags_to_return + +/** + * Exists for the purposes of custom behavior. + * Called directly after [crushed] is crushed. + * + * Args: + * * mob/living/crushed: The mob that was crushed. + * * was_alive: Boolean. True if the mob was alive before the crushing. + */ +/atom/movable/proc/post_crush_living(mob/living/crushed, was_alive) + return + +/** + * Exists for the purposes of custom behavior. + * Called directly after src actually rotates and falls over. + */ +/atom/movable/proc/post_tilt() + return + +/obj/machinery/vending/post_crush_living(mob/living/crushed, was_alive) + + if(was_alive && crushed.stat == DEAD && crushed.client) + crushed.client.give_award(/datum/award/achievement/misc/vendor_squish, crushed) // good job losing a fight with an inanimate object idiot + + add_memory_in_range(crushed, 7, /datum/memory/witness_vendor_crush, protagonist = crushed, antagonist = src) + + return ..() + +/** + * Allows damage to be reduced on certain crit cases. + * Args: + * * crit_case: The critical case chosen. + */ +/atom/movable/proc/fall_and_crush_crit_rebate_table(crit_case) + + ASSERT(!isnull(crit_case)) + + switch(crit_case) + if (CRUSH_CRIT_SHATTER_LEGS) + return 0.2 + else + return 1 + +/obj/machinery/vending/fall_and_crush_crit_rebate_table(crit_case) + + if (crit_case == VENDOR_CRUSH_CRIT_GLASSCANDY) + return 0.33 + + return ..() + +/** + * Returns a assoc list of (critcase -> num), where critcase is a critical define in crushing.dm and num is a weight. + * Use with pickweight to acquire a random critcase. + */ +/atom/movable/proc/get_crit_crush_chances() + RETURN_TYPE(/list) + + var/list/weighted_crits = list() + + weighted_crits[CRUSH_CRIT_SHATTER_LEGS] = 100 + weighted_crits[CRUSH_CRIT_PARAPALEGIC] = 80 + weighted_crits[CRUSH_CRIT_HEADGIB] = 20 + weighted_crits[CRUSH_CRIT_SQUISH_LIMB] = 100 + + return weighted_crits + +/obj/machinery/vending/get_crit_crush_chances() + var/list/weighted_crits = ..() + + weighted_crits[VENDOR_CRUSH_CRIT_GLASSCANDY] = 100 + weighted_crits[VENDOR_CRUSH_CRIT_PIN] = 100 + + return weighted_crits + +/** + * Should be where critcase effects are actually implemented. Use this to apply critcases. + * Args: + * * crit_case: The chosen critcase, defined in crushing.dm. + * * atom/atom_target: The target to apply the critical hit to. Cannot be null. Can be anything except /area. + * + * Returns: + * TRUE if a crit case is successfully applied, FALSE otherwise. + */ +/atom/movable/proc/apply_crit_crush(crit_case, atom/atom_target) + switch (crit_case) + if(CRUSH_CRIT_SHATTER_LEGS) // shatter their legs and bleed 'em + if (!iscarbon(atom_target)) + return FALSE + var/mob/living/carbon/carbon_target = atom_target + carbon_target.bleed(150) + var/obj/item/bodypart/leg/left/left_leg = carbon_target.get_bodypart(BODY_ZONE_L_LEG) + if(left_leg) + left_leg.receive_damage(brute = 200) + var/obj/item/bodypart/leg/right/right_leg = carbon_target.get_bodypart(BODY_ZONE_R_LEG) + if(right_leg) + right_leg.receive_damage(brute = 200) + if(left_leg || right_leg) + carbon_target.visible_message(span_danger("[carbon_target]'s legs shatter with a sickening crunch!"), span_userdanger("Your legs shatter with a sickening crunch!")) + return TRUE + if(CRUSH_CRIT_PARAPALEGIC) // paralyze this binch + // the new paraplegic gets like 4 lines of losing their legs so skip them + if (!iscarbon(atom_target)) + return FALSE + var/mob/living/carbon/carbon_target = atom_target + visible_message(span_danger("[carbon_target]'s spinal cord is obliterated with a sickening crunch!"), ignored_mobs = list(carbon_target)) + carbon_target.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic) + return TRUE + if(CRUSH_CRIT_SQUISH_LIMB) // limb squish! + if (!iscarbon(atom_target)) + return FALSE + var/mob/living/carbon/carbon_target = atom_target + for(var/obj/item/bodypart/squish_part in carbon_target.bodyparts) + if(IS_ORGANIC_LIMB(squish_part)) + var/type_wound = pick(list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate)) + squish_part.force_wound_upwards(type_wound, wound_source = "crushed by [src]") + else + squish_part.receive_damage(brute=30) + carbon_target.visible_message(span_danger("[carbon_target]'s body is maimed underneath the mass of [src]!"), span_userdanger("Your body is maimed underneath the mass of [src]!")) + return TRUE + if(CRUSH_CRIT_HEADGIB) // skull squish! + if (!iscarbon(atom_target)) + return FALSE + var/mob/living/carbon/carbon_target = atom_target + var/obj/item/bodypart/head/carbon_head = carbon_target.get_bodypart(BODY_ZONE_HEAD) + if(carbon_head) + if(carbon_head.dismember()) + carbon_target.visible_message(span_danger("[carbon_head] explodes in a shower of gore beneath [src]!"), span_userdanger("Oh f-")) + carbon_head.drop_organs() + qdel(carbon_head) + new /obj/effect/gibspawner/human/bodypartless(get_turf(carbon_target)) + return TRUE + + return FALSE + +/obj/machinery/vending/apply_crit_crush(crit_case, atom_target) + . = ..() + + if (.) + return TRUE + + switch (crit_case) + if (VENDOR_CRUSH_CRIT_GLASSCANDY) + if (!iscarbon(atom_target)) + return FALSE + var/mob/living/carbon/carbon_target = atom_target + for(var/i in 1 to num_shards) + var/obj/item/shard/shard = new /obj/item/shard(get_turf(carbon_target)) + shard.embedding = list(embed_chance = 100, ignore_throwspeed_threshold = TRUE, impact_pain_mult = 1, pain_chance = 5) + shard.updateEmbedding() + carbon_target.hitby(shard, skipcatch = TRUE, hitpush = FALSE) + shard.embedding = list() + shard.updateEmbedding() + return TRUE + if (VENDOR_CRUSH_CRIT_PIN) // pin them beneath the machine until someone untilts it + if (!isliving(atom_target)) + return FALSE + var/mob/living/living_target = atom_target + forceMove(get_turf(living_target)) + buckle_mob(living_target, force=TRUE) + living_target.visible_message(span_danger("[living_target] is pinned underneath [src]!"), span_userdanger("You are pinned down by [src]!")) + return TRUE + + return FALSE /obj/machinery/vending/proc/untilt(mob/user) if(user) @@ -779,9 +943,9 @@ layer = initial(layer) SET_PLANE_IMPLICIT(src, initial(plane)) - var/matrix/M = matrix() - M.Turn(0) - transform = M + var/matrix/to_turn = turn(transform, -tilted_rotation) + animate(src, transform = to_turn, 0.2 SECONDS) + tilted_rotation = 0 /obj/machinery/vending/proc/loadingAttempt(obj/item/I, mob/user) . = TRUE diff --git a/icons/mob/actions/actions_AI.dmi b/icons/mob/actions/actions_AI.dmi index c25b88aac5cfaf5f92e8620bd3a2aabb4aae086c..048793c607838893c6cd1cb44b8b50c4a95851ec 100644 GIT binary patch literal 11853 zcmV-TF0#>yP) (0003(dQ@0+L}hbh za%pgMX>V=-0C=2@l+B6)F$~Ag&QlC}m1X@p?qyLH59=$G*k+ty+CW-I=Iytxup^7o zp%jw#_isqjc2 ^*`r(WEJ zF&dBIGOZ?#H0I?+HE9$)-t9lwiTLQCHx4_p_?*v?95VFT+{`zFn8lu*TthhuCJEU# zYF#YSM@2ZBh7=7js2{5U(y;=`MP&hJJlK}Pi@g5{lpKy0s2nAcH4WG#+CS%4Lt$6* zJRUM)uxGiXVjT3SCk(bXe7g(f)N`q9qVCoWn|Bq;5+K76lZh=dkD;w-xk8y6eV_+! z#+^zbp<3}p@Hwy^I ?FSwV_OI()MA_tgA+B0xZF$?W^=o0ugF=wJ8XY{ zg&(tLI65N?F%tj)EXGMhK~#90?Oh3c6h+p5A@>0}ksJ_`a34w#l%NO^KNZ1cIaWjw z1lJV=JXlu2^;?a6>x#Optbh_lWx<05Q5IK3`3V69gL0oC5E8=`2)Q66;YjAIS3T9K z>FMt2p7b1H`mar$U9UU+fAwBfSM~Vi RX_fB$|O&(6-aZOF&a!qXREqLKXFx^<&R zctkk-Bj5yl8hjEyA;;=u3k@+nKj0)pL`1+Hx7 4AA~1)Nko2is)rqb!tZ z0wBQC7oZojvW8pK%3se~>(U<0X!$((!@|NKqW%h`^}Y$dsqY4{^|cUltxl*I*$K)6 zLZGO!1~PAmhw2tVVjFl)rph^G{-KcRA4tv#fNK9B=+U7A95j`HqS;sgBpx1-Eu$bh zGFtY)GFGp-$Vc)gL`PHdGhjS2vK3s5X#-cHT0>1llt|>@;9%AEunyM6bMRbj1KZ-; z4~dF`zCC+3x?DbtW?=ysp^#vKL4wZy_w$i9b?*P&m*5Y6v`*E x7J&Kov8f#a$e@-HI-=o Bk?yKH@RpfpK@kmGZH7SHro>*L0ubsN5TY _oaTzxLPFrt&`>BX)z71K#64W@V63C607zoaU0j z{#21zHe&QdF}`N?QZYUxDMdVwszPRl&dat(0@3Cg;%C=i6O{SB5nn)Ra$|X$HUjxv z<@>u^ehu->(LG_ry+Bm2CLn~SYpi}dmdD1%!ez+=#yY{IE}p{^pS9@>4jecj_M&BE z+`&*)RRzO`4|mH_idL;!(R;;Z96az{#=ZC63*UbGtyqS6(!YxmR26^`pAmXbNeRp- zI0J8WNPzeaYarw95s*tlJ|7i;KgS3KVDJwR{? +0$t zBt)?M()gG$V+>J{%*;$Ue*8H2pEwRFVWBjysHRpVJ%&V4X`Cn}UYDkJ9lAi&m9d6H z$O8=?GZM0<&8B&|Rwc+FfB!m?KcsV4vA!$)e?~NPNfgWN-DIzpe=cATZIMCRTVr_` zbhJB9O8%y_C5Lam`38FS> a4|s4Bo7Lih~M_s__% z_U8O}z#7L{q6|qoH#gU*i<06}gP#r6&>v@zb32k&7tzqUKZ$$gr`{>NnQhr75pv-) z)W9*jI&FHBzx|LzxOn!eZNY}d^Leng3U?R6!1SJC{+>O1mD_TFii!&8z4#Ae`u!|& zomoQu`)!Xw@xvX(dPsg2M>okRWM^mN>RH&haU)Hq-I7LKAAWfV_Wo!ow8HUw0B%;t zsd58=6nkU#%VN6pd?mDwYn<)QUNHmGdjSNIzr#=Cg^_9TZ`(sfRR!8k%fmT2P3nQ% zCI9Tn3K;yGbi4eGUty1)sOy?*i^~Z42UF7Hk_vnqoK7xsPw_?wsc8)tA<%In1laRe zS67psd=-oz|43uO%Q;j5(9IcttStmu4GyLQ{R73gkwBs^UAmN70t~VDmy#b{AGBCn zp{@Y0&h^WEakix^@xP)ZH yr&|QYe2tr;FdG1}K3e`IZHJ;JqbBT`FHILTZHGJ+S5? zTsmD0mq(XFY4U!!efKDcv d-=al}EJKIhM^eH7cwN6*p)e~G zmS=rTL&(a3{#FkmIy#!x8M-}`o^RBB;n-!~ym^qHpHGzlEdk~+B|!2&{q)l`&c*|d z;Mf8ni8rjbAgQAQ8+$z*2P|H`SR4;9R9>xYJkJ1JDDx*SM+J=|*$}J;UcX_xUHwy| zQ(zdp2Ior4;Fn8(r+G*|XG!R4`GuQ=-ZCO0nlyY?OxvI|4vzo#2HF!VozH;Npf%xf z5Lrf^RjjZ%W>iibEi37M1)w|{&VSPq&eh={g($-$$A5!q)28_iBG 6L_G}NRs=-7AU7!q3ZlxOU1ltVREO&R8w|?HvTV-G zYP=V8gHREW^k`UK$rP=DE3kO+Vq79CVpG{BXixq_OqntTrca+v 5N3d!MVaU<)&HBhyuUBU#}baYIyI@ zIq<~aGG)tfRp|EcB#0Q?lFp+aj!T28b7iu1HGvR&5M){?+5K{e0)RLcpuqSm)@z|* zj`Ol)fb;uw+LIes<2V4xKXc|xk@$M{IcQS=>sWxz0xe#%1}1hQEU+&7MOTPg1F-h~ zO>}>zOnKI_c=3Ec48f}5D7&aAj||GXY6?rNhN PfNKz(6b%RVA0&g{WH?lR6~e+oVOQQUi0jr0YD0-7sRm7B01>2T z$KD@ZAxdsbjzDs11wfc7$jTJ!+JsHSziP>N!s`A>eS1u^Ie)(FHZl%@tBz;7v|9i| z>ZPMcSSCI-8>Wn(LI=MHv;de!EL|`cx~^Vhi0_Z&i;ou+tfZt;7x^$ODai$P{uZ#G zS@9P6%!jXu`W4RZ_J`C|`=!u@g@trlGpy2n?w+mh=f?sEqe`K6zdy83PKHZYE|aGm z2-TOb(IwFZhl}B2Knc`WccFFfn4s|wg1x_mke`s+kHjsk_oro -6qpAmqn$yE~}nr}wK6d8h q@617XXSEijOT40GnpahekqPd#H -BRLGtv?F~}$S#K?{rAG8 z1v!4G2+<4v>5^$=r}hLhCE(M>U6L9mOG3l|;nlode)EvultpUp1}ecEYut}0Lk^dA z1u`BMIu29><5rZ}!s#uMNP2b{c|mfxaKXCnxR0Q7DH(-goQ5*_;yGR}pOK#}P(yb| zD{wWa9 F-33%n#G=AoWi5n`5q05a!-kI8p`C0=Xkk%w!9f#684=Q ztxABaL8bn`5?Gr`V+mh{SAP~|@#4jPGiJ=-cJwe8V*uFaN%L77^PTQJ+$CRJ|8G?S z9KcGIK&jW3BfpY0G0ATa-LpU@jKwq0JY!*Bg=4^39DX2+gq9;NeiDUO0De57rH!3C zcfvpa>6*c1bAhP<9>&6j3oY!^hzLgmVxHtu1<(T|KfYC+K7G1RD}YzKK)wKUchEIC zeE2X`0)ERQLk;)&61m-Cd_9)0HoL^g&%Q(C%P GUaQ4jRGJ^x2#{H`@;2L z;E=~1x >AFTsf5r_L5`9VF!Cc >085Gv&+70v$b33t6+LDmI)o_Z=Gk{tr)yc^|yLz+qY| z+(rKL=g%wFVSfHLh0WDC7C zFnqgi20dO~mq$Y>cw)1-T%J~V1Nj+{+}PvG9el5^G%jG!I&&m__=r}Kwt3QrjA?dA z$-#s5hSJ^-N$2bGRcC?vWD5(u-r3#@c6>M)UIg3U2;h5pIXEik;STa|`)MQey6Gl| zb#c&JW%c`akfX0PpN|9S@frD {v2cHf1!do_Vq^^z}v{(TofB*i2DM?)g?D-k__wL;Xy?QxT z1V`X!hA>2eGorINvjA%3*9x9Ad>(6~O+u0#2|oGv2hhLoAoyv=W*V}Y7gN3fMHdK( zj|UQ;l7Hx1N>wSCBB+f8 )F>Ph4Ca?A^By6uxOIi@pkRv=UMaAP1%l zj^xM@LGFRefv*?ax{Vk|GEATHN9fbHj~FV+^i}z?7U0(V{|f)PUx@!j%>AoP9jVVR z?dg^BJptzPe?PE+`~#X5z{cf~$&*@uA=ar|_U-_|UZ5eG^0Les-~(T{_#a{qIPO8= zr<|C}Vak_rf!6&f6s()p*{;E%i}J CoQ& z8wbLLg+h8a8FOHW76`u*p*9YX^MR#MD#!fGSA?%W)YjENixw@Y&%1NSPTKP!kPH 1jy8znzr*;=-hlEe05M{aTHr>;sYT$day1L8ak60jZ>QAW$Wapal zWh?*^w6H2fcIqxN!k?Bay`^o+M(K_!e)KVwX+~XOUoTVmb|sP%$&5X?mN&a{FT!fA zA5IRnetMIUpFvIul-vO=B;o5^xnlbbAh{lpm&wlcW%;sWfcQB1Pv-UL5Dw$VJ!xr| z(oME}{KO|^6=V6*cQq-79MRPjLpKQdClm7L%1nha@*|iMC;`hu72W_N@ufY#6h>MC z_8>z-f}y6S)}~CZ2jmH(#qGG><;yC-E!|tfx8=%j&yooNiU~m+g`>yKfYqO<&mKvE zgg30mmq3mD7$WH_Ro4A5?AB0csLDGHx#?abTmnRBNaHRrwI0xvFF<%;Q7DIrw7Jo{&^lP@7T4krtm_Q@SyR_URrO!RpdwF^W@)02F1L~hOP`AIA}HE zSog&jve$v+Pe^FA40>imq;(yg%X&a00){*sxgWkFU)BQfA0j$(u<=JAj^*vp6|!^j zvv$sW-p*Cl0Ld?hDk6_fbMhg8{o7!rd AKX{vyKPS}e&E!TP$NGZ__ilx9Ze1v zj+UhPkdNO*lo#WXTu9rkY#i(|@+<8haMlgL^Vlp1x&Tt38^Dg}=tz1Xr2ez7%6FRr zD1G1*2cmj@)XW)Z>G=`pu3&GChg|xT;k#r*Tn2#z=R=mygWoyED~A%;!%|E04;8`B zf=GcR;NfTx=?n6`=CdfSR01vJN8rGf_s^wMnd8>pEE}@5K1v~jjRmm&?5x!HEh;L4 zvuDpjSy>r*V0n;!Q+w4k1Dg1gr);NZLqdXxm>Lp>iQ(~rBH|mTh;n^hzSk_$%}- z0RLq*B}m`=N|!g_02X_EBt7%#S*R7v0w|q>JsEx`k?na6k7(;K%^ocFZb*WnUr*Y! z$sWEGaxRB2%J<3d0!iNiAm|P-LQHlb;n~5!dVEG;p2*B{NJAcSV*vIb@Zn2MwA_PJ zE64Lw+S~ukDvyeaGTI9=x53xt` Ev0l;~+uvPzWrr})w@Njc`Lkp+9f5Eb88Dqq?@yB;QkU5~5eOWU6#kuUxG zG)2DGxIpfJ9`YlkFRd7%+1S9D=}a6L^W;ZAm4En%ozj!mtpj-QLA$otLsfhDWo5R% zD-atSZ0!Zf{Ur*UN2Kzl?JHf6%j8Simx5i75%PUT0kn``X=Vo32x0H7W#&c=Qt~Sy z{-u|oiukyfF8R~+%Onk4`X!P^ADBJd*jRa@)YV;qTW;-P^HqerLq!lCUQeuBjYvQ# zr0pwRkE`TM+h^Cq ~PY&V1hC@nPADYL~ zd^tX3tskE<<=ex~0zCKJbCwAcCWz^Fso4+}u~0KXDyyg4^rj~Y8-GM@*|KHu=9_Oe z%a7!mxIQ5}4T?Xo#*X-?3lI`8L9>g1%7W*xgKx`(^Vqs|tJ}Y!?+cK1f!efb9G$oA z^o%vOlRkiBhx;h@;DZLu%sqAP;eG(mh t_C&T9@m1oJ0^^Sp>KknwHzk2q44x2NKFifuTPJJKG4|f ze;q!O=Cg5tv#wB!7A>3_M>I4vD9X#cFUykiO_rwXF 2Tut)vhg$oyof9&bgryF`LNNglHoB2U3zwijmu_^=t%g{waD}nW$QG$U32SP}& z@}e?1)Lp&|N>k}bz^~75g3P!(H7S9S@+tY-J^(OoD!?c2C@P|61;E*#J$v?eMGr3} zKh2x@sl`)DfaHJm)mO#zi!Z)tF;WST%uERiInrm`+#b%BoTq6V2($9042D~cc?mlA z%hXgie|rwpEq_tAjFJEAQ^Z2)Py(amQ;eJ17{0zrwu tTIoWg+>HShx4@Z*vSC zcAHHa>nIVGC;zY{XjB5uC2^&Eh913aLpe0>Es<8@+r`}kp8N>hG{fRX0m&Q{6AxkG zkr30l6Yb# +6 (dS1cKopgv!kP2Oz&|t$s*5iHo`>hJ_+*Jw?hq1fdwvZ-C!vqbJ6}uVNJJ$P z^5lP~*PQ@+H_?1Pt<@cJM!poh1vscs$E6m)(OYF_u=(rRig)j>F@RdnkKuRUeP@`g zes}zilAk9sx;yNh0EWC R}s7%MmVB0POiABkywk^r#zqe DCdXHp2WVx0UPU~;~;d7_&5rIUT@y2WL*7GmWbW}on8cv!qMROZk2BAjEN9Drt z1&%YoN_I8~XX#;awid`~3*a@&9ylxDPULJBEKbJlM=9y4VyGO>%4yvuMRaFA&iTym zrtX3h%}CEe%*S$!V|_f&2w=~PL>%?#WO^Vm9reNtjQncTkVeZ#awEy5Gf;BmM>u5x zgeA{Ucy#sWgi)= <0(XwSrPl jW#ndjAvF7i2&0DTT+M z{lF_0!)aNJ=1TeO8q{Q~Du7z=?&+T27r|U0Bs(KIQw*jQ&XV6(VQz|VM!;E~%n^;B z^ 8O}&-8EpS6kpnm8__;ct#m~x=w;|=O&S&u!_uXq(-tf)0ey+}EaZldA zX#q_5G)xOXaG=3HZ=g(mSgUZFe_ZDGXIXyDg-dq%O6@ZoktuH@lOIgvV}6m0e3pM% zB44f@md|kTntg?CGWkJ7KCU`C8Ww6-pXFbX$d_w} jEM9!$N}~EiDZO4H^U(M|AJn zRXi|Hdhq3V=ux7nIXQmpz%(QC#I;|`Dv_gnsq##9@Ppg~NjW<(%^qey1v!rLrQ{jt zKz_Fk8~oUTX-4J_tJlb$=O|yQJgi+Mc;fzTEPyMvq~(krVaZhZLpefxLR)Cpt{t2> zaRP3+wLks%EN+%gN)!y(gzOJtYx+%M{oD8KgJqK+qw!Ox&%)Hnli^Tao-?8i1uB9O z*1%aH5sa_~&H@#|2y5Uh)b8I#0gV1WJd*#}TQXq2lS+W(KXc}c7$T6=|M_Z-Nd6Tq z1S5u;`w&TEJ}Lp$+q-X{UXhv*92zVeSA$+xN)3UW%s`Fu^|}&j2(VgXX krqG;(X}drGtly&wUWUZXd${*WpD s4r zw#Vp+HfgM*q$oW3>vD4hB_L_c*K=_^`I!a4&*Y(N^v(bMg}OXr8ydu8!LB_tAIq_x z9t$AXLnEP`CAHGIN|r#bhr$QyEUA^wRq6@ldT1oHv!qr!SIH81>i!)Lf~#JBO^949 z5#lY?zd0#KerJ{7m!E$U$$#=hk(eJ66a*>l)2ORc(66s}U>ftW+?5Cj3ZREH?k@Nf zdHz I`CzvtT?ag(qK8BqZh1K-acZOsGR-QG|n*WdT5kIj^ukMjImK>M~cAOms$bV zE0uHdV>tO$M84cZz9^CJhzP8GX)nln<-i9=itSzf_m`slkVnT_ 5d;`YjaX`)QP~*NyNGv90eXAr{J|hxHop zP=j7K%ANPG*> E2k?C^)1 zR%Sv 1ePFudr8%8bdE8(cMJWwM4xPtsbo$`-5sGoOD5EM|~0PXqnuE}4((RzKe z=6MS3i0ygc{#9L|tVMGnYr;eb>CqKNkb^>j{0uy~r4~S1?g*u&<*<2DZ%Dv6l*7`G zmOx@+A|$nKN6T@Hz~We!m9OfX1gjv4WR`0cr3nu#UnzU-qyM!+*X)+k3p7XQr6B)C zo${4lAUQ(Yk3Nw-FKLOsU!_!ff#wLVxPN5@AVklaHk&E|B0XLh{Niko0LRmddoET& zddnyp>IE)gkiNx@ijGtD@#V_(EQL@aCK7+u_%U|<5E6ei@sIVizjXL8#rjIb#0S4u zMm|3H{W|1h``cCID-jcke{#||yYrCvryke7^%C1JP?-er#Qi&TfzSfjxIGRm01rlf z40AM})rh99<8c)l8X9EdO1NBm*`xBW8Z6&Z-yj=T!Zjx*Ce&c${o5&kIKQvqWf<0l z{mPbr_599Q0CxhHDL8sld+o|KIvaHJXJ6PUCC&!*TDH;;>uAB2!I>6*KLEa_cl4&V zp0tg#A(1ljaW #ts=vq5z-?gq{V)xQ6+AJ)+V+#kU2 z2k_+m+bKYo-Mck4LUFD_wRnAg9t`Q+m4+eOTlISIM8|l&o0k;w4;teQ?v~FH9pe@^ zUx1PKZ=(QPwrp{#5jiu&W`c6^1oAt(AkJNoTdXE^U5-d*C)Dzl+#Zy30BrXI(E2j+ zrKY<;LB3qRlG#T&N5FPJ077oDn$&eUBAFdh%U5!HP!9fPUN=d;r|#cI0n}D?Is i)fYLyWtz#kzMC{Zal+uoOJe CH^g`n zMz5XMyBFZ9!SeOqPyK}&L*?t;3*gTCH!T1Me(x^&=vron LotEcNKk9EwdwX zU~fG25})6@i$1!R*WwH@ u;Zr {5Xx#rZC6 z--HQ0kX;IK{)xBXaztxG1qD}^Lfm^Zbe(f+?{0ANOewTY5Uz!fD?zS4hMvGH|M$Ah zY}U(vnRVSP55Ha@s{lxTB&?Cpqfk}l=V0sR?U34~15NX@L0V>pr1cAr6wo%^2_%1q z&YdA9E}Evx&sV_XPdsT^^4`0q5_l186aanmjDf?%^h*zQRXnj q$ar`^y&pW^p}TA)p4^9D zTOfqivVvbXL5KEtifLN^g{SP!V~8F-3En6;1Yz-=Nw2K@g^9u=$029ekIvkaN3mt- z7)ysoAA)Y(I>CSc^EJ(5%7A$YQwh8WQ~}Nu?twQ*`22=d0O^D5Dj|uH%wN3pcS?93 zNPcM^yaq68&Y$Eh0+I1XLMman`pY-$wNU}?c;s1qcSP{R$MfylDmv<#-wNP? e1N+jP^|d<>kBB+55onT_P^W! z-D3s6J|H3x4waQceXw %oxt^b__C5gE^ihQV@T0nVr>0a^ft7azFIuKd+i z+f;At=#hLl{q0Tg(W)|=wp;~9{^-~kxHNkXG}P9@l1*RHULJdV90Qo}K4`K4h+T)) zLlNP7On4$(K208`KKku+4a2@Nma3(K>Zu z)4Ac0DfcM>1Hs6j+PMRKxqd% !?Ld~((vw% z(PBPYfX#1*z{ERT``c7}y-QobSJb?04Sw!_b8HK(qi*2(Ww6TId3{7J&Kj=+1 24fu&_Iq#v_DYT96koHRyu46r0)EyRr^767zafa8V5 z0EF0&&75hUPtaw;ML4qTUJ`x_KhH=O^W9+qc=B)h<)k9{`2nvv`1LM`V}O9(2V^Y( zL)Aws;nKxQxFND9^gf24b|OBI^_N(pf+8XPEQy@8&~mH?q&_Nv6wCr#+uc)j48S9M z!(PR*0X>OJ@x}@uW{0>}jqU|M1}JiY5ztD06W$Gn765_dKXa-Wy8iE (4U<;J}oC;U2w1cH@9fQyNmBa70$H2rH z9cj7$p&c}=@7`BD#}yX9B>(jXhZcZA`c@ao|5Jw(5ZvY>4S8i?FV5t~0D3I|4r-D7 zv9}h|I3quXnEs%DAWXgO5 L^%u zt(b #jTeIOx^<_%_E+owpUvYJ)jyJ)R9-83%opI2*6hgXwAAt+os^g*|V%|F@ z`L9E0UJJyNnZ=Rxj_TM!rv+fpyPn6{CD6EXlda@rt2lqzww X>=H9c z?>n$j0IjZxJHf0#=FI~%4{TYl?~aWyCYm>>0>rgx3;hNw&j7gsC{t}uFb`)4ZIa)F z7ElHF?#m?}5w7-eTzBl@HDQ7(fEp1!ft|;Xn?AS+x8VN)@`*fE4M%Yf00000NkvXX Hu0mjfp8@Z# literal 11221 zcmV;`D=O59P) 005u}1^@s6i_d2*0003pdQ@0+L}hbh za%pgMX>V=-0C=2@Q^AVDFc3YrUs3cbqE?T4S(L@Y`U_#4Ol>g9z$C5o?>DZnl|@Mj zftlpJdCZ$+UVR?Ej;Ct>eJp#DVAxk5)pofmi<{B0O-a2ei k?Q*V%7 r#&)zPCrFq3;sg#PG?>nPW#{g`vZ~s9)Nt(^@9KaDqcxM zK~#90?R^Pg71i1H8?tXCVKIRO2p|fG5(O)$g-EMXZ7EBOSPSCPf}$1br=tC )n0AM|^J5F3QTvqHkY#p{RTL<(CVGw8xTmnbtv@+&*MMW?Mj#*B9Ux_skmQv&w&X z*4t72i;lE>nS2=;8I;w0f_j~Q9=+S#k2*FtQioGbYQda7RFjlORkaONdPV`&w@cC5 zK%G*HI)@U|sWdTJsgp$Yi79kW@7}bbjgauP(SUgHa5>qzl$Vod=#a T%W6JJjoBS&{kH8|dL5}oVPU@kRNr(`gnbH;A=_)5HX0Bb z1`ofb#Yf${*3b{fKBMLRaNIN0qp(JVQ3fP9UF1VE`TSZ9cHG8In?$&%sF1RYx(f#a zok-6T1oNe&1k_))Y*|1YWl$D%xIWqf&^BbWC7d<{WQ@N0FqM5sv|)pyneKZ=xDATJ z_;5MVsPEOBmhuyh9~Z>nk!f!Co|fg={+lpi0*x6nhRRly1=xf};5H!xvLLf9z)=@G zq^#VvO;uhxBr1b}l`Dyw{5JL$zX{XQqNc4{rN-N}i{3aS8uFn5*|zK0^GqUTP{D-C zYB`~mlSzibME#*df=oOm!}+rQmFuAGBbAjxRz2i3G`PCXXuy_$AYW5?8Ks@m*Kwmw z8!D)zyv&z2q%`70eCp3PY2k^J)oyV6*48xGV$npb<%2-zPLCylpw0+`vZ#}5qZQ?W ziC3`ff=lMi@zLlXsFCaf=gtohH(FTuw`XXb(qq9P9BV{VKD3(^_8&jxx{h7GSFqK9 zO*8K#Xfirl?X1O&G~tOlAaCt~FfOlk1PbFHqfBTRj6nbK59N}MsAUkI{8OSUN4>6% zQn_xpBVn)PgD{xiGfL0MFeZ~#owf=~0)j-)VywxQ?yl1EMvt4Uh2MI8p%xxlSgh3( zR4g45_C -=NrL=pLCkM-=KeB&CUX zgWqmLe#ed->9}q%fikH|S=5n<$8Cm!_3PJbV}L`7-N~q{tD{k)Mum0LmIP$tF`?E} zSJRAbd+D*>U8rE$TQubI(NwPZaykeo*NFrH8Hq{iy@CjpJMyScKc9H$l{;Ygx8?&p z9~VhPQ@%_j5JgWI&X?0(dg&$M<5iqlTYy cuRuXcEO@6_4^~0A2fCR7@9S0rbsLIlW>S~|4mBz$g}!t<)dl; z@q S4mj(`%KhJc*Ii=r{P&M6r)z$4CUr0BPLKS4IZe1$4ZrfrD?Td{CRnb9`PEF{kRj$8 zth~IO?DFZsIbqvi?G `9ow^mj*qLM1Kq!%OFkb xozX>1OaHzjG%*)FYWv==xUDUT~-(CZPM9Y@?K&ss2 z*1vk#f%6y5*X%(??aA8KdJ({pLy1b4cUXk+umRRUOP75XRDSokV!E0hqWuRB(f=O% zgGj@}50wPbmS6dysx7jz@|1@6Y4HFY=tSH9d4|wLX7w4EhH1#`L^+3)XVp<%oI19w zlgO*?e}bqcj}E?@P5YZ*q$*^*!TvWu7dl+I{uO-vN?7?Pu#iH+PlaEm#+NAnE2UT5 zbB|@)j$Xw gdry^MjzMu>CF{>7;4Lxc|`^RPe?Tsvg@u!2i`3@6&(Z zxR6$#tH`7mMA<~2`cAgriJ4S-=Nw )&8_*(~3R(pjzdf^HZ@1nHnye^aVyL#J@Q97|{GVFX-#q?QWW=ZcYVG*N#z ziMH(TL0j-Pa3lHsCR$hARgB2CZJ488Q+Y3c4Q0@f{MFY+z7@;{A^=v!e3n^AStGKk zef##bsZ%ei+keQgtRpDI8cwA?LAHc4g@B~lf&vKt#(yosFSBjZ*=iWrf(qe;wo@Fs z-V$YGh&ey_h;yDO7iRZ6b?S5<`@WMs2o4&!pdeTBOBDmTK )dQ>P&l@R8q}T%cx<@-gB8@6>CD84~&KU=bwLG$lWW(36k4WmH_>b?2q zn*kHja$EX$!(Z&!9njXKNs}D?E*OL`(S|Q?p%o)^ohJx7Y#4p?(ML2)@fovc&kl1G ztOH2KnvfAm%@>F!N>5Lx-n|FVYp=bg#1|-Tir+s+ahEFYKSYBrQEagUe>#aWmtJ}) zZQHhu8XJ9KOh(5}zTI9Av4+opfOc&eEktnB76aWcNXR6k5v4(-vm=`kLfR9;B$VT! zwBsIAbUtvY`f4aB{q&F@&LV+W10 Od0J>YJz-kyb;I`c?MleHJgeKNci#;KjLJ(N>l<=y`cWYtc z8>DM>+PHjPUWG1^_vtz%Ay&ia?LRYcn`XA8S7_7QGUv~qpD<&_3~4_KcX5|eaGE}y z+n5{jUuvx6YwQ2~B*XwhPeL=TWkfzRZQ9nz2e=*u#}QfFamO7#J{5t1QLH?W0HKVC zgDsgd0cCsANgJzHt)f5u=}(@X4HGAz$9Uq2CwzP~2VgfKkx8Z}pc{aEIHc&d+ivre zfNi=9{w4t3ExN`{n>Gm&%1iDU4bt{Wqt()KIZn44T{HQ3AF`K-6R-_HCLvH$<9vg= z{$vCzbm$O#Je${u6VPLLeLmmwi~ix8H1P+XTmxjE7Sk}_bWE5q!6*BXM6cdw3L-?h zT{}Q#US7wxtpRVn=ULyA58oKpUuPuRXbTuwTI$O!C@>}AOE0|?@;x#ML5NtR&5nXW zoIlUKmu{K<5QR#hNVMTKK)BlKYPxvD2>N9Ca^dT-apOkeheE=)-u~Fvsgr6OZr}DL z6?N|s<`vsx*N(3UWY6o6*Y-3ZNW Bi>WO$@-(F4o0d0zaz}B6OG;8LqrunnxJSqH}es;T-_RL=%w~aeRtjKrp z;6c+eZ0DiNH3DowfXRn)oF=#7rc0Jd$Uv9uG!E@)1Q5|m8@Aa|aA83zy|-kBa1So2 z5WXHVu{dm$<`iv#d<>8a8qdhdTF=ZbWH=o;>&XYS&k0P^lMHcodUDEOZK&6NAib>y zeC|8n3Ck9`;e!uCHvR`KP+#;v`Po2P`Ro+>6$PGAC~G+**yYQJ1^GVtbU6(??>yVG z7 yl^#k 8=;W!*=Ln*_*wkuAmLi&hhIQf(qjQ-^j)nHoM=IiZ|Kkw zf+W!bG#`_1?b>xTaG*UA?I4eg;e%jIG!C-{v?8BVFlqE@+(v$v!XOeZe)$<1I(RsJ zx^ji^q5b4`Y&9UrcfIoK9XR0roWCVmS+pp`#g8lf%bW)Lm$_eg)niu0H6p^7yJI y}CK7W9NJ= z9b~+1>Muoj(BMJBKYX|@K&InC2o_jkNe|v5%D_sFE3dqg-h1yoEe~noth)Wy_kT-& z|GpajwU+wZfHHcUPfs$lbT7a*-v`55lz)g!4%G@mK$mNfrq_TY{ZmIgcL?wVHW1Ea zbtnMoo|ykfO#@zcje3+Zclm&00%HFw=6@1*Zl~f5bys~=L!(AI`0LlJ-?tbwY81|m z3+bYl{71d8CDYCd%DZab7A^mD!;p3Bh?M`uCT`GVpnV+>ZKXk)43H1gG3O6$)#8=7f*#AG`Fd>_8(MP!F?wq#r+_?!IemYM^%Q+6kL))yh=?8W7q~FUK(f@x}z( zF<1O`dq8@xUcE$K_wL XdB6n=7U1iS%Dt;M-|NC&qU)7c-a`99Q-%r Jf-cp3K zQRYkwJL=0w*@(D0i?dTe4v+~N%*mS#qlxH7|G`!1{-a-*d<-KJn%R0z1}Jiw6DI=q zARB2wS*9x+5CGAz0UeqUKD3{neCT>{pkX*D=wy6k#5uh)Y2tOa__`Gve`0Fl @Yd$?9sR@}#X=$m{(9jr= zXQTmT!aAA x->N&w7Ax5z}}&SdkAtNLOp|-k0&YgECiH%xM};H@TUPu|j%U>ndz3 z4ajiyCIsZW_r-f@@mGt5g9emG#ZNr(gz%S#sWqOyCxzCp{UqW1^9R!1zn&``!gt>H zsK`IF?_fv%kvjE C&nh zoldnFm9?wO8W2Q;FC!!yaQ^)Hk`+w!;h_(y2dT? B{H2d5`}o0VIU)<~pLK4M9F5)G6|Knnm`Bg5QRir8CeCgM_js)RT`9j`ox5 zrlh3MsZ*_|vqy9k3}AjwXgo=iLJNX?ciwrYM!ZOVhjxc#3vt-3?-Y7_FIwa~Ptk;C zBphtwo8{}%Wq^E4LMC5rZEb*U$Ycm*8b*#|6x`C%BBo#%cq3wE13Dgr{8brRUFF|X zs<-VB=Q%$)@8Y~XqLYEAce;hs!9yevt*%!Flg|oz4M!BL5v{bLUK4_RpM3I(7 %ou&IGEGg?0hYi9%65)kim@j7ma+waUO)87Jj)T=q!{f5)%Yauk1@-xuvl+K$O*wtg_wZkX#~BKp*+ zX2lOY%pTn^Lw+6SXc}g8f2B*^xIX0fxwzGER>MRRvRx?y1A%D24|byY0Cbo9+6-Ds zA74CbHC^}i1wp?2I!^D)HXyH@W^HJPyu2L8@#=l*S^;?!tOv-*Bm``yVW2f`c)Tnm z1O9YFxFdHfI3XVZ(#ZCiekuiIGxC|FW 7lf=6h%yf;%8|7_H9*)ZCLC~1G3iey8DU+tiJdZE*czs7+U}4L;rdW z0|-sy`E5G(okG@tMu0}c0Fw87WM&oF#+g{G-KxIY70@P=PY))&qj^BH?kkSapq~Q- z#0qGI80 j`N@P=LvY`b(v202x01&%d>Dg|h?CJ^jr}b)3zF zo2|=7x)oWW1=CDy!#1}m>FW$P K6zfP~s%T?^oim0hwGKI~r@Zrlu2g;kV8S*cDf?v5GP?n-%Te5GNpmr(&Wl!z$lu z`4#2~!`*i7lzcM!_Aho62dSXh%%I;REO*Ck_Gpf85=O@06b3W$tz5m$w|dn|K?-QJ zVc+g4j)}vHrz=KYOX;Z%mT|yr-!BtS2FfyF`=8T4t^wNu+-jysgUN^PvQg<(JD$;( zhAK`aRwfnq8YI# t)|{*Im9zlUl!D)vf0O%FTMh z@%7c#-xi?Fc2>3?V*dQ|&(rUJ|NHoqmV|a*pAYA?d?Vn1Z*~Q_M<^|6lH= T`!bv`Uch%T0q!HQ4ascb4zo|C5V|?$FX|j^vva zk3&Ji^j|*c>G^Q-yGSqOUwU!Bc$erQ0tO*ooDbKQ Sm!#6AS&i=Z; zHjgO8t2lXWKhA#}GiD53fBp52;%4B{kskUJlG=iN86aO>-3P)q$}cG?Dc6@*{mn|f zYlJVS$6*ak1A28nTw9UPfaV)M{5z%^aLt-EL-gfU|7@k+%bErZMT345p)E%6>U_Ai zB40+R $J|E5u`tDFqJ}>1*3h{2&J(9dWAI@v}kwm=Pbq~!3 z^y+-Lwj%#@!|F3A!s*tPuVcS6tzkgkfnw{myt9^7(L9XM>#1@;!&2ctPP9K_(x7 zMYC>?at@k^mJzA*;baZOkY#!;KbnZz$hcw$(el-;aXB0V$mHY2w4t0M9<~k04-)6Y z>0zXS^ykB!?((f*_f{notaxXpsevD>frypnga7{f?>lbGaugg2+@25DR^&$mc5hW8 zh8Su;kZ;$XLv;38kJ68RrTzh5cinZuXYx&-KAq;yo$F|1+|KLs;k=d~EwI*$2Kh8C zh;EqEVmJ*`{;_%{ e|tAM4TjRA}`K|V|Xp!Eka$g6>&fd_6`3F1#STn zmJWZxH{_D7^nTYGSCh{&!rqh=?)h-1qkMJA)z3X$|2JuDXDytaokZh3xP~A7So!T0 z4Hqi;0L#@dPJnuRO1i!a1keq#!x0dCCWyW(^jf}Cux*F6e{$R_WM^l4CL6>HhkStL zG>nt;;pB%3*qsk2!;S=A%QpgdyE!>82gGBIn{K)(0dD;G@xpm=4Us^=o)F-&;pb`R zzkOO>K{wqv)-ulE^WktvHQQmGJLM5?dgf!i)SYm}v~_Ex;b%f)E#GQSh?Sk!W^BOI z903}T&xc#HW)1!8U;h$mc5yzK2HK^lFz`S`!0vpwwj^H Uep6R`S 2;lqc8|Hu0$3bH}-$v&sY5r-SN(tu2|%00(O)_{n|5l1L+ zYy%$K{U18_%v}27%UZhn*qby+sfpu$@LsmN!oPPi$3*Wc 0#qiDZ3 zwRC7dq;nVj%h`)U8Wp!|*Dj>LxR#a{Q+cJ|o8>Eecb22;iL`vk=CG4%h{pYwjT`MN zef_PFo<~8KBI?$y8|~b=lP>($P;vOs$De*mh5l7UlNLNnAD6U#4gQif>*)C@H;VA? zJ^Sd^DN|@;MTKG83`Yb6u|P1k`+!A9eoC|bganX>E*e@A od&$530e?ByDsiNPgebRm-8x$tjtB@cF$pUr(ha$w2komqDB`dUIX_el z2(%x6Kb>_^siU%6J}aZ9MZYr4WAeSTThY)iNEj*k0w5pz55MZD&Faf8qoup69F-ke zSWLM~mm20V`O=1z(sU&r3JF2FE?v54K0Z+R