From 9ecf33adf91e604ef91128b50b9e7b11f03f9add Mon Sep 17 00:00:00 2001 From: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:59:59 +0200 Subject: [PATCH] Ore vents now turn transparent and clickthrough when there's a mob behind them (#95646) ## About The Pull Request When a mob enters a tile above the vent its top half (and the rig, if the vent has been tapped) will turn transparent and clickthrough. The bottom half residing on the tile below is still clickable. https://github.com/user-attachments/assets/5ed1cd3a-4f33-4692-adce-471eb2942d45 Same applies to ore type overlays. Also I've cleaned up ore vent code a bit. ## Why It's Good For The Game During combat some mobs, especially legion skulls, can end up hiding behind the vent and be extremely hard to hit, requiring the player to pull up the alt click menu to dispatch of them. This should resolve that issue. ## Changelog :cl: qol: Ore vents now turn transparent and clickthrough when a mob enters the tile above them. The bottom half of the vent is still clickable. code: Cleaned up ore vent code /:cl: --- .../objects/structures/lavaland/ore_vent.dm | 150 +++++++++++++++--- .../mining/equipment/mineral_scanner.dm | 10 +- icons/obj/mining_zones/terrain.dmi | Bin 3110 -> 8578 bytes 3 files changed, 125 insertions(+), 35 deletions(-) diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index 4870d33304e..f4ef87a2c09 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -54,12 +54,21 @@ /mob/living/basic/mining/brimdemon, /mob/living/basic/mining/bileworm, ) - ///What items can be used to scan a vent? + /// What items can be used to scan a vent? var/static/list/scanning_equipment = list( /obj/item/t_scanner/adv_mining_scanner, /obj/item/mining_scanner, ) + /// Turf above us that we're currently tracking, stored in case of movement jank + var/turf/tracked_turf = null + /// Visual overlay for our vent tap + var/obj/effect/abstract/vent_visual = null + /// Underlay visual for when we turn transparent + var/obj/effect/abstract/top_bit = null + /// Are we currently hiding our top half because someone is above us? + var/trimmed = FALSE + /// What base icon_state do we use for this vent's boulders? var/boulder_icon_state = "boulder" /// Percent chance that this vent will produce an artifact boulder. @@ -82,13 +91,13 @@ )) if(tapped) SSore_generation.processed_vents += src - icon_state = icon_state_tapped update_appearance(UPDATE_ICON_STATE) - add_overlay(mutable_appearance('icons/obj/mining_zones/terrain.dmi', "well", ABOVE_MOB_LAYER)) + add_tapped_visual() RegisterSignal(src, COMSIG_SPAWNER_SPAWNED_DEFAULT, PROC_REF(anti_cheese)) RegisterSignal(src, COMSIG_SPAWNER_SPAWNED, PROC_REF(log_mob_spawned)) AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_NO_TERRAFORM))) + set_turf_tracking() return ..() /obj/structure/ore_vent/Destroy() @@ -286,7 +295,6 @@ ) COOLDOWN_START(src, wave_cooldown, wave_timer) addtimer(CALLBACK(src, PROC_REF(handle_wave_conclusion)), wave_timer) - icon_state = icon_state_tapped update_appearance(UPDATE_ICON_STATE) /** @@ -321,7 +329,6 @@ */ /obj/structure/ore_vent/proc/initiate_wave_loss(loss_message) visible_message(span_danger(loss_message)) - icon_state = base_icon_state update_appearance(UPDATE_ICON_STATE) reset_drone(success = FALSE) @@ -335,23 +342,113 @@ log_game("Ore vent [key_name_and_tag(src)] was tapped") SSblackbox.record_feedback("tally", "ore_vent_completed", 1, type) balloon_alert_to_viewers("vent tapped!") - icon_state = icon_state_tapped - update_appearance(UPDATE_ICON_STATE) - qdel(GetComponent(/datum/component/gps)) - if(!forced) - for(var/mob/living/miner in range(7, src)) //Give the miners who are near the vent points and xp. - var/obj/item/card/id/user_id_card = miner.get_idcard(TRUE) - if(miner.stat <= SOFT_CRIT) - miner.mind?.adjust_experience(/datum/skill/mining, MINING_SKILL_BOULDER_SIZE_XP * boulder_size) - if(!user_id_card) - continue - var/point_reward_val = (MINER_POINT_MULTIPLIER * boulder_size) - MINER_POINT_MULTIPLIER // We remove the base value of discovering the vent - if(user_id_card.registered_account) - user_id_card.registered_account.mining_points += point_reward_val - user_id_card.registered_account.bank_card_talk("You have been awarded [point_reward_val] mining points for your efforts.") + update_appearance(UPDATE_ICON_STATE) + add_tapped_visual() + qdel(GetComponent(/datum/component/gps)) reset_drone(success = TRUE) - add_overlay(mutable_appearance('icons/obj/mining_zones/terrain.dmi', "well", ABOVE_MOB_LAYER)) + + if(forced) + return + + for(var/mob/living/miner in range(7, src)) //Give the miners who are near the vent points and xp. + var/obj/item/card/id/user_id_card = miner.get_idcard(TRUE) + if(miner.stat <= SOFT_CRIT) + miner.mind?.adjust_experience(/datum/skill/mining, MINING_SKILL_BOULDER_SIZE_XP * boulder_size) + if(!user_id_card) + continue + var/point_reward_val = (MINER_POINT_MULTIPLIER * boulder_size) - MINER_POINT_MULTIPLIER // We remove the base value of discovering the vent + if(user_id_card.registered_account) + user_id_card.registered_account.mining_points += point_reward_val + user_id_card.registered_account.bank_card_talk("You have been awarded [point_reward_val] mining points for your efforts.") + +/obj/structure/ore_vent/proc/add_tapped_visual() + if (vent_visual) + vis_contents |= vent_visual + return + + vent_visual = new(src) + vent_visual.icon = 'icons/obj/mining_zones/terrain.dmi' + vent_visual.icon_state = "well" + vent_visual.layer = ABOVE_MOB_LAYER + vent_visual.vis_flags = VIS_INHERIT_PLANE | VIS_INHERIT_ID + vis_contents += vent_visual + +/obj/structure/ore_vent/update_icon_state() + if (tapped) + icon_state = "[base_icon_state]_active" + else + icon_state = base_icon_state + if (trimmed) + icon_state = "[icon_state]_trimmed" + return ..() + +/obj/structure/ore_vent/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + set_turf_tracking() + +/// Update the turf which we track for mobs entering/exiting +/obj/structure/ore_vent/proc/set_turf_tracking() + if (tracked_turf) + UnregisterSignal(tracked_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED)) + + tracked_turf = locate(x, y + 1, z) + if (!isnull(tracked_turf)) + RegisterSignal(tracked_turf, COMSIG_ATOM_ENTERED, PROC_REF(on_entered)) + RegisterSignal(tracked_turf, COMSIG_ATOM_EXITED, PROC_REF(on_exited)) + +/obj/structure/ore_vent/proc/on_entered(atom/source, atom/movable/entered) + SIGNAL_HANDLER + + if (!isliving(entered)) + return + + if (!trimmed) + trim_vent() + +/obj/structure/ore_vent/proc/on_exited(atom/source, atom/movable/exited, direction) + SIGNAL_HANDLER + + if (!isliving(exited) || !isnull(locate(/mob/living) in tracked_turf)) + return + + if (trimmed) + untrim_vent() + +/// Turn the top part of the vent transparent and clickthrough +/obj/structure/ore_vent/proc/trim_vent() + trimmed = TRUE + update_appearance(UPDATE_ICON_STATE) + + if (vent_visual) + vent_visual.vis_flags &= ~VIS_INHERIT_ID + vent_visual.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + if (!top_bit) + top_bit = new(src) + top_bit.icon = 'icons/obj/mining_zones/terrain.dmi' + // Because its transparent we can't just underlay the full thing + top_bit.icon_state = "[base_icon_state][tapped ? "_active" : ""]_trimmed_top" + top_bit.layer = ABOVE_MOB_LAYER + top_bit.vis_flags = VIS_INHERIT_PLANE | VIS_INHERIT_LAYER | VIS_UNDERLAY + top_bit.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + vis_contents |= top_bit + animate(src, 0.5 SECONDS, alpha = 128) + +/// Revert the transparency of the top half of the vent +/obj/structure/ore_vent/proc/untrim_vent() + trimmed = FALSE + update_appearance(UPDATE_ICON_STATE) + + if (vent_visual) + vent_visual.mouse_opacity = MOUSE_OPACITY_ICON + vent_visual.vis_flags |= VIS_INHERIT_ID + + if (top_bit) + vis_contents -= top_bit + + animate(src, 0.5 SECONDS, alpha = 255) /** * Sends our node back to base and cleans up after the reference @@ -420,12 +517,13 @@ */ /obj/structure/ore_vent/proc/add_mineral_overlays() if(mineral_breakdown.len && !discovered) - var/obj/effect/temp_visual/mining_overlay/vent/new_mat = new /obj/effect/temp_visual/mining_overlay/vent(drop_location()) - new_mat.icon_state = "unknown" + var/atom/movable/flick_visual/visual = flick_overlay_view(mutable_appearance('icons/effects/vent_overlays.dmi', "unknown"), 4.5 SECONDS) + animate(visual, alpha = 0, time = 4.5 SECONDS, easing = CIRCULAR_EASING|EASE_IN) return + for(var/datum/material/selected_mat as anything in mineral_breakdown) - var/obj/effect/temp_visual/mining_overlay/vent/new_mat = new /obj/effect/temp_visual/mining_overlay/vent(drop_location()) - new_mat.icon_state = selected_mat.name + var/atom/movable/flick_visual/visual = flick_overlay_view(mutable_appearance('icons/effects/vent_overlays.dmi', selected_mat.name), 4.5 SECONDS) + animate(visual, alpha = 0, time = 4.5 SECONDS, easing = CIRCULAR_EASING|EASE_IN) /** * Here is where we handle producing a new boulder, based on the qualities of this ore vent. @@ -562,7 +660,7 @@ /obj/structure/ore_vent/random/icebox //The one that shows up on the top level of icebox icon_state = "ore_vent_ice" - icon_state_tapped = "ore_vent_ice_active" + base_icon_state = "ore_vent_ice" defending_mobs = list( /mob/living/basic/mining/lobstrosity, /mob/living/basic/mining/legion/snow/spawner_made, @@ -653,7 +751,7 @@ /obj/structure/ore_vent/boss/icebox icon_state = "ore_vent_ice" - icon_state_tapped = "ore_vent_ice_active" + base_icon_state = "ore_vent_ice" defending_mobs = list( /mob/living/simple_animal/hostile/megafauna/demonic_frost_miner, /mob/living/simple_animal/hostile/megafauna/wendigo/noportal, diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm index 420aad1ceaa..8d763fd2f67 100644 --- a/code/modules/mining/equipment/mineral_scanner.dm +++ b/code/modules/mining/equipment/mineral_scanner.dm @@ -113,7 +113,7 @@ layer = FLASH_LAYER icon = 'icons/effects/ore_visuals.dmi' appearance_flags = NONE // to avoid having TILE_BOUND in the flags, so that the 480x480 icon states let you see it no matter where you are - duration = 35 + duration = 3.5 SECONDS pixel_x = -224 pixel_y = -224 /// What animation easing to use when we create the ore overlay on rock walls/ore vents. @@ -122,11 +122,3 @@ /obj/effect/temp_visual/mining_overlay/Initialize(mapload) . = ..() animate(src, alpha = 0, time = duration, easing = easing_style) - -/obj/effect/temp_visual/mining_overlay/vent - icon = 'icons/effects/vent_overlays.dmi' - icon_state = "unknown" - duration = 45 - pixel_x = 0 - pixel_y = 0 - easing_style = CIRCULAR_EASING|EASE_IN diff --git a/icons/obj/mining_zones/terrain.dmi b/icons/obj/mining_zones/terrain.dmi index 5351cde085c3a401ec7f72a7221e50a6dbc252e3..f46a677296b12cd2e9c7f265ebc633730b7d2249 100644 GIT binary patch literal 8578 zcmdUVXIN8Rv}O_#2}J>=>4y?PdKDC;NFqf9qzHs6T_g$tBou)JqJoMjy(!g#6zNh# zN)SW|p(r3NbOWKI2`vy{;=On7+&_2bxj$x}JAZc0%GrCZebzo}z3*Q8#9Epg9pya5 z2><|&8ecWAV*O470Kiu05!O}X)=f{A01vmZ4>It=cn01M2)ga>2LRjyrrD65K2+g% z&Z=lN-=i@=NdMJ>KKW)}_&6 zxves$kb2J(HuwhNJv@HJy8p!PgwH^vi{12QwDp=e{w%rC>G<@ zEpInkqO>9bRVNis*~br?q)K*g!1M50^edpBfwvU$QX1|WE~y*=YbTWddgiQ{7i#2j zJJe`Rf?rnWYVRB2M$BxzCI42h%QHTy&qwU|o5u4zvT=2;39qZG3laYG9}(^%2J?2J zw;gU#%}Du{O5k@WaqS>)k+=eIlEXl?)^m-M!v`}p>v=WWVUn1q8WX#C06_G>*x<6w zy|L9Cbg3^g?3?$_;5|b$-0~79-_fCa+?nG`1s+9$%^g=f4A4jWZP~u^T!(YSRmc^}psBPyD` zSNB&g(f4pKFt^rU95(3?93skUUEh5e^#uWRukrD=@_@ksU?5QBG6G=4%iGHL-%?Dv z0?G|&<4BXA3Zv!>D7h*y*y)G+C8F0NH@oc;r#vHtZgLd)Gv}0}!sfygbazqGqF{z} z_uFany^UPrX5J{AAh0sEoOxI8e|j0W^3-)%=TJ>_XLj~L`XH3*9r8t4`Y43=I69d3k)prR_OAx zw|Lq-Qh@tRY zx-^~B-0Rvd4Qla?G$EyDRqVI>omKcI1{%|-$Wq;Un7VqGp-PEtu+Zh*i`tPSD%bn! z&PPHi8l*!i{IG)fBBL2yK=+!S)6iv@KBpmD-!c3^OIP!7^rw*=)n zvWE9O2!Xr6k>EDv%ayEUqty2Z{dl{c(Umczma5VnDip9j*{rsYOcFY1W#d^vEv;7M z`DIYC99kl!hs#9P~fKicOk)p{%HS6anjAAkx0v#rksh8 z`h=-^|EXCvSRV5oLp*QgsI~Z%bL;Pil__IYeCPH)FZ{zD71O4XO@Bv}hD*Ed#)!Kv zvXr&+=v7KsX^EhXfOEE!P2=8K46jKQ1|?#?kt=goB~WIc)qhHjV0ux6A) z>?BY-u)UC|i>FO>L2V}a9Tgjj?qn=DI&E6N5a(9M*d6XWoZQ{2mmYSBY7gROK%w%V zCsWL`(ahPe%=Oj7$=sF3)%ZnW8XxZ!!;-wsG0*ti)4_vDbb@H)P7*Qn%)J1uM=X6m zEPwVb>9;EV?Wd1~zc;~Ky?NtIk$^pWR1#@xf=~W;$yTbil(K*lVwrmL>N!Bt$f&W? z2qAnF4VKZoIAF+{CJKSTpFyb&+gi2i()oCg$mS($4+|=3|7!Q{K5SV?HkJBE`I3h? zDGjnze_ZVcwTQ&h%Wp;TAj|Hgh=!d0!BBkO~OYbnX$gWtob;%2c5U*UYGb9lvoyigYL*< zHZRy&tW?{ZdPBZ%rVCWcxjGWV=08-|5?MxS@b2=3$2(QUd>2i)>@!5?VX)F@ysZkk zD;D(%5Mo_X43MOukAd2{phYoROMStLZhkF2ml49v_&|BXh#3a8Z#|_)w}1zTpy&K`B?w%~b1SVNJ)rK()5(lRC6Y^;lppljg{$1xOt;#o< z!7pH0dGt+APGOP|qcn&%E=OptRUIpi1%b{KIW#PLz@mQA%K+zM+lGvjA{}o7H#JMK zvWr&r>lbgY$2Z?h=lwnXm{7Zk(N6dEJBZ>jR7q!+yR?T>>^NkiacXlutmuvFkHKq+ zqNspXn`p^Yizwlz=m%X=S62?GEzxTlkC*5qf5DinoK1$8qkHGAR8Ry4<6 z%&v&u(jb2^R^G-$SN>I6g{ z-eaX-&R(12(vtNrl=N88eI0V=Y#My`_LE@Y=cQd&73CS9cDpN%TRz%+Uvxrc9Q!;+ ztjq+l@9J1DcN23lJL}q<8qbLP-Ec0MdPx_jJchCscDUPwpHn6VZ6uC-WG|}1Z+XUl zI3XurmgUPkEjfO?E4ba=KpI~FWpE!5w9j^36|Pg)E7;UoaDqJcEm=I>X*8QO3O-uE z$!-uxYZnE`J7;0RB9}HFnopS6OTl#nqK}2V1=lDhne%+vMKyrt=f8n(NQnZCy5%;b z+O01yTxYk%k96Mr7*^Ex>Ua@A3-6h5s}iK%Qx6A(OuS=2pI{$$@>oEw|Ll*!+#Q40 zJe07-2kh!)II!O2Z(!e)1iT8=0I)|u->s>vt?+hg9uaZ>xQ@E>ZOO7#iV=Rki8E7+ z$IO`(dTwBD_Vaqcw%KD&ts}pJO)e++)`4ZhW0@;P9%D137xXq0Kmf&(b@AwsG( z*g{AaCI47q;QE&mHWwDjpx;gZ{b!4jK^)#B|21LKmMIeQLVP)Td(_J;m#ilGe~ES| zJaO?<3R(OW+$q$uw`2WbxM|~eZI3@ENKh%Vz@MXu{#YCUDwmysoT+F&c(kZB=kteD zmI0V#pb@Ql(HZq)q2$v8!Ck|%hX>nH7sGzOk8BBLjUqq5A2r_mu(712caQN4*){CJ z!^IprymwD3CW`)iD!a*jd!9nk5Us6dKo=D9JR}$N0qd+cUui!s>V@&l6voZx;4n-y zs?bRnR4#+uFO>tDJU0)g+Hw3&`fz~STCzLrVy>^_)>-KDY4*cF$!23`)VhD}Nr8Y) zHZ|x7{@*fg$|S?FyDHK)2OsF9_n-tTP)WjqgWi^;87sw@C1 zwI;t|^|u9l-lAK9-a3|6%}Y#of)z9`&i?I3?l$Fsy-6uJa;1rtk%7+i+RS$GisOhY z(!n{_Q-|pVj$-Rr@Jg&Y1}zEs-D~mu)R;%FK=>+(nA3SF9I^metq`-OhX1ZxU8$3B`89sS_71>(( zWH=l z53E4%jzByVa|94vUJk(bw1TuuZ(Vt(SZ1)0CA(G-!DLZwQEFx`!$r3WG~!GEGYaY< zMFS8tc%#ez0j?~VQ3m05YItAK5dID7G}50YSg8c>UbI3K7)O=7#p5W2N7MwTzR_9u zbon41H0ZjU4C=yc4IC1fQIx{&72t_If5 z7t4yaLOF9z=fGuvu0cEH4#=o-9~Mxd&5eQ99WR}_Nt_EwqMWb}%uqvK-r?!3LL0zk zE;BBbCPEn=N3G{mktS5(t(Zc&Q3p)_(G~06<0{gwL$Q}a9DvK5wLZX0^n#+A--Lj< zTKeA24;G91t>f4pE?G&p3G?wB4pf|L65kcsm2#*2Z0y{iwYesxN{d%QYjRx4`w{AdnR}q$``a&@f&N!^CR+wMf)THA{M2I8oc?J1L1OL2kkb>>eu228k$ z`rPMCjWa&sz8OdSv-Y{1}=B6WG z2iSec-gY%Wh^=JFx;CPxZlo%KQ>hpKk|#+XNiL4L0nsC1U*-IXCeFQEPgnRn3>9Vb zaq*VbiI%zoo+p822JTZT+xtR%Dnm5cs!Q&rAI0%otI;La5?^R#ma>e}XxkPkhBId( zy=jtx1buPT?bsS9m8nBkT5}e+Kd@rh7Nufs_2gYsSSc+d>`;7~xE{H#(Cx3_#<^Np z0OoJIpOy-)!d?!otQ$Cf?rrU$@!mUzGl!*PYB>v$W42PPATrTx^5;0L7!S<5$hTvK ztGAz-k7@K&oT%K`^FN!S_Lv8f}F9mTtt@(|3$uPj48THf; z9D*hS|L_)*cp#PiX`MWfmEC=1@UzpwmXi7C*r0IOkP%4$rU937i^YzFor=){uHK#w zF8pOLS{4h?WSj$RY0jtM;?tUC0VlXU{x0{Wd&j@E}YPS}%sSTRBk-f#U zI{icdmfiB*%a*0+9593E8rtSu;d}itVPj_hQ~8#E8;JYydhiPYH)nb%6h+`A&!XIY zT>I+6t2EmJrhW{@|9pivqj_Oip_i41rVpQP{*?H7@A_CeA+ZX7WFzh^X)6e`n@lPfmO@ol->R5~W5F+dsIO-bWbYo`Te&r8R zJERcI;*2r)t&tW>0J;8-yxToXvJQe~wYl{eRH9ifC6>wWRYg|PS zFL#QxX$=681?v^3Gx48dQK6IZ9I%mnHS+vp&|fng)*J z=2FYj`PJpjz77Y#|F+n|-o1YKIfkxGwK$}_mQ8T&`6e-qPg4H<<;3>2wU3^MVx%+J zg;zwjDaIOe-P2|7CGkfEc1~=0!81b^Mr&j}D3E@@bFj72#?_mv!+e-SMeHvV7<};Zv^7i$pq;>MA(8!3D`MaMEW`*pA(k|aW z4!O$O_dqWOFeqH(q024AOYB<~p15RQMZCt!q#8MbHaVpKhqBG0y%K_!YeJN`Eg1wY zqtB@IK9@`uTy;hs0^25we_VU{URt_o^=-%5%GKYdz)tX8a*qmakI3rhVKluW<7f{k zgh#auR02wJA2Mes64Qt(LMzGgH3UXzWD$6o;)(p1{UMebm%v6LLM{i zgr$-iGI(*OibCZMqgG!7CQqSR?mJH?XH$OTYoW8%iRJH&@4pasjJ8i_C9#N@fhwv5 z02FVf+YtWz2JxLghv{GWm}6aK>n3NR#?(#^19`_hl72mhW5g(OB14+A-n3uG(6RZc zxAeviW-^jUgasAhNcKDnGjNv1l?=oeUxcp%>PC5Ndrtut^BHH%Bxmin$|OIPJwnfxH2gps%|QIL?vami^fhtsN_XDirqi2oVtcFu&g} z4VhweX1q&~#kg*^>KSzA_`H{}1F%RfqSbeWw-LsoXO@qbv!F$6r$4zCY`h3c`Asub z&fAd8w~Nex{AL<^5tS2_mlvjx6;(=6vcOuyM30~8eh$EP+xcvI?`YKx7R^V5S>UK- zWwSn`hHJnjhAf#CBzwsbpGQoS7GHcIQ+R9;CfjI$Na}IQ-B_vf|W=jH25V1FFJg?$xd3oNf#oKff7u z4!9g5o}NS)*(Y%p#Z)Bx*xNWLhir5k;b*8MP5o6v`;y%HS{6xqjR~{WLZ4J%5v!)n zRqwjvDzz<2%Z{xjOsN-WS}kVE5Xc!Uao>~JcN)AX=X!RKl!!ET&dB5cZvJlhhKo$j znoxsU0Os*CaZ(BNBRAAuiLeU0y11RE?*j~0bvE4lI$ZIR4KHF zP0vdKG1Rg^Flsm3v(sM{+CR6~SFofh53zY+pnF+E*0aCB&r7e7SdP*J)g`e3?TlKb z9>Sbk4ky8bk=e(}?TLDNjGL3CgHlBsaPrf0>OhQoR3M}>>v99cJ|mCJzAR24!miq_ z6F88JQIO*+E1CmG8TP@kX_TmHu)TQ(h~irpPTh6}c=oWK%;6*ul9MV}Rg&0j6x8ns z;WyCLt~`-aDGz-4WcEdcqUq23`DjkWF>Zmtzsc$^t$lE5Q07smWHcyprpqU^G(&6N zF9$NH{5}rU8D>t~>+zS&p8z^lZI2G9+XKCLjnvH`noq?;0!n(-!a1gM5&+PT`iM(% zh%0)Au!yZiYA!QBNXzjKyH6>8=8S5U?vGDgMXn>5yxR>R6b(z+&LHR`8kZeT0D}vc z?M17K@pAVISsT(yT?H_A=%s-l=JNRhoi6N`6L`O~VK)Cf;6#O@{4nQ%t#Z}8D&w2o zCSE+2KV@Sx@neJ&rTCDI0VREeeW)O4=_ihVB4OaEmxYOsvVl10(YKz_eMI{Yi&3?A zJ!>}s@2{{hGi9L8*S0+1weHqVC8Q?7=wC*DJ>qPmO0JLNK(V8uEn7#7U-I(8#N004 z_nWH*b%1LMUlkpS@BG{|{xHa0>kbiC>? z(JQ9x_&UMa79r3e`-Dfo&F`yuq&X6+c6Z1 zD&IW$2&u!@aCEo*{u33OJCM}$)s2D4`d^Vra{T7e{0@r4&WC9Q=6}i*E1QoYPEa1d zG2r6l464{C-atJ6O9=F_!poLD%=U+t)yV@eMrc(QbMxmoG z&As6|E0sj2$X#op&u}fq+^8oHvyYM&AJ!>3wYLwC6I@3{xqV=F-_T)dg2sX~M!_|G zuNtb0<)f_@Wjlfg>_rFWq+~$iZpxq~2=83fBG1<&Uf{*|#QE3!O)dsi(fBRUvq1Dk zf$QrZgnS2=zod0m{AzW7+;iZsxTRm&wAH?STO&C*W8pE=6Z&eWs z?V@j!IXh^pfe~Q}V!nJCJ?z4zc;tqd+r3y1vGMbU2w}Eb&=S`58iG$hQ7?g`i2jfS z6`m)lhV}kV{t_}^llV;`g=bpMZK?W`ZQF8rLwH~t+1zmjlHyq3bM>Y#_`HhLnT;btST0S`?T$u`RL#Wc9&OQlo;wX*-9&l4#XF23_Ah+9&2o#D=ay%qj!@C@}V;&Rk1%vs=&ZHY~K@04CfEf^= zyMwJ>E45G7aj(r}?Q_4BD9m=*46HNjWhCXzqmTyrYW~D$_($xHobd)T8x1b)>MJnQ zMLdMaUu3uCHwUuy<+}s}m-C4Gk#OLD1lC?Z* z1rVK@#a7OdCtl984gR!V`;^0pQvOF#y3Y`SpErP%W`REXFHgS6MLYJn;aF5{K(2T! z+qN=QK_rt)zaR}CIPvKBU=$K34+cL;;ji?gTbvyR>|4xXNl@tY)gTMu9A5=lL<*JO z%f1jti46nhNBp-vm;8^XrPYw}krC9*rLC>48VZ+W9uwK^e|Fj;clY|xCuJRGM-CQ1 zNg>=}JclsTFlKO+6nHw;5kVt{cCTM+m2e56P;%M!;&4!XVm%>5 zu<^olnlfoaD3f`Yb@IZXXtRo`jOVmk$kolmy>5$U!36A{1}NF$K&}pN<`N(z3Sdww z93y{Sa-Xb>u-IZViTrE%DP!g7k!dIhbnyWm5D$alC0PFcFN?EE(?O2RQXt?BD}U=O z|1IJEf7B3O;Tz<=onr8nz5j^==y*WxW&+DW|CJ_^rObO@0*3eeKR2Rqw)62Pu00sG TP-A^t0WdZ+Hz?P0d+gAda`S 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