From 395f618b0f2f614ca96bc8c59eb5ffbe032c354a Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Fri, 7 Nov 2025 08:29:14 -0500 Subject: [PATCH] Ventcrawler update (#18709) * mob atmos properly handled * Ventcrawling signals * ventcrawler blowout * clarity * fix * fix again * again * consistency * Teshari rattler * scrubbers can be welded shut --- code/ATMOSPHERICS/atmospherics.dm | 19 ++++ .../components/unary/unary_base.dm | 2 +- .../components/unary/vent_scrubber.dm | 30 +++++- code/__defines/dcs/signals.dm | 18 ++++ code/datums/ghost_spawn_options.dm | 2 + code/modules/mob/living/simple_mob/life.dm | 100 ++++++++---------- .../simple_mob/subtypes/vore/demon/demon.dm | 15 +-- .../simple_mob/subtypes/vore/demon/demonAI.dm | 12 +-- .../subtypes/vore/shadekin/shadekin.dm | 5 - code/modules/mob/mob_defines.dm | 1 + code/modules/ventcrawl/ventcrawl.dm | 34 +++--- .../ventcrawl/ventcrawl_atmospherics.dm | 5 + code/modules/vore/eating/belly_obj_vr.dm | 5 + icons/atmos/vent_scrubber.dmi | Bin 8106 -> 8207 bytes 14 files changed, 155 insertions(+), 93 deletions(-) diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 71c55554a3..1f598436a7 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -260,3 +260,22 @@ Pipelines + Other Objects -> Pipe network if(user.buckled) user.buckled.unbuckle_mob(user, TRUE) user.throw_at(get_edge_target_turf(user, get_dir(src, user) || pick(GLOB.cardinal)), pressures / 250, pressures / 1250) + +/// Blows out a pipe, deconstructing it, breaking the floor and releasing all mobs crawling inside it +/obj/machinery/atmospherics/proc/blowout(mob/user) + // Deconstruct turf + var/turf/our_turf = loc + if(!our_turf.is_plating() && istype(our_turf,/turf/simulated/floor)) //intact floor, pop the tile + var/turf/simulated/floor/our_floor = our_turf + our_floor.make_plating(TRUE) + // Deconstruct pipe + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = our_turf.return_air() + var/internal_pressure = int_air.return_pressure()-env_air.return_pressure() + deconstruct() + // Release pressure + playsound(our_turf, 'sound/effects/bang.ogg', 70, 0, 0) + playsound(our_turf, 'sound/effects/clang2.ogg', 70, 0, 0) + if(internal_pressure > 2*ONE_ATMOSPHERE) + unsafe_pressure_release(user, internal_pressure) + playsound(our_turf, 'sound/machines/hiss.ogg', 50, 0, 0) diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm index 09c1e2d9c9..19c698d742 100644 --- a/code/ATMOSPHERICS/components/unary/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary/unary_base.dm @@ -11,7 +11,7 @@ var/datum/pipe_network/network - var/welded = 0 //defining this here for ventcrawl stuff + var/welded = FALSE //defining this here for ventcrawl stuff /obj/machinery/atmospherics/unary/Initialize(mapload) . = ..() diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm index 23a48b8a51..286576890e 100644 --- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm @@ -68,7 +68,9 @@ if(!istype(T)) return - if(!powered()) + if(welded) + scrubber_icon += "weld" + else if(!powered()) scrubber_icon += "off" else scrubber_icon += "[use_power ? "[scrubbing ? "on" : "in"]" : "off"]" @@ -269,6 +271,27 @@ update_icon() /obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(W.has_tool_quality(TOOL_WELDER)) + var/obj/item/weldingtool/WT = W + if (WT.remove_fuel(0,user)) + to_chat(user, span_notice("Now welding the vent.")) + + if(do_after(user, 20 * WT.toolspeed)) + if(!src || !WT.isOn()) return + playsound(src, WT.usesound, 50, 1) + if(!welded) + user.visible_message(span_notice("\The [user] welds the vent shut."), span_notice("You weld the vent shut."), "You hear welding.") + welded = TRUE + update_icon() + else + user.visible_message(span_notice("[user] unwelds the vent."), span_notice("You unweld the vent."), "You hear welding.") + welded = FALSE + update_icon() + else + to_chat(user, span_notice("The welding tool needs to be on to start this task.")) + else + to_chat(user, span_warning("You need more welding fuel to complete this task.")) + return 1 if (!W.has_tool_quality(TOOL_WRENCH)) return ..() if (!(stat & NOPOWER) && use_power) @@ -278,6 +301,9 @@ if (node && node.level==1 && isturf(T) && !T.is_plating()) to_chat(user, span_warning("You must remove the plating first.")) return 1 + if(welded) + to_chat(user, span_warning("You cannot unwrench \the [src], it is welded down firmly.")) + return 1 if(!can_unwrench()) to_chat(user, span_warning("You cannot unwrench \the [src], it is too exerted due to internal pressure.")) add_fingerprint(user) @@ -297,3 +323,5 @@ . += "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W" else . += "You are too far away to read the gauge." + if(welded) + . += "It is welded shut." diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index a965c3b470..9de4f014a2 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -822,6 +822,24 @@ ///called when you clear a mood event from anywhere in the code. #define COMSIG_CLEAR_MOOD_EVENT "clear_mood" +//Ventcrawling + +///called when a ventcrawling mob checks if it can begin ventcrawling : (obj/machinery/atmospherics/unary/vent_entered) +#define COMSIG_MOB_VENTCRAWL_CHECK "ventcrawl_check" +///called when a ventcrawling mob checks if it can enter a vent : (mob/entering_mob) +#define COMSIG_VENT_CRAWLER_CHECK "ventcrawl_check" + #define VENT_CRAWL_BLOCK_ENTRY (1<<0) + +///called when a ventcrawling mob enters a vent : (obj/machinery/atmospherics/unary/vent_entered) +#define COMSIG_MOB_VENTCRAWL_START "ventcrawl_start" +///called when a ventcrawling mob leaves a vent : (obj/machinery/atmospherics/unary/vent_exited) +#define COMSIG_MOB_VENTCRAWL_END "ventcrawl_end" + +///called when a ventcrawling mob enters a vent : (mob/entering_mob) +#define COMSIG_VENT_CRAWLER_ENTERED "ventcrawl_entered_vent" +///called when a ventcrawling mob leaves a vent : (mob/exiting_mob) +#define COMSIG_VENT_CRAWLER_EXITED "ventcrawl_exit_vent" + //NTnet ///called on an object by its NTNET connection component on receive. (sending_id(number), sending_netname(text), data(datum/netdata)) diff --git a/code/datums/ghost_spawn_options.dm b/code/datums/ghost_spawn_options.dm index 3f1724aa57..31832e8f20 100644 --- a/code/datums/ghost_spawn_options.dm +++ b/code/datums/ghost_spawn_options.dm @@ -55,6 +55,8 @@ host.ckey = user.ckey host.add_ventcrawl(vent_found) to_chat(host, span_info("You are now a mouse. Try to avoid interaction with players, and do not give hints away that you are more than a simple rodent.")) + SEND_SIGNAL(host,COMSIG_MOB_VENTCRAWL_START,vent_found) + SEND_SIGNAL(vent_found,COMSIG_VENT_CRAWLER_ENTERED,host) /datum/tgui_module/ghost_spawn_menu/proc/become_drone(mob/observer/dead/user, fabricator) if(SSticker.current_state < GAME_STATE_PLAYING) diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm index 259fefc308..cd5a070de0 100644 --- a/code/modules/mob/living/simple_mob/life.dm +++ b/code/modules/mob/living/simple_mob/life.dm @@ -11,7 +11,6 @@ handle_weakened() handle_paralysed() handle_supernatural() - handle_atmos() handle_special() @@ -102,73 +101,62 @@ /mob/living/simple_mob/proc/handle_special() return - // Handle interacting with and taking damage from atmos -// TODO - Refactor this to use handle_environment() like a good /mob/living -/mob/living/simple_mob/proc/handle_atmos() - var/atmos_unsuitable = 0 +/mob/living/simple_mob/handle_environment(datum/gas_mixture/environment) if(in_stasis) return 1 // return early to skip atmos checks if(is_incorporeal()) return 1 - var/atom/A = src.loc + if( abs(environment.temperature - bodytemperature) > temperature_range ) + bodytemperature += ((environment.temperature - bodytemperature) / 5) - if(istype(A,/turf)) - var/turf/T = A + var/atmos_unsuitable = 0 + if(min_oxy && environment.gas[GAS_O2] < min_oxy) + atmos_unsuitable = 1 + throw_alert("oxy", /atom/movable/screen/alert/not_enough_oxy) + else if(max_oxy && environment.gas[GAS_O2] > max_oxy) + atmos_unsuitable = 1 + throw_alert("oxy", /atom/movable/screen/alert/too_much_oxy) + else + clear_alert("oxy") - var/datum/gas_mixture/Environment = T.return_air() + if(min_tox && environment.gas[GAS_PHORON] < min_tox) + atmos_unsuitable = 2 + throw_alert("tox_in_air", /atom/movable/screen/alert/not_enough_tox) + else if(max_tox && environment.gas[GAS_PHORON] > max_tox) + atmos_unsuitable = 2 + throw_alert("tox_in_air", /atom/movable/screen/alert/tox_in_air) + else + clear_alert("tox_in_air") - if(Environment) + if(min_n2 && environment.gas[GAS_N2] < min_n2) + atmos_unsuitable = 1 + throw_alert("n2o", /atom/movable/screen/alert/not_enough_nitro) + else if(max_n2 && environment.gas[GAS_N2] > max_n2) + atmos_unsuitable = 1 + throw_alert("n2o", /atom/movable/screen/alert/too_much_nitro) + else + clear_alert("n2o") - if( abs(Environment.temperature - bodytemperature) > temperature_range ) //VOREStation Edit: heating adjustments - bodytemperature += ((Environment.temperature - bodytemperature) / 5) + if(min_co2 && environment.gas[GAS_CO2] < min_co2) + atmos_unsuitable = 1 + throw_alert("co2", /atom/movable/screen/alert/not_enough_co2) + else if(max_co2 && environment.gas[GAS_CO2] > max_co2) + atmos_unsuitable = 1 + throw_alert("co2", /atom/movable/screen/alert/too_much_co2) + else + clear_alert("co2") - if(min_oxy && Environment.gas[GAS_O2] < min_oxy) - atmos_unsuitable = 1 - throw_alert("oxy", /atom/movable/screen/alert/not_enough_oxy) - else if(max_oxy && Environment.gas[GAS_O2] > max_oxy) - atmos_unsuitable = 1 - throw_alert("oxy", /atom/movable/screen/alert/too_much_oxy) - else - clear_alert("oxy") - - if(min_tox && Environment.gas[GAS_PHORON] < min_tox) - atmos_unsuitable = 2 - throw_alert("tox_in_air", /atom/movable/screen/alert/not_enough_tox) - else if(max_tox && Environment.gas[GAS_PHORON] > max_tox) - atmos_unsuitable = 2 - throw_alert("tox_in_air", /atom/movable/screen/alert/tox_in_air) - else - clear_alert("tox_in_air") - - if(min_n2 && Environment.gas[GAS_N2] < min_n2) - atmos_unsuitable = 1 - throw_alert("n2o", /atom/movable/screen/alert/not_enough_nitro) - else if(max_n2 && Environment.gas[GAS_N2] > max_n2) - atmos_unsuitable = 1 - throw_alert("n2o", /atom/movable/screen/alert/too_much_nitro) - else - clear_alert("n2o") - - if(min_co2 && Environment.gas[GAS_CO2] < min_co2) - atmos_unsuitable = 1 - throw_alert("co2", /atom/movable/screen/alert/not_enough_co2) - else if(max_co2 && Environment.gas[GAS_CO2] > max_co2) - atmos_unsuitable = 1 - throw_alert("co2", /atom/movable/screen/alert/too_much_co2) - else - clear_alert("co2") - - if(min_ch4 && Environment.gas[GAS_CH4] < min_ch4) - atmos_unsuitable = 2 - throw_alert("methane_in_air", /atom/movable/screen/alert/not_enough_methane) - else if(max_tox && Environment.gas[GAS_CH4] > max_ch4) - atmos_unsuitable = 2 - throw_alert("methane_in_air", /atom/movable/screen/alert/methane_in_air) - else - clear_alert("methane_in_air") + if(min_ch4 && environment.gas[GAS_CH4] < min_ch4) + atmos_unsuitable = 2 + throw_alert("methane_in_air", /atom/movable/screen/alert/not_enough_methane) + else if(max_tox && environment.gas[GAS_CH4] > max_ch4) + atmos_unsuitable = 2 + throw_alert("methane_in_air", /atom/movable/screen/alert/methane_in_air) + else + clear_alert("methane_in_air") //Atmos effect if(bodytemperature < minbodytemp) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm index e4dac2f548..112126086a 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon.dm @@ -71,14 +71,12 @@ /mob/living/simple_mob/vore/demon/UnarmedAttack() if(shifted_out) return FALSE - . = ..() /mob/living/simple_mob/vore/demon/can_fall() if(shifted_out) return FALSE - - return ..() + . = ..() /mob/living/simple_mob/vore/demon/zMove(direction) if(shifted_out) @@ -86,26 +84,23 @@ if(destination) forceMove(destination) return TRUE - - return ..() + . = ..() /mob/living/simple_mob/vore/demon/Life() . = ..() if(shifted_out) density = FALSE -/mob/living/simple_mob/vore/demon/handle_atmos() +/mob/living/simple_mob/vore/demon/handle_environment(datum/gas_mixture/environment) // TODO - Refactor demons to use is_incorporeal() if(shifted_out) return - else - return .=..() + . = ..() /mob/living/simple_mob/vore/demon/update_canmove() if(is_shifting) canmove = FALSE return canmove - else - return ..() + . = ..() /mob/living/simple_mob/vore/demon/apply_melee_effects(var/atom/A) if(isliving(A)) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demonAI.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demonAI.dm index c399d88a3f..3b3b67e4cb 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demonAI.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demonAI.dm @@ -74,14 +74,12 @@ /mob/living/simple_mob/vore/demonAI/UnarmedAttack() if(shifted_out) return FALSE - . = ..() /mob/living/simple_mob/vore/demonAI/can_fall() if(shifted_out) return FALSE - - return ..() + . = ..() /mob/living/simple_mob/vore/demonAI/zMove(direction) if(shifted_out) @@ -89,19 +87,17 @@ if(destination) forceMove(destination) return TRUE - - return ..() + . = ..() /mob/living/simple_mob/vore/demonAI/Life() . = ..() if(shifted_out) density = FALSE -/mob/living/simple_mob/vore/demonAI/handle_atmos() +/mob/living/simple_mob/vore/demonAI/handle_environment(datum/gas_mixture/environment) // TODO - Refactor demons to use is_incorporeal() if(shifted_out) return - else - return .=..() + . = ..() /mob/living/simple_mob/vore/demonAI/update_canmove() if(is_shifting) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm index 29fa42e05b..0844f243f9 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/shadekin.dm @@ -347,11 +347,6 @@ return TRUE return FALSE -/mob/living/simple_mob/shadekin/handle_atmos() - if(comp.in_phase) - return - else - return .=..() /* //VOREStation AI Removal //Friendly ones wander towards people, maybe shy-ly if they are set to shy /mob/living/simple_mob/shadekin/handle_wander_movement() diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index ff5cf908b0..97a406720b 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -262,5 +262,6 @@ VAR_PROTECTED/list/resistances var/custom_footstep = FOOTSTEP_MOB_SHOE + var/vent_crawl_time = 4.5 SECONDS // Time to animate entering a vent VAR_PRIVATE/is_motion_tracking = FALSE // Prevent multiple unsubs and resubs, also used to check if the vis layer is enabled, use has_motiontracking() to get externally. VAR_PRIVATE/wants_to_see_motion_echos = TRUE diff --git a/code/modules/ventcrawl/ventcrawl.dm b/code/modules/ventcrawl/ventcrawl.dm index f9dd5444e7..bef1b7f4af 100644 --- a/code/modules/ventcrawl/ventcrawl.dm +++ b/code/modules/ventcrawl/ventcrawl.dm @@ -21,8 +21,8 @@ var/list/ventcrawl_machinery = list( /mob/living/var/list/icon/pipes_shown = list() /mob/living/var/last_played_vent -/mob/living/var/is_ventcrawling = 0 -/mob/living/var/prepping_to_ventcrawl = 0 +/mob/living/var/is_ventcrawling = FALSE +/mob/living/var/prepping_to_ventcrawl = FALSE /mob/var/next_play_vent = 0 /mob/living/proc/can_ventcrawl() @@ -180,8 +180,12 @@ var/list/ventcrawl_machinery = list( break if(vent_found) - if(vent_found.network && (vent_found.network.normal_members.len || vent_found.network.line_members.len)) + if(SEND_SIGNAL(src,COMSIG_MOB_VENTCRAWL_CHECK,vent_found) & VENT_CRAWL_BLOCK_ENTRY) + return + if(SEND_SIGNAL(vent_found,COMSIG_VENT_CRAWLER_CHECK,src) & VENT_CRAWL_BLOCK_ENTRY) + return + if(vent_found.network && (vent_found.network.normal_members.len || vent_found.network.line_members.len)) to_chat(src, "You begin climbing into the ventilation system...") if(vent_found.air_contents && !issilicon(src)) @@ -202,15 +206,20 @@ var/list/ventcrawl_machinery = list( to_chat(src, span_warning("You feel a strong drag pulling you into the vent.")) if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE) to_chat(src, span_warning("You feel a strong current pushing you away from the vent.")) - if(HAZARD_HIGH_PRESSURE to INFINITY) + if(HAZARD_HIGH_PRESSURE to (HAZARD_HIGH_PRESSURE*2)) to_chat(src, span_danger("You feel a roaring wind pushing you away from the vent!")) + if((HAZARD_HIGH_PRESSURE*2) to INFINITY) // A little too crazy to enter + to_chat(src, span_danger("You're pushed away by the extreme pressure in the vent!")) + return - fade_towards(vent_found,45) - prepping_to_ventcrawl = 1 - spawn(50) - prepping_to_ventcrawl = 0 - if(!do_after(src, 45, target = src)) + // Handle animation delay + fade_towards(vent_found, vent_crawl_time) + prepping_to_ventcrawl = TRUE + if(!do_after(src, vent_crawl_time, target = src)) + prepping_to_ventcrawl = FALSE return + prepping_to_ventcrawl = FALSE + if(!can_ventcrawl()) return @@ -218,7 +227,8 @@ var/list/ventcrawl_machinery = list( forceMove(vent_found) add_ventcrawl(vent_found) - + SEND_SIGNAL(src,COMSIG_MOB_VENTCRAWL_START,vent_found) + SEND_SIGNAL(vent_found,COMSIG_VENT_CRAWLER_ENTERED,src) else to_chat(src, "This vent is not connected to anything.") @@ -226,7 +236,7 @@ var/list/ventcrawl_machinery = list( to_chat(src, "You must be standing on or beside an air vent to enter it.") /mob/living/proc/add_ventcrawl(obj/machinery/atmospherics/starting_machine) - is_ventcrawling = 1 + is_ventcrawling = TRUE //candrop = 0 var/datum/pipe_network/network = starting_machine.return_network(starting_machine) if(!network) @@ -242,7 +252,7 @@ var/list/ventcrawl_machinery = list( client.screen += GLOB.global_hud.centermarker /mob/living/proc/remove_ventcrawl() - is_ventcrawling = 0 + is_ventcrawling = FALSE //candrop = 1 if(client) for(var/image/current_image in pipes_shown) diff --git a/code/modules/ventcrawl/ventcrawl_atmospherics.dm b/code/modules/ventcrawl/ventcrawl_atmospherics.dm index 710f296d88..8b5c11c386 100644 --- a/code/modules/ventcrawl/ventcrawl_atmospherics.dm +++ b/code/modules/ventcrawl/ventcrawl_atmospherics.dm @@ -4,6 +4,8 @@ for(var/mob/living/M in src) //ventcrawling is serious business M.remove_ventcrawl() M.forceMove(get_turf(src)) + SEND_SIGNAL(M,COMSIG_MOB_VENTCRAWL_END,src) + SEND_SIGNAL(src,COMSIG_VENT_CRAWLER_EXITED,M) if(pipe_image) for(var/mob/living/M in GLOB.player_list) if(M.client) @@ -43,6 +45,7 @@ if(world.time > user.next_play_vent) user.next_play_vent = world.time+30 var/turf/T = get_turf(src) + SSmotiontracker.ping(T,40) // Teshari rattler playsound(T, 'sound/machines/ventcrawl.ogg', 50, 1, -3) var/message = pick( prob(90);"* clunk *", @@ -56,6 +59,8 @@ if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery) && src.can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent user.remove_ventcrawl() user.forceMove(src.loc) + SEND_SIGNAL(user,COMSIG_MOB_VENTCRAWL_END,src) + SEND_SIGNAL(src,COMSIG_VENT_CRAWLER_EXITED,user) user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") user.canmove = 0 spawn(1) diff --git a/code/modules/vore/eating/belly_obj_vr.dm b/code/modules/vore/eating/belly_obj_vr.dm index f135846d0c..95b43d6478 100644 --- a/code/modules/vore/eating/belly_obj_vr.dm +++ b/code/modules/vore/eating/belly_obj_vr.dm @@ -883,6 +883,11 @@ if (!(M in contents)) return 0 // They weren't in this belly anyway + // Ventcrawlings will explode their vent to avoid exploits + if(istype(owner.loc,/obj/machinery/atmospherics)) + var/obj/machinery/atmospherics/our_pipe = owner.loc + our_pipe.blowout(owner) + if(istype(M, /mob/living/simple_mob/vore/morph/dominated_prey)) var/mob/living/simple_mob/vore/morph/dominated_prey/p = M p.undo_prey_takeover(FALSE) diff --git a/icons/atmos/vent_scrubber.dmi b/icons/atmos/vent_scrubber.dmi index ddf9b159b7b43a7e9cdd3a312dc64c04e371fd5c..eb956f373501e6bd1a7060172a072ed6f5898a1c 100644 GIT binary patch literal 8207 zcmb7pc|4Ts-~VM0B9bB+rU+$8k}b=e7D+_*WEmxmNMskygfi{W>Wr z34uUt*|NpQ$EUiw`ug?jN=iy^-@ZM4`gB!Q)%o-1Z{50OZ*Q-wtE*>v%phjjF_o{Q zqhn!VamdTXl>hM_e`MdjeQIiIj*gB20RdO8T(LP~eb)aB27@s+GPJU?+O=yJFh>F8 z8DQoO3|#>7G&mRtj9h{737~x_A|e7H_5cfCU~vkVc!0f5Ku!fn$pKw!pt2jts{-94 zKvEXSYz9hNK-~<$guxoY!vY}~P*4Y&7QoyG2#bQXGC*Dps2PJb5@4SfFmeN%)q&Ol zAR!GlZUtNS0BtLvZx1xg0YV?hZ(k!S3N|Q%wd;YH6p)Ys>*Rsr4xqFX>^ zU~~#t#R2;iV0;=#DFB6?K-B^$XaUO$02vAleZVd!V2A>T(t5U3vnLc&0FEs&6fW)EN@KwJiF+zz&xfz8_BKqNRA z4R$#L)3ZR|9qdD=2!jk^a8m@ti-0r{kSh!>ivXZu1r&EBiU5o-2oM4GFc1I(w?)7u z=uhjFfs{Ou+@uc!2mpdaz)=`5f`Ri85H@LS0bmCV91sGY!oU*-)&Kxa0s({O0Ym{X zg8?5A5CeUkFz|r^aR3wnFo6MAVc;qR)&l?+0@sAWbzy)N2Ier}2Tev8_z3|801Sk{ zF=#TvAY2%ngbpD9%>@p?F(Cj!&JG4lVL%TClmOTWz-9op!@za`{(yn~Fd#7lt_rp? z!pYtl{6XzK3k{ksxtxeNUY z?ym;JqdFwJoh=~jcIsOb11Ez{gQHsuMl%~TUA8*!G=90Y&g9**y*qbS?XX1Z9!pL! zP_KNp`3Ww?plpY}@uo(j#;4n42$!561O(cjRzFfAy;1dc^PzA4M_!l5(;~LTwX7%P zwmOG9iEC*c)!o_JGWINX4&ZjJ1**v38J@RgK*xX!o&k&crVTssZ?tOY+3)pOF`1~A??K0aR^Cy?hZz|_n=$RpXeZ9mu6M*!& zm3_oA;LC#qRyt=+$QuPC#Wk@xO?T!|!h||l+*KJB^Oy%5&2%pd-{TQNYZ`=SqAXYb za8A# z{p95zL033z6=G5+&mvCj24;HGj=9a|73BKM?4{|R>JTD^-AZJfk=ixAId3_av0}00 ziLd8#bWR`1kl88bKvHduPl!%Xn0S8h{k^8y3w55el6E~RggjQ-K=ciONnN1E;TCiUcreDEFp_l zqFa=T*CytO_vj{UaaOd?0_Xft>_>WK?B8E)iM$^@^!UY;?me#+dfoFA7qPR|9|aFo z@%S5Wz&cCo7l|w3_5;U`3CLz&X41w#=jx&A;BqSQ&by$&g&{0vzI-D0^PL*U zp00uioc>+4fVye-jlOky^+oLnhKkjRxny zw%PflHideXBj}vw&lxynrFN%zWw?d!j#(MR1OB>qhpGGXKI0!f5?L6+U_wJ^AN-q1 zEVH)+v$xgEz88ojMgZ@v!<0~MJLxP|-myGBBAPku-QIN8UuB?#Bs0{-c?H2LIX*O1 zML6h%KF;&pzm_{&u+!{+*6+U;l1X!Q(*G1Vj`$?rVTz{7Oc0t0W*C1i!3?3z-M>DR zsk!+&PDpR#+60dCj$wnJX_cowj)|?TWH04zL&VL`%c@B$>Yr|$ANjD~(ya)A@Nif7 z-gPnldd=?JH{gXr2>Ff7-7(j7C~BtWAJ(!zXQx|S9iHM$Nn7X)KbAIgvHZAbKoPxP zjN7*5?8VUQ1Jq%uZ0zEQ5OQ>0{|WXs^|1{7e&@9kwsjFlKhYSj)|(NzKNr1&Xqi>>S+NyQ6q#eWwo2LzElH*Fd&fkOJd=hr zRgU}(o}6kLSKk43%B=6ecJ~2=n5#l+&m_%hU-T0Vzjt@HPVc6EDX+f5A=?zy{h_{; zZnu;UH?Xge$j9xzAobIM_8LiT`iu8f9x>R3Yja!I(5D?IwI;e^L`@2j@xDItMykc( z?`vhh8^H~hE8c&X+v|mqG%}A&^fu|z6Y0XPd~H(XUmZ)cgf9k_r{PV%A}+o_u^h4K z-;!z-RRd!OpRul=+*X{Cu+KIWc1uilqUss)=YHZ=R{CaaO(*{CXB;I0xyaB#1i7^!Y~X4?MrJvB#1F!IHiPT(NH(oiEI5|7xbMKl^jR4sJ2KqZJW8lWw}~Y0kAT zw`2Cox>$CXs^1<>2-khsoLqJFW>%2mwx%G7$1=!7v5qQ{kJec{{Zq(yIkL0}tRn5D z7^0LKaH)I^?WG55-EQX37=g(i(%^hx6jj&hDziSozWDJfM~>S8XkWfIehow{l(Kr-)>$lShWfxt0%XE4hg1N$f{dQ%eIBN^3u)gtK>an{>YA+&u-lxA*y`W8a;^cu0xw&yQ`*Q!Q z1t)C-hF7bgrRuh-X3w4j_GRkLFOyE#4jVxudqea4C60;=w6BHX7*A?O{LH}IZ`uAg z*tH$9m%3gAGSHW6tA21j$okJP#Ox6D<$_N>vh&*b&9<|#AK`Pcsb#q}7@c<$n=bx^ zym-1^)4t#c%P);9tI2_mR}Mo(qTldu<3$$fXX-Z1=69nEZ7N!_${*1xjjRc8Q!Y|8;L!Vh)2+D+=R2I46y2Pi4#PtgYSmB-Tsi^&7{xhzL-jA7-l23^g+stCz zgVCO-PQ=CG9(4J;nwCGXnZ;c7C1GL>Rx#JhX96b16}><&N5LF1m_;>s2sE4qP&oPslEJ+&g zJ0><={tqH`Y9%v#BTtfE=7L#nZE-iFSKcDfDsAi;&Y0LA_QcU$SP1!dagsiZ^hMFT zTu2_~Sd%5$9i+1lh+7|F90B*10$W`yx}q>WID(4C7sWp{K$g7KMo!Plz`57Q^hSE| z=#$b!0&*)WD|*LeG09Qs+0687e)Cn=AwsXxK-#7?^yG1LAh64+;5H{|Dna0vOU1#V zSHu#ex#jyS3!81z8@}OVS@*qi<8|^y?YcQi8aF+zd>qkfoDU*Q8pGRR8iBj`#{|WI z-lo1Nj7&$%Uh4Q#MIOC&V5)lkvsf=x@>*)flAYbV{FI$p$(+*mujb#V0V?-~>q9!c zo=pR;0P1SkL}8)@$qEx!RDdadM6YzK+XOvR{&okj1kpD0Dp3o$>Q|=^N%ha zO!-Y=#E%)|OT}xr+w>lgMVvVv1G~vbOVzzDs>_szma0R5|NJbsX6R*(?8QLcQ_#nz zPuT7V3)6kv3>Cl~KkR0?4fW{%s>>ZoI`x5a1kPcl!u4T_t7-7gYE2v*7_SDF5(s*U{d zE0b)Q+B$a<&MGDaD9ofSD+-7oA4{Nqf!H5sTH;Y*b|1Zs_=8c+SQ=#WCgXZ3(fzX< z;+MS>2ai&sb*1S9{U+uhMD_&)QCwWLZ>PjRg)xJ(JQy0w?Q2_{__IYU;y$*@^hk%7 z@v(}yh`KkyKO!F>TRI&oo?wPUa?nwDYWUf`Ndofs)EWBbjhwIU$T~!yq@VmcA;!>( zQ)lCe++A7Z+KKXhycjpXWGqOU6_T@goiR&g(k5gD_~&mW+|BjekXc{iQ8 z-y9an+fhQla(nn1i_C^=Lr=u~`Rjr!3z^;@y_tjCi`maPv#Gzr3k77qWiD@e@kRlS z9FI2pH5bXNT1z9i4x$1R3Bzr39Tx}<_sFgT+2lxuSQ_TT#2fNY0o{{oKXiq>{Y=J8 zwEwggzqDi^epafki4p{nxk?rpqW(BLOfEd>!EpO|NL0^V_8fU7GX zBE;*C?KI3yPplNbFpgRvC8%TUrP|a&{71|Cyau`sQ=$bDuuA!uSWGl&e00|IszA`g zDGs5-xSjZTGX;Y`g^976r^h`<%pSsuv6+q&(Zz!r4!k$#s-2+y9OKNs?pG&zUtFyT zQUEb2d0DY@C${Y+_HbNWh99)aST}m1-Ck0@1m|PiiFNA=vgXDg$LqYy2^E$p2$HaZ z1Yx5o`XJgXA_i~Pv6Hs=dG>@Ucegw#%+72K>j=`vp8=<$Engec;V@W5L2 zVPo?5sorocDhl#G&v3o;eB#z&Nrg5|M$PGe#Nb#8%n~chE4CZSbms4`Z-$b;W)*=RlQy}*Pr38g`)YiOsp&hIK2P%vC z=RO}YnQp6cuxfthLgY9JMh&xSGy;?e^8BDhLe+uz=kc=gzn#xZBz1T!ICc+tZKigm zs-ovlC;0y37u$*2w zj9&kJ^ALR8@BY`#yPBHC)@e3<5fgAfVlT!W8xwDG-SUiyDRkjbHgoPQ5Lh3^K6qTs z4zWU78EB6->ik6wkPjtNWq$i00rh|Is6X9~IK7$$Iu0EDDc1QKlJ@_&9ftnbWuOqu zkYjp7g5I8iY)HV$%Gu_Gy*#5o0_DAaI8)A?d;ci3bp^P>;+W4h_gpYhJ}FZ@a4K3s z&qE8>ILdy;xzGmfRpu@H{^6B)rxdPa@E=t_(%^|4|? z)==BMEF->Ms}*`?)SIZMzNAtwTfu#KYuuEpW^W})Vu)4Xt9uC3J-Qpoo}p(V@g^Mt z;76PJtgoSqHn?qA_4k5xW^p9S`apMA6Vu!Q{W0@iC^JmmQ+9UMekvbQ@R z8!}n@!_M|;HS9B-J5eRIs2TeM<%FC^R=HL5?8W;3au!GfFOQfv>)uyl?QR}ItHfIp zM;-rT`&E1vyfTp0qo=-vyj_32n2qqHCoV+SRZ#W6tooq`*vLlr5196+D0&26P8+p{ z*W+`NDe|+&OBogZ2s!Gl7pjZDZ${|4hl5xhcX@k5CwE&hjgTH4v5$L;z!BIao+d7~ zPXKNQT132IKeIn@&Vh>dM3W+a#{f?>aSbi%x&DeF6oyv}$26OUdu}6A z8!pMumv&UwwTkFVR+y~p@^XTVe?-7g%j&q`+TAZuFu(`!l%Qbn*JwR4{#S}Ds;J1S zIo3;3j#L%4Fi;8Y7-WQYGNB#g>tx`Lh3-!GYH5@#^s4!?|38_)->znHkAhz9k=00y z6$)Q9x>;Zsc~4>Tt;XwHbj&q>zGL5i})-5XRU{zpDY<9^1)WQsMOg!Kfi zyY(BkeO{@pZ;`^&&jm@rdh6K#o`wm?X($cPl30Q?TqbZyi>xg|SbAztiv>P!%tG%Y z*{PscV3V-y!Yf2}NBNSX^#edKht_;O3m66Bfw6B&we5yL&jDym4BDZGi*>s zh&roUiAbH~MS9lh^AN?iR!S9U?+BV%{SucZu{dy;biNjbweB zlrbIWPfnnqSjBD?t!N_RG?Sby#;~` zX78G5&UA{KrFF_^N1b>^HCy{b`T0!8T^qB6M*d@^s$VQ=>8jZFN(8ZXM!Yg$%QHPl54QT#hr$K*ndq^p8Ry>Fc$SEq50O?Q7bZcHN~q)oka zU+}t~ooAO|Eexl5Q)JoZ_vF=X_`hTIXZNC3yxXs#R^FwcNcUwZS`gZboa?b+v!lx+<>JeJ#>}3Z zNtCwy;6(0oW?FENAPl1(bY=2gDJA={iN%=)T|TFHuKsvT=s4=yZu7#ysouIy3Y!1p zU9IpcCS%|8d+zYxg|qPw$f1z`4-)ir%J>vdG{%Sm<=tv!tjTKLB@lnD3_{RcbieGn z59*z9f~(wBk?dcZ(}heAC3YKfc;o({IA0pntKJck*;*9UwvB>0D@DIQiEU3Okl?GH7e}237k~|I*1jUS)>s zd3oqLmK(;fw3;`CKZ>7N?nZH4CzX?jSpWHzggJGNizKR7sGPa8cnbQ_6xdkVA1XiK GoA_VnD5j$T literal 8106 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 AW&i*H