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