From 9f813bb6995d56888d57205cf6c4fa6ae07a4375 Mon Sep 17 00:00:00 2001 From: AlexCube Date: Fri, 16 Oct 2026 17:37:13 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D1=80=D0=BD=D0=B0-=D1=86=D0=B8=D0=BD?= =?UTF-8?q?=D0=B5=D1=80=D0=B0=D1=80=D0=B8=D0=B9:=20=D1=81=D0=B2=D0=BE?= =?UTF-8?q?=D1=8F=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C,=20=D1=81=D0=B2?= =?UTF-8?q?=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=20=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BF=D1=83,=20=D0=BA=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D1=8C=20=D0=B2=20=D1=80=D0=B5=D1=86=D0=B5=D0=BF=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Модель (бандл necrocinerarium, BlenderWork/necroCinerarium/make_urn.py): тело вращения в чёрной глазури, по четырём сторонам - вдавленные лицевые части черепа, наклонённые вниз; руны старшего футарка кровью «SERVU ĜIS LA LASTA CINDRO» («Служи до последнего пепла»), меандр на ножке, бусины на шейке, пояс черепов в двух линиях. Руны тлеют красным, глазницы светятся фиолетовым, только когда в урне череп (_EmissionColor через MaterialPropertyBlock). Своя иконка. Коллайдер-тег - DecorColliderTagPatch. Дух: выше на 10 см и на 7 см вперёд в своей системе (хвост не вылезает сзади), necrogenie fwd. Рецепт: + кровь некроманта, копия на соке розы. Co-Authored-By: Claude Opus 5.5 --- Config/blocks.xml | 9 ++++-- Config/recipes.xml | 10 +++++- HarmonySrc/Cinerarium.cs | 32 ++++++++++++++++++++ HarmonySrc/CinerariumGenie.cs | 21 +++++++++---- HarmonySrc/DecorColliderTagPatch.cs | 2 +- NecromancerHarmony.dll | Bin 427520 -> 428544 bytes Resources/necrocinerarium | Bin 0 -> 418561 bytes UIAtlases/ItemIconAtlas/necroCinerarium.png | Bin 0 -> 23216 bytes 8 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 Resources/necrocinerarium create mode 100644 UIAtlases/ItemIconAtlas/necroCinerarium.png diff --git a/Config/blocks.xml b/Config/blocks.xml index 7de1efe..4c7ace6 100644 --- a/Config/blocks.xml +++ b/Config/blocks.xml @@ -518,7 +518,9 @@ Составной блок, как земельный камень: TEFeatureStorage - сумка духа (окно хранилища 6x2, «Открыть»), TEFeatureCinerarium (HarmonySrc/Cinerarium.cs) - череп (положить/забрать), показ зоны 5x5, -1..+4, лимит ceil(убийств / 500) и запрет пересечения зон при постановке. Всё - в радиальном меню на E (решение 16.10). - Модель и иконка ВРЕМЕННЫЕ - кастрюля игры; своя урна будет из Blender. --> + Модель (16.10) - своя: бандл necrocinerarium (BlenderWork/necroCinerarium, make_urn.py): чёрная глазурь, черепа по + четырём сторонам, руны футарка кровью «SERVU ĜIS LA LASTA CINDRO», меандр, бусины; свечение рун и глазниц + включает TEFeatureCinerarium по черепу. Иконка - рендер той же модели (make_icon.py). --> @@ -536,8 +538,9 @@ - - + + + diff --git a/Config/recipes.xml b/Config/recipes.xml index fe285b7..681f293 100644 --- a/Config/recipes.xml +++ b/Config/recipes.xml @@ -606,12 +606,20 @@ - + + + + + + + + + diff --git a/HarmonySrc/Cinerarium.cs b/HarmonySrc/Cinerarium.cs index 7511c83..6311ef4 100644 --- a/HarmonySrc/Cinerarium.cs +++ b/HarmonySrc/Cinerarium.cs @@ -245,6 +245,7 @@ namespace NecromancerTome /// _sound - череп только что положили или вынули (порыв); при загрузке чанка дух появляется молча. public void SyncGenie(bool _sound) { + SyncGlow(); if (HasSkull && _genie == null) { _genie = CinerariumGenie.Spawn(ModelTransform()); @@ -259,6 +260,37 @@ namespace NecromancerTome } } + /// Руны кровью и глазницы черепов светятся, только когда в урне череп (16.10): _EmissionColor через + /// MaterialPropertyBlock на рендерерах модели урны (материал общий не трогается), духа не задевает. + public static Color GlowColor = new Color(2.2f, 2.2f, 2.2f, 1f); + static readonly MaterialPropertyBlock _glowBlock = new MaterialPropertyBlock(); + static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor"); + int _glowState = -1; // -1 - не ставили на эту модель + + void SyncGlow() + { + Transform t = ModelTransform(); + int want = HasSkull ? 1 : 0; + if (t == null || (_glowState == want && _glowModel == t)) + { + return; + } + foreach (Renderer r in t.GetComponentsInChildren(true)) + { + if (_genie != null && r.transform.IsChildOf(_genie.transform) || _bounds != null && r.transform.IsChildOf(_bounds.transform)) + { + continue; + } + r.GetPropertyBlock(_glowBlock); + _glowBlock.SetColor(EmissionColorId, HasSkull ? GlowColor : Color.black); + r.SetPropertyBlock(_glowBlock); + } + _glowState = want; + _glowModel = t; + } + + Transform _glowModel; + void RemoveGenie(bool _sound) { if (_genie == null) diff --git a/HarmonySrc/CinerariumGenie.cs b/HarmonySrc/CinerariumGenie.cs index e8761d5..83124f7 100644 --- a/HarmonySrc/CinerariumGenie.cs +++ b/HarmonySrc/CinerariumGenie.cs @@ -22,8 +22,13 @@ namespace NecromancerTome { public const string ModelPath = "#@modfolder(NecromancerTome):Resources/necrotechnologist?necroTechnologistPrefab.prefab"; - /// Высота корня модели над низом блока урны, м (кончик хвоста в модели - на 0.45 м). Подгоняется командой necrogenie. - public static float OffsetY = -0.25f; + /// Высота корня модели над низом блока урны, м. 16.10 в Blender по модели урны: -0.15 («духа чуть приподними»). + /// Подгоняется командой necrogenie. + public static float OffsetY = -0.15f; + /// Сдвиг вперёд в системе самого духа, м (16.10: «чуть вперёд, чтобы хвост не просвечивал сквозь урну сзади» + /// - загнутый назад кончик хвоста; в Blender 0.06 - ни одной вершины за стенкой, взято 0.07). Поворачивается вместе + /// с духом, поэтому хвост всегда в горлышке. Знак проверить в игре: necrogenie fwd. + public static float Forward = 0.07f; /// Поворот без работы, градусов в секунду. public static float TurnSpeed = 6f; /// Покачивание: размах (м) и период (с). @@ -203,14 +208,14 @@ namespace NecromancerTome { return; // замер как был } - Vector3 home = new Vector3(0f, CinerariumGenie.OffsetY, 0f); float yawTarget = _yaw + CinerariumGenie.TurnSpeed * Time.deltaTime; + Vector3 home = new Vector3(0f, CinerariumGenie.OffsetY, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward); if (_visitLeft > 0f) { // Туда и обратно по синусу: на полпути - над местом работы, лицом к нему. _visitLeft -= Time.deltaTime; float k = Mathf.Sin(Mathf.Clamp01(1f - _visitLeft / VisitSeconds) * Mathf.PI); - Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f); + Vector3 over = _visit + new Vector3(0f, CinerariumGenie.OffsetY + 0.6f, 0f) + Quaternion.Euler(0f, _yaw, 0f) * new Vector3(0f, 0f, CinerariumGenie.Forward); transform.localPosition = Vector3.Lerp(home, over, k); Vector3 d = _visit - home; if (d.x * d.x + d.z * d.z > 0.01f) @@ -252,7 +257,8 @@ namespace NecromancerTome public override string getHelp() { return "necrogenie - show the current values and how many spirits are on screen\n" + - "necrogenie y - model root height above the urn block's bottom (tail tip is 0.45 m above the root)\n" + + "necrogenie y - model root height above the urn block's bottom (tail tip is 0.47 m above the root)\n" + + "necrogenie fwd - shift along the spirit's own forward (keeps the curled tail inside the neck)\n" + "necrogenie turn - idle turning speed\n" + "necrogenie bob - bobbing height and period\n" + "necrogenie alpha <0-1> - body opacity"; @@ -268,6 +274,9 @@ namespace NecromancerTome case "y": CinerariumGenie.OffsetY = v; break; + case "fwd": + CinerariumGenie.Forward = v; + break; case "turn": CinerariumGenie.TurnSpeed = v; break; @@ -287,7 +296,7 @@ namespace NecromancerTome return; } } - con.Output("necrogenie: y " + CinerariumGenie.OffsetY.ToString("F2") + ", turn " + CinerariumGenie.TurnSpeed.ToString("F1") + + con.Output("necrogenie: y " + CinerariumGenie.OffsetY.ToString("F2") + ", fwd " + CinerariumGenie.Forward.ToString("F2") + ", turn " + CinerariumGenie.TurnSpeed.ToString("F1") + ", bob " + CinerariumGenie.BobHeight.ToString("F2") + " / " + CinerariumGenie.BobPeriod.ToString("F1") + " s, alpha " + CinerariumGenie.Alpha.ToString("F2") + "; spirits on screen: " + CinerariumGenie.Live.Count); } diff --git a/HarmonySrc/DecorColliderTagPatch.cs b/HarmonySrc/DecorColliderTagPatch.cs index cc4d901..2ff82e1 100644 --- a/HarmonySrc/DecorColliderTagPatch.cs +++ b/HarmonySrc/DecorColliderTagPatch.cs @@ -20,7 +20,7 @@ namespace NecromancerTome static bool Ours(string _name) { - return _name == "necroBookMindPrefab" || _name == "necroKnifeDecorPrefab" || _name == "necroMortarPrefab" || _name.StartsWith("necroBloodRose") || _name.StartsWith("necroPoster") || _name == "necroBookDoorPrefab" || _name.StartsWith("necroSkullDecor"); + return _name == "necroBookMindPrefab" || _name == "necroKnifeDecorPrefab" || _name == "necroMortarPrefab" || _name.StartsWith("necroBloodRose") || _name.StartsWith("necroPoster") || _name == "necroBookDoorPrefab" || _name.StartsWith("necroSkullDecor") || _name == "necroCinerariumPrefab"; } public static void Postfix(Transform __result) diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll index 6c2356752483a498b8b984cfdc38e88c166a5fcf..beba47cba9c7f4c641eb7102ae8a2e4f806e6eb1 100644 GIT binary patch delta 175682 zcmbTf349bq+CSdYGt)g+5+;+(B$JaPp_wC)fDq0E5EPJ8!30peKoPaEN4$p24Bi)b zlo~}{5p}&+5nWyH`&zt@bv;nmW7qXwkJVj`|L^lu&w={Bpa1X82fC}Cdg`gCo~nB4 zsj8=Xa_2;I7e>!JW6n3@Pxw@^|K3%GH2WQ+6y+pMQSgucW?qFrGXf7WU=W8@MR8Fg z{_<}{(a(Q5S^lprnlhi@K!3$~{@_sHT}=rf{_d5UvX2<7CPc4exbOH4Hr12FS&pGz zATyy;#2+0^UgS_vf^vq~=@?SmN(|s{PyAOEw{rLUs$$&dR2==mQ?zm07pP9Bqf6}L zp5!=RobTSR{9skls*Em0@g*vagT;^TZH}Sa|De@69V@p#B3o6B z=S5YPV?W`ke$%mH`v=w6JMh{Vd8WSZUZ>(}Nk&>T_aHE!xm=2ty2+vVT5|Vpx5m7$ z)`5po1UDf(#t8RqUpww8XWavNg!`F-hY;Rmga@|oJ+ay4SSgO_8tGjLh7$qRXc0Ge zH30TZ*M*LY#ER}{+c{_jHEf(oFXL`JGe0A{2{HJRj+kmV5%6io#YoYNE5vo(o6C32 zvVH#4s~i>UAlxb=MiK#?v&GNT<_;2o_4FN%r&FzT825C*GYt%<=o}?%jo> z{8*McWNw@R`eMtBTJ0 zO0~}(q^4!HxS@-91T4Xu7<6ThCeTO-jzbv>QORRcH%0**cW2fTN*xI2(Z>Mpjr5dx zJ)2f{t^E1tpH;n&>Y!?URD+lpG_%`>+N;W5V0@xj_;pQFmE9tKO*iX7YuyKm;%G^_ zTC3ukZZwGVW;SOhk(^37r)Ffa*}_TyV7=0$tW9k}W&5Z)5}zxjh0SYN99+#h$j+~a z%|;Za4>|Xdb(75|1T$#4e0w)p7_V!M?Z}jBR+TDW3lld7120E-dgfR$eUDbp5TfBR zhi*T5k1dY5!w?IHD&q}$uu=;KgOyj;pe|+$z-B8R!OBQas4`d?3~pN8@n-C_U8+8laP`F-G8J^rPeG#wI*FH0u&@9IJ{= zP^+cLjTlg3e;e}bjzD+GLs4>FsfSQTj#@9Z8(J(!O>Ny)ff~n=Cr3>kn`R^swjLol zB#BbpC>Q!{R#B#a8&33Pjt6EUlsN$}&Db486B@%}T8?0;HX=tz20E%{j6`(&sc3l_ zMK|#I!-4o(-LtuAeb{VAyv!Tk+?0^vkSplgw7PcmUO`uQbEVtpK>CzKSI`yNT&Zy^ z(GhgjY_8O0q=m~4x`SF!PmKn$chl;~DBT)at*nhZy^+o47*R22q~mzeHK!qq5fDt} zz_ZW5sMgHgh@1FIjOwun1++@l7>7W*6AyDd9!&{z0$zFe#Qy<4sQ_P}$Dd4~DR?yb z7}SbFhIS#IykKW}rDGtRP?S2e8oa#4lF>|bc%RWN2IkBj+)d&p4pnE;u8Zb0rxM9A zco-Lo)q6E)BxG}%IA^csP%(o!9SDgDKzVSlhC?I@Abu2&5`6|5(`U5dfyPypz0ojJ zsizf+QMnjj*e@ltJC!*Dk3c*GDa=I3ZgrO)>G1=!hv=R=Dmx8CqP{vi*PfJX7Ux-q+CZtAwlGLLm* zJ|$7<)X$A$;70luh`xD^!399?yo$3wh8*U6ar3+(S%Suo_E;Yj@(oriokl&hOJb?l zTu5asvMGi87<;QRO}6b045R6;dPs1Yc`Vnxf;VnySQh)=a-Ex?3dwZ6==A5e?TC6TAV1@e?> zNxwJg(UWdZ64WzLJYvWEsAG}%`}{E#dxO%Va7;`1TtUsaL&O&B?mHVDHm(OX^Bi&f zf|l$?L^1a|jdKz3X^D_4yhzKPNSa{=Sk9#pioK>Tu;vZ5n3-%r|mEzJ0fDHi> zF69-xXHw^|%?jPvLXDwT#WI8pL#Yvje46!o3%cE4v3>7$#|rV`-U-K6QMu2OX?qm& zCMeVCl&MlN4*P_>wvIuM{1)og;Z+DjLCVO9h=lF%S%klmaehz1RUCY*0Bye8uP9~?rD~Z|z?}huN0WSN z=AncfQ-DEc2N+WJ6h|(un{yc8zDmW}HyHU;&0LK*iHtPiWV~mg+DyYd94Y1+LXQV4 z72^nmip2nMqyY$xRh85s) zj&{UUXWu5VbxCXrMw)HEQ7CgB9$Y%us+c_F(}kUrxfaB4m*IdA*eYOi&XYM;`1Wng z!h&_F=311m8bM&79?esi_QCLS6##ckU)l$&))gC@k?2PQy_I^eTfvm7%r0<8Hd>lc zGb9Pa0^ZUdqKWR!@}f|x6J3U3YMQKNk{#@Yjk0t_k?1)jFs6|L6=Of-@vCVP45v?r zRimg9z~%I^(2$nvveC#YTGne!C%SJ|%<5ih90N*aYMKNs3&}E5B%U4y$O5S>j>@`l z@c##Uu+WW_$f^fjUSl1KGOtBU7N;BNJid?{n&(jj?AOwu=_ED4EV2)I zuWjYDrCd3%kOTk60g8@{*d5dN<0g?M=9v{&^;9h=P0n!VLx-F+N@t|_2-cozni{xA ziiHxomon-y7E$ITBre6ei)$m?y~7=6f^UJ0s?kh%{eE&HOk*Fiws~RmML>iD6DOuR}AHd97*j{Cqvu zYt1Y~+P&7iLL}j}dJ2)@Uh9D2h1i_*oJPK@*L zxQvg~xqtA`G_HY8i8pB05g=!?WnDs~M}uecIXq%oQ~Acl4lG{6)()g>ZHaIfvIa+h zHXl{k+EQcvlT&K5ITBS#O4Ry3u4Iv^E=$A1@+1nG>aB7_wzf26N%p0g8m)RxY4T>N zF-tQw`K)0w&7URRQktzPU`>!|Wjtuj6Hx7ttI{Rvmer!y^VHLU_g*x3lTNI}RO3YI zU-`AE(@C>=4(i9HnQ5e0t3WVfu165MiRPW#k$*1bH~J|3Cm7*jV-lp`gUR2frZa$g zHm|z~hE|z3QR`(4GpjD8C1hU?son;210h9v0v?HvWz&=S&_;|zhd&&0#j7x=jOCzg zo&@x*SReGz(m@CIhx(?(hzzlr7?Qa__?8bHdNOcgV^!l6ysNkdF`60BjLJM>*J)rT zUEbulK&)L}Q$`Yy`8~*g(egw#FNZEPd16RO?ak9bF)^qVa(c0hMr;UB$#u|+f5m8+ z55h2lP~5}lSw1cO5OzDvGl3F_FM>&X7D8oSuX#4!Uh^D0!e{BE4U2wxpar>~cu;n1XP%pr&B}LJet& z5wwyrF9aB=Mbe0r(*7^xMzn#JyiHo*P~rA!MoT|TwYrE%>+x4!|ib2umOZ1TIB-@-7sl4}>R{6!aJsw3%r7;)5%F#$^=rt3f}} zzn|&@V+;am2iUc9HZU$HO0R*EZy-Fmgwo?g=?Wx<+pAU0ct||D|ABKBP}fLwsOv7r z08hr$4D36X`8!}G8r1Zac-O!t+CuR%7{Z!)6%iOv$@(NiZYsE^KPOB)b49dlE0U9H zIObHt=GEfj6+>oV4nXHKu}|VFgE5L-2A?l=4bc3V<#+}(S&DfrBIfV$2)R1{1&Wm} z<2s7b^l`oTX~od&eW-0nNAw23sTnz7ore)>z6P?eKyA?Fc~IOHi95o{!AQ6fnV|J^ zU1LHS3{IDM696`R)G7uccC!TDf>)%+`~zOpmTQ7~Quk(VMHJgZw~6Zy7@OUO2zb05 zuL&x)gdlL&Kp66{`-pCCM^d=G3R^OFAmlP00RKt1JAyR@hKIC^kzm%~fv&cWsxY`x zWmdvbF8n3qP9)mc9>&zoKLQlm#~06{vG1ZVdVnkaAez^B3`M{UzZ=kSd!sA;D1eD@ z&>z`|DGUl+LbmRTH;&dlK4d#dY&$UK`4cjO@;&1H0|!@L&-yf?nb`MWJ^!N!A2d{T zCB?*p7SCUVwsmsJ=Do<}!}=MUu?VTggVZ%$5&zCn0NoEz@&ian83^xQkdRZdS0>-c zBHlS@a1hJ&VAvO*rJ4^Qmu5aFf(M5jcZ-&To3jrALd!rc(&N)4g849D9XFsmf-ZE> zna~OQfJEvMz z*#yYGfM6j-JugwA)p@)@cj{4OF(1Q2?g%m=^I`GM!FAck0ZsZVwSj7|Qy0`?&NPoY z9>01gO&u8OJcK=(Y-i1+Ox)P!69`dvOFu~gG{UdD0vd)!kP4mR3!6^?FR=m{o<<<( z^`xJ{J7PR3HXIVF8@K_1W2_bv62;-f{M0HBqqV0HjHjpWDyT= zarNq8c3<#-;zLf&d=H$3Rr7CnwK9U!d>`SFUR5f*QE!I*@g}Fvh$Zz<^J5@23^hgd71{}!jNnK)+|*vxDpj=l$6a#ZD$kRJf? z$4@{Z|3N6AVs`v50?d=#m`N@VmjU`Cpy?gLe?(W9D?_{6jA`@ulQ`gr#)Zt0yYmJJ z2g;n8(zqaungLJ<>G{~th_z;LR6#m~`3>snXVEE4#R)u5JX+~798U545jA5~K$35x z{+@v_CPKUZz<-YLorLd;2cUsn2pU)*Y6!bU!;v+Pe~Yn44xV`$8WxKw&D01NYMFPh zX))z$qmER*X~YGFOrCbaBOz2WOX4o7+uQ!3ngLe~z>frk>0C3JiR%bT9cC@4B_pX2G=ra8LZ;|uRD5R6s;NVANY~RlNO!x$HG_g47#f;JfsPQ~1Lw20qh7&0T7FjdWWQRgz zFe2P=L4Rd{8@JVt1_LlwsbOhN8T3TNzt#?~>_%PH^(0;HSJq2yB7XGX))ggkrF~Co zcj_!dkUfFz&MJRZz!)kv-?#siVE{oD7%fEEXvLFeifA}B96`TMDw6f25_GQ#YHLyk zz~Qht0x#W+iE=ACD~|A*>?J58-H2ve`cfZ3D#^%66qA}{5$enm{IKYTm)3i&M8NK{qs&z^!HgqophXX(*S$o6h7P8HGTd{zFPVK3LmlTYv% z9l(1_N<%kCQ>NI|F;qiEX~lmW4J%?$Dv>>ar zj1j`dSVVnVve~+ZI4o(5_EGj0i-9tH*g&M*$tqP#k3*JPHPwk%9|7$&nP8630^W+1 zy-PutQ4S<#(h~qnPs9U9TAjB7j3ehsh^`C6(g=9QL;Q-z#4dnu{6++MEP0CKE}8bo zN~GkQE0I{`$y|nJGbe*kqSs?Q3$jShQfh`c_u8GB&iwerm zTx!r=YfcA+R<0msk_6bdwiQ~EYQxjprgeLBm$&{xX*_k4C{r6CyBv{}A)1a;yMthE z3x#G-h;({tCWUBR;watN0lCwrl=UHqSrh!q+N!G59>6g7#3OdBOfcRB?Wi#eARxQc z2F|yLY#B|t1;RX-dP<&HfBRsyNnC!cKRX*4{Cav0Ug2<+u@~ODITz3Lyb=%}cm!l8 zMPp-2hJ_sVnj~Me?APEe9Q4O~%Xv zAaiRqD%(FOr8bH=$NRHOklPh8_a(;69y}wg1IjeD??n)>GxosD(9#6Zz4BcuAPE`J zdXYV~6pzdcz+~s6=Mfmt!eE~hT5^=44QMq4S>{v@gq~iG9Nxsp5*}@~>+Q05GdW^)WfoFgbJAu(eG;0eEVcDopRLThKV15L(?%Xk~zGXOOTE7*s5 z8b{F=BGDX*vo{<*|1%#Bxja_)XmQ~Q!&hBA+Rps;wdj}DzGI*qjCVlBIvPQ2d2;w{ z3cES1ugB4m&ud)^kg*epAx&S8Lm8|)tvmCuR((B=6fi+q0iDpq;1fe*4+Phmo+jMB zgJhVH`VOWrj^XgaS+4OWXc}+fvD4!Tn=8fPCpJ1(i8D{EQy(28et+U_jzh&;C)T!J zxEtGR*w3JWPj=b>LuxCe-@(?Fs5NA)s7tqpXw{;IN5XD73#duI)jd|MNVjD_8=Dtg z>M#`EqV=t&mvzZFOecpIg7!%1TSE}_#l9mr<}{cFep3$ws+w#J&*$);sRz7kREaE8~J!$I2+wE3?9ZGuwdm z6tK7xz=$A_X@LQ&V?3Zq^l$i3jKnKhX)OmxV-+&r#@CD#F>d#oLDq8x4B4w_MWrdQ zPL)GRkPFsf26p5(=@Sua<^*M}%Q|m@cw_y_a*CjmE^3h1CWyIx9jb4l*w8mdZJj9Y z>TBHvVbVnLNnax&_%n@!5YLP-N+}#Ok>#VAI9P-Y3wb+GVCxT?1d1~=&8yhBjX>YOBQ&9)3W45Dia9RrQq77)P;cR2(@!h<2&DTmH-Tl%JQWczPTdh2Mp=V$IK?~-3DiNlsHAdD zX0JJbbI00Jrz1H>B;?{A73ROJYOR9&q_50U)!I~u%vG&>3XyKr+KGrc2>L!bTD5*G zL|Rp=YI2@Ht!hm|M54j%WO+U%76H#-+O70JpB@3G?X}v3WRo;ojSr!H5|tUu)Hs;uH&LMH$ zB5po;oI0{ge0cJ>1F=NVl309oVQmASpTe$41Qk9K6;9RBl#5yLWlUI^+fYqcZ;oCZ z=9R=)M-1;=7lWPY1*jIh=sMAA*#CQ3^qsO>_G1L;Kp+tdx`2D&?qddLVDGxT(wL=% zVPdwI&915{b0fHnPb@{CRVE!AB)l^JjIs3++uVedQcPa^^9dcw2FvNasAHK+@a*b> zmCCr7cCL*?HV6_BF+$$+}61nWW6!&lvuPS2pisCZlAL-GX zYPx2l!K|yhvT!6t`?wU?ZOYN88M_6*xdxigHO3LsAnCQ!dU6z%BJpRiW$!XC2c>Wz zLy|VHKseHi&5hL@#~H!z0O3hU9 zsR66C6c)txrnwbRocW?G7}6sz@IV=)a;P;N25uo~O+g>>um`6#s@sldu0|#aJ0K6! zW&EV>g*K8b%De`sIK3Q)9r0@^=