From cd515971a1bf982f37ee93381ee39c0f33e5c4be Mon Sep 17 00:00:00 2001 From: Kylerace Date: Tue, 29 Mar 2022 22:47:15 -0700 Subject: [PATCH] makes vent scrubbers only activate if a gas they filter is on their tile, again! alive edition (#65591) Alsonce scrubbers only wake up if something they can filter is on the tile an existing bug where scrubbers dont filter very small but non garbage collectable amounts of a gas becomes a big issue, as in without changing scrubbing rates you can breathe once on a tile with a scrubber set to scrub co2 and that co2 wont go away barring any other factors and the scrubber wont go to sleep. so now with the idea of rohesie and permission of lemon i changed how scrubbers removed small molar amounts of gas from their turf. now scrubbers will look through 100% of the turfs air mix for filtering, but will only remove up to gas moles * (scrubber volume / turf volume) * (gas moles / total filterable moles) moles from each filterable gas in the turfs mix unless that amount is less than either MOLAR_ACCURACY * 100 or the number of moles of that filterable gas, in which case all of the moles of that gas are subtracted from the mix. this is to make it easier for the scrubber to remove very small amounts of gas with filters without changing how fast they scrub large amounts of gas, thus making scrubbers able to go to sleep faster only after a gas has been reduced to near zero scrubbers are the biggest proportion of SSair's machine processing cost which is a non trivial amount of SSair's total cost. now they will only do most of their work if they can actually scrub anything on the tile which is a minority of the time. --- _maps/map_files/generic/CentCom.dmm | 10 +- code/controllers/subsystem/overlays.dm | 28 +-- code/datums/components/_component.dm | 20 +- code/game/objects/buckling.dm | 3 +- code/game/turfs/turf.dm | 6 +- .../components/unary_devices/vent_scrubber.dm | 175 +++++++++++++++--- .../atmospherics/machinery/other/meter.dm | 61 +++--- 7 files changed, 216 insertions(+), 87 deletions(-) diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 921bd48b6d8..c4517f6a7b7 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -327,7 +327,7 @@ color = "#596479"; dir = 1 }, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ +/obj/machinery/atmospherics/components/unary/vent_pump{ dir = 8 }, /turf/open/floor/iron/grimy, @@ -4988,7 +4988,7 @@ /area/syndicate_mothership/control) "ri" = ( /obj/machinery/firealarm/directional/east, -/obj/machinery/atmospherics/components/unary/vent_pump/on{ +/obj/machinery/atmospherics/components/unary/vent_pump{ dir = 8 }, /turf/open/floor/iron/grimy, @@ -12909,10 +12909,10 @@ /turf/open/floor/iron/dark, /area/tdome/observation) "VV" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on{ +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/components/unary/vent_pump{ dir = 8 }, -/obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/centcom/admin/storage) "VW" = ( @@ -13575,7 +13575,7 @@ /turf/open/floor/wood/tile, /area/centcom/holding) "XS" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on{ +/obj/machinery/atmospherics/components/unary/vent_pump{ dir = 4 }, /turf/open/floor/wood, diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index f7b1be0fcf6..bc38254f1d3 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -39,21 +39,21 @@ SUBSYSTEM_DEF(overlays) count = 0 //so if we runtime on the Cut, we don't try again. queue.Cut(1,c+1) - for (var/thing in queue) + for (var/atom/atom_to_compile as anything in queue) count++ - if(thing) - var/atom/A = thing - if(A.overlays.len >= MAX_ATOM_OVERLAYS) - //Break it real GOOD - stack_trace("Too many overlays on [A.type] - [A.overlays.len], refusing to update and cutting") - A.overlays.Cut() - continue - STAT_START_STOPWATCH - COMPILE_OVERLAYS(A) - UNSETEMPTY(A.add_overlays) - UNSETEMPTY(A.remove_overlays) - STAT_STOP_STOPWATCH - STAT_LOG_ENTRY(stats, A.type) + if(!atom_to_compile) + continue + if(length(atom_to_compile.overlays) >= MAX_ATOM_OVERLAYS) + //Break it real GOOD + stack_trace("Too many overlays on [atom_to_compile.type] - [length(atom_to_compile.overlays)], refusing to update and cutting") + atom_to_compile.overlays.Cut() + continue + STAT_START_STOPWATCH + COMPILE_OVERLAYS(atom_to_compile) + UNSETEMPTY(atom_to_compile.add_overlays) + UNSETEMPTY(atom_to_compile.remove_overlays) + STAT_STOP_STOPWATCH + STAT_LOG_ENTRY(stats, atom_to_compile.type) if(mc_check) if(MC_TICK_CHECK) break diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index e5ad2e3d7d8..7f527e2b4ed 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -123,20 +123,24 @@ * Internal proc to handle behaviour when being removed from a parent */ /datum/component/proc/_RemoveFromParent() - var/datum/P = parent - var/list/dc = P.datum_components + var/datum/parent = src.parent + var/list/parents_components = parent.datum_components for(var/I in _GetInverseTypeList()) - var/list/components_of_type = dc[I] + var/list/components_of_type = parents_components[I] + if(length(components_of_type)) // var/list/subtracted = components_of_type - src + if(subtracted.len == 1) //only 1 guy left - dc[I] = subtracted[1] //make him special + parents_components[I] = subtracted[1] //make him special else - dc[I] = subtracted + parents_components[I] = subtracted + else //just us - dc -= I - if(!dc.len) - P.datum_components = null + parents_components -= I + + if(!parents_components.len) + parent.datum_components = null UnregisterFromParent() diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 21beefc7f42..0a4e2bffa94 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -73,10 +73,9 @@ * Returns TRUE if there are mobs buckled to this atom and FALSE otherwise */ /atom/movable/proc/has_buckled_mobs() - if(!buckled_mobs) - return FALSE if(length(buckled_mobs)) return TRUE + return FALSE /** * Set a mob as buckled to src diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 66dfb052a18..a430e5f6459 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -22,8 +22,10 @@ GLOBAL_LIST_EMPTY(station_turfs) var/list/baseturfs = /turf/baseturf_bottom var/temperature = T20C - var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed - var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to + ///Used for fire, if a melting temperature was reached, it will be destroyed + var/to_be_destroyed = 0 + ///The max temperature of the fire which it was subjected to + var/max_fire_temperature_sustained = 0 var/blocks_air = FALSE diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index 0b1a98f4496..d2b1d4bfee4 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -16,11 +16,12 @@ shift_underlay_only = FALSE pipe_state = "scrubber" vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED + processing_flags = NONE ///The mode of the scrubber (SCRUBBING or SIPHONING) var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing ///The list of gases we are filtering - var/filter_types = list(/datum/gas/carbon_dioxide) + var/list/filter_types = list(/datum/gas/carbon_dioxide) ///Rate of the scrubber to remove gases from the air var/volume_rate = 200 ///is this scrubber acting on the 3x3 area around it. @@ -37,6 +38,8 @@ ///Radio connection from the air alarm var/radio_filter_in + COOLDOWN_DECLARE(check_turfs_cooldown) + /obj/machinery/atmospherics/components/unary/vent_scrubber/New() if(!id_tag) id_tag = SSnetworks.assign_random_name() @@ -46,6 +49,10 @@ filter_types -= to_filter filter_types += gas_id2path(to_filter) +/obj/machinery/atmospherics/components/unary/vent_scrubber/Initialize(mapload) + . = ..() + AddElement(/datum/element/atmos_sensitive, mapload) + /obj/machinery/atmospherics/components/unary/vent_scrubber/Destroy() var/area/scrub_area = get_area(src) if(scrub_area) @@ -57,6 +64,80 @@ adjacent_turfs.Cut() return ..() +///adds a gas or list of gases to our filter_types. used so that the scrubber can check if its supposed to be processing after each change +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/add_filters(filter_or_filters) + if(!islist(filter_or_filters)) + filter_or_filters = list(filter_or_filters) + + for(var/gas_to_filter in filter_or_filters) + var/translated_gas = istext(gas_to_filter) ? gas_id2path(gas_to_filter) : gas_to_filter + + if(ispath(translated_gas, /datum/gas)) + filter_types |= translated_gas + continue + + var/turf/open/our_turf = get_turf(src) + + if(!isopenturf(our_turf)) + return FALSE + + var/datum/gas_mixture/turf_gas = our_turf.air + if(!turf_gas) + return FALSE + + check_atmos_process(our_turf, turf_gas, turf_gas.temperature) + return TRUE + +///remove a gas or list of gases from our filter_types.used so that the scrubber can check if its supposed to be processing after each change +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/remove_filters(filter_or_filters) + if(!islist(filter_or_filters)) + filter_or_filters = list(filter_or_filters) + + for(var/gas_to_filter in filter_or_filters) + var/translated_gas = istext(gas_to_filter) ? gas_id2path(gas_to_filter) : gas_to_filter + + if(ispath(translated_gas, /datum/gas)) + filter_types -= translated_gas + continue + + var/turf/open/our_turf = get_turf(src) + var/datum/gas_mixture/turf_gas + + if(isopenturf(our_turf)) + turf_gas = our_turf.air + + if(!turf_gas) + return FALSE + + check_atmos_process(our_turf, turf_gas, turf_gas.temperature) + return TRUE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/toggle_filters(filter_or_filters) + if(!islist(filter_or_filters)) + filter_or_filters = list(filter_or_filters) + + for(var/gas_to_filter in filter_or_filters) + var/translated_gas = istext(gas_to_filter) ? gas_id2path(gas_to_filter) : gas_to_filter + + if(ispath(translated_gas, /datum/gas)) + if(translated_gas in filter_types) + filter_types -= translated_gas + else + filter_types |= translated_gas + + var/turf/open/our_turf = get_turf(src) + + if(!isopenturf(our_turf)) + return FALSE + + var/datum/gas_mixture/turf_gas = our_turf.air + + if(!turf_gas) + return FALSE + + check_atmos_process(our_turf, turf_gas, turf_gas.temperature) + return TRUE + /obj/machinery/atmospherics/components/unary/vent_scrubber/update_icon_nopipes() cut_overlays() if(showpipe) @@ -135,7 +216,24 @@ check_turfs() . = ..() -/obj/machinery/atmospherics/components/unary/vent_scrubber/process_atmos() +/obj/machinery/atmospherics/components/unary/vent_scrubber/should_atmos_process(datum/gas_mixture/air, exposed_temperature) + if(welded || !is_operational) + return FALSE + if(!nodes[1] || !on || (!filter_types && scrubbing != SIPHONING)) + on = FALSE + return FALSE + + var/list/changed_gas = air.gases + + if(!changed_gas) + return FALSE + + if(scrubbing == SIPHONING || length(filter_types & changed_gas)) + return TRUE + + return FALSE + +/obj/machinery/atmospherics/components/unary/vent_scrubber/atmos_expose(datum/gas_mixture/air, exposed_temperature) if(welded || !is_operational) return FALSE if(!nodes[1] || !on) @@ -146,10 +244,17 @@ return scrub(us) if(widenet) + if(COOLDOWN_FINISHED(src, check_turfs_cooldown)) + check_turfs() + COOLDOWN_START(src, check_turfs_cooldown, 2 SECONDS) + for(var/turf/tile in adjacent_turfs) scrub(tile) return TRUE +///filtered gases at or below this amount automatically get removed from the mix +#define MINIMUM_MOLES_TO_SCRUB MOLAR_ACCURACY*100 + /obj/machinery/atmospherics/components/unary/vent_scrubber/proc/scrub(turf/tile) if(!istype(tile)) return FALSE @@ -162,32 +267,34 @@ if(scrubbing == SCRUBBING) if(length(env_gases & filter_types)) - var/transfer_moles = min(1, volume_rate / environment.volume) * environment.total_moles() - - //Take a gas sample - var/datum/gas_mixture/removed = tile.remove_air(transfer_moles) - - //Nothing left to remove from the tile - if(isnull(removed)) - return FALSE - - var/list/removed_gases = removed.gases - - //Filter it + ///contains all of the gas we're sucking out of the tile, gets put into our parent pipenet var/datum/gas_mixture/filtered_out = new var/list/filtered_gases = filtered_out.gases - filtered_out.temperature = removed.temperature + filtered_out.temperature = environment.temperature - for(var/gas in filter_types & removed_gases) + ///maximum percentage of the turfs gas we can filter + var/removal_ratio = min(1, volume_rate / environment.volume) + + var/total_moles_to_remove = 0 + for(var/gas in filter_types & env_gases) + total_moles_to_remove += env_gases[gas][MOLES] + + if(total_moles_to_remove == 0)//sometimes this gets non gc'd values + environment.garbage_collect() + return FALSE + + for(var/gas in filter_types & env_gases) filtered_out.add_gas(gas) - filtered_gases[gas][MOLES] = removed_gases[gas][MOLES] - removed_gases[gas][MOLES] = 0 + //take this gases portion of removal_ratio of the turfs air, or all of that gas if less than or equal to MINIMUM_MOLES_TO_SCRUB + var/transfered_moles = max(QUANTIZE(env_gases[gas][MOLES] * removal_ratio * (env_gases[gas][MOLES] / total_moles_to_remove)), min(MINIMUM_MOLES_TO_SCRUB, env_gases[gas][MOLES])) - removed.garbage_collect() + filtered_gases[gas][MOLES] = transfered_moles + env_gases[gas][MOLES] -= transfered_moles + + environment.garbage_collect() //Remix the resulting gases air_contents.merge(filtered_out) - tile.assume_air(removed) update_parents() else //Just siphoning all air @@ -201,11 +308,7 @@ return TRUE -//There is no easy way for an object to be notified of changes to atmos can pass flags -// So we check every machinery process (2 seconds) -/obj/machinery/atmospherics/components/unary/vent_scrubber/process() - if(widenet) - check_turfs() +#undef MINIMUM_MOLES_TO_SCRUB ///we populate a list of turfs with nonatmos-blocked cardinal turfs AND /// diagonal turfs that can share atmos with *both* of the cardinal turfs @@ -222,12 +325,20 @@ var/old_scrubbing = scrubbing var/old_filter_length = length(filter_types) + ///whether we should attempt to start processing due to settings allowing us to take gas out of our environment + var/try_start_processing = FALSE + + var/turf/open/our_turf = get_turf(src) + var/datum/gas_mixture/turf_gas = our_turf?.air + var/atom/signal_sender = signal.data["user"] if("power" in signal.data) on = text2num(signal.data["power"]) + try_start_processing = TRUE if("power_toggle" in signal.data) on = !on + try_start_processing = TRUE if("widenet" in signal.data) widenet = text2num(signal.data["widenet"]) @@ -236,18 +347,20 @@ if("scrubbing" in signal.data) scrubbing = text2num(signal.data["scrubbing"]) + try_start_processing = TRUE if("toggle_scrubbing" in signal.data) scrubbing = !scrubbing + try_start_processing = TRUE + if(scrubbing != old_scrubbing) investigate_log(" was toggled to [scrubbing ? "scrubbing" : "siphon"] mode by [key_name(signal_sender)]",INVESTIGATE_ATMOS) if("toggle_filter" in signal.data) - filter_types ^= gas_id2path(signal.data["toggle_filter"]) + toggle_filters(signal.data["toggle_filter"]) if("set_filters" in signal.data) filter_types = list() - for(var/gas in signal.data["set_filters"]) - filter_types += gas_id2path(gas) + add_filters(signal.data["set_filters"]) if("init" in signal.data) name = signal.data["init"] @@ -260,6 +373,12 @@ broadcast_status() update_appearance() + if(!our_turf || !turf_gas) + try_start_processing = FALSE + + if(try_start_processing)//check if our changes should make us start processing + check_atmos_process(our_turf, turf_gas, turf_gas.temperature) + if(length(filter_types) == old_filter_length && old_scrubbing == scrubbing && old_widenet == widenet) return diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm index b16231fe75e..192c8ff2458 100644 --- a/code/modules/atmospherics/machinery/other/meter.dm +++ b/code/modules/atmospherics/machinery/other/meter.dm @@ -7,13 +7,13 @@ power_channel = AREA_USAGE_ENVIRON use_power = IDLE_POWER_USE idle_power_usage = 2 - active_power_usage = 4 + active_power_usage = 9 max_integrity = 150 armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 0, BIO = 100, FIRE = 40, ACID = 0) greyscale_config = /datum/greyscale_config/meter greyscale_colors = COLOR_GRAY ///The pipe we are attaching to - var/atom/target + var/obj/machinery/atmospherics/pipe/target ///The piping layer of the target var/target_layer = PIPING_LAYER_DEFAULT @@ -25,7 +25,9 @@ /obj/machinery/meter/Initialize(mapload, new_piping_layer) if(!isnull(new_piping_layer)) target_layer = new_piping_layer + SSair.start_processing_machine(src) + if(!target) reattach_to_layer() AddComponent(/datum/component/usb_port, list( @@ -46,23 +48,20 @@ target_layer = new_layer PIPING_LAYER_DOUBLE_SHIFT(src, target_layer) +/obj/machinery/meter/on_set_is_operational(old_value) + if(is_operational) + SSair.start_processing_machine(src)//dont set icon_state here because it will be reset on next process() if it ever happens + else + icon_state = "meter" + SSair.stop_processing_machine(src) + /obj/machinery/meter/process_atmos() - if(!(target?.flags_1 & INITIALIZED_1)) - icon_state = "meter" - return FALSE - - if(machine_stat & (BROKEN|NOPOWER)) - icon_state = "meter" - return FALSE - - use_power(5) - - var/datum/gas_mixture/environment = target.return_air() - if(!environment) + var/datum/gas_mixture/pipe_air = target.return_air() + if(!pipe_air) icon_state = "meter0" return FALSE - var/env_pressure = environment.return_pressure() + var/env_pressure = pipe_air.return_pressure() if(env_pressure <= 0.15 * ONE_ATMOSPHERE) icon_state = "meter0" else if(env_pressure <= 1.8 * ONE_ATMOSPHERE) @@ -77,32 +76,38 @@ else icon_state = "meter4" - var/env_temperature = environment.temperature + var/env_temperature = pipe_air.temperature + + var/new_greyscale = greyscale_colors + if(env_pressure == 0 || env_temperature == 0) - greyscale_colors = COLOR_GRAY + new_greyscale = COLOR_GRAY else switch(env_temperature) if(BODYTEMP_HEAT_WARNING_3 to INFINITY) - greyscale_colors = COLOR_RED + new_greyscale = COLOR_RED if(BODYTEMP_HEAT_WARNING_2 to BODYTEMP_HEAT_WARNING_3) - greyscale_colors = COLOR_ORANGE + new_greyscale = COLOR_ORANGE if(BODYTEMP_HEAT_WARNING_1 to BODYTEMP_HEAT_WARNING_2) - greyscale_colors = COLOR_YELLOW + new_greyscale = COLOR_YELLOW if(BODYTEMP_COLD_WARNING_1 to BODYTEMP_HEAT_WARNING_1) - greyscale_colors = COLOR_VIBRANT_LIME + new_greyscale = COLOR_VIBRANT_LIME if(BODYTEMP_COLD_WARNING_2 to BODYTEMP_COLD_WARNING_1) - greyscale_colors = COLOR_CYAN + new_greyscale = COLOR_CYAN if(BODYTEMP_COLD_WARNING_3 to BODYTEMP_COLD_WARNING_2) - greyscale_colors = COLOR_BLUE + new_greyscale = COLOR_BLUE else - greyscale_colors = COLOR_VIOLET - set_greyscale(colors=greyscale_colors) + new_greyscale = COLOR_VIOLET + + if(new_greyscale != greyscale_colors)//dont update if nothing has changed since last update + greyscale_colors = new_greyscale + set_greyscale(greyscale_colors) /obj/machinery/meter/proc/status() if (target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - . = "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)] K ([round(environment.temperature-T0C,0.01)]°C)." + var/datum/gas_mixture/pipe_air = target.return_air() + if(pipe_air) + . = "The pressure gauge reads [round(pipe_air.return_pressure(), 0.01)] kPa; [round(pipe_air.temperature,0.01)] K ([round(pipe_air.temperature-T0C,0.01)]°C)." else . = "The sensor error light is blinking." else