Assorted atmos performance improvements. (#27966)

This commit is contained in:
Charlie Nolan
2025-01-20 07:18:32 -08:00
committed by GitHub
parent 8954a8e401
commit fdbd2b2050
13 changed files with 271 additions and 230 deletions
+23 -15
View File
@@ -12,12 +12,20 @@ SUBSYSTEM_DEF(air)
name = "Atmospherics"
init_order = INIT_ORDER_AIR
priority = FIRE_PRIORITY_AIR
wait = 2
flags = SS_BACKGROUND
// The MC really doesn't like it if we sleep (even though it's supposed to), and ends up running us continuously. Instead, we ask it to run us every tick, and "sleep" by skipping the current tick.
wait = 1
flags = SS_BACKGROUND | SS_TICKER
/// How long we actually wait between ticks. Will round up to the next server tick.
var/self_wait = 0.15 SECONDS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
offline_implications = "Turfs will no longer process atmos, and all atmospheric machines (including cryotubes) will no longer function. Shuttle call recommended."
cpu_display = SS_CPUDISPLAY_HIGH
/// When did we last finish running a complete tick?
var/last_complete_tick = 0
/// When did we last start a tick?
var/last_tick_start = 0
/// How long we took for a full pass through the subsystem. Custom-tracked version of `cost`.
var/datum/resumable_cost_counter/cost_full = new()
/// How long we spent sleeping while waiting for MILLA to finish the last tick, shown in SS Info's C block as ZZZ.
@@ -178,26 +186,21 @@ SUBSYSTEM_DEF(air)
currentpart = SSair.currentpart
milla_idle = SSair.milla_idle
#define SLEEPABLE_TIMER (world.time + world.tick_usage * world.tick_lag / 100)
/datum/controller/subsystem/air/fire(resumed = 0)
// All atmos stuff assumes MILLA is synchronous. Ensure it actually is.
if(!milla_idle || length(sleepers) > 0)
var/timer = SLEEPABLE_TIMER
var/now = world.timeofday + (world.tick_lag * world.tick_usage) / 100
var/elapsed = now - last_complete_tick
if(!milla_idle || (elapsed >= 0 && elapsed < self_wait))
return
while(!milla_idle || length(sleepers) > 0)
// Sleep for 1ms.
sleep(0.01)
var/new_timer = SLEEPABLE_TIMER
time_slept.record_progress((new_timer - timer) * 100, FALSE)
timer = new_timer
time_slept.record_progress((SLEEPABLE_TIMER - timer) * 100, TRUE)
if(last_tick_start <= last_complete_tick)
last_tick_start = now
time_slept.record_progress(max(0, elapsed) * 100, TRUE)
// Run the sleepless callbacks again in case more showed up since on_milla_tick_finished()
run_sleepless_callbacks()
fire_sleepless(resumed)
#undef SLEEPABLE_TIMER
/datum/controller/subsystem/air/proc/fire_sleepless(resumed)
// Any proc that wants MILLA to be synchronous should not sleep.
@@ -317,13 +320,15 @@ SUBSYSTEM_DEF(air)
milla_idle = FALSE
cost_milla_tick = MC_AVERAGE(cost_milla_tick, get_milla_tick_time())
cost_full.record_progress(TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer), state != SS_PAUSED && state != SS_PAUSING)
cost_full.record_progress(TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer), FALSE)
if(state == SS_PAUSED || state == SS_PAUSING)
in_milla_safe_code = FALSE
return
resumed = 0
currentpart = SSAIR_DEFERREDPIPENETS
last_complete_tick = world.timeofday + (world.tick_lag * world.tick_usage) / 100
cost_full.record_progress(0, TRUE)
in_milla_safe_code = FALSE
/datum/controller/subsystem/air/proc/build_pipenets(resumed = 0)
@@ -624,6 +629,9 @@ SUBSYSTEM_DEF(air)
for(var/turf/T as anything in block(low_corner, high_corner))
T.Initialize_Atmos(times_fired)
milla_load_turfs(low_corner, high_corner)
for(var/turf/T as anything in block(low_corner, high_corner))
T.milla_data.len = 0
T.milla_data = null
/datum/controller/subsystem/air/proc/setup_write_to_milla()
var/watch = start_watch()
+2 -2
View File
@@ -61,8 +61,8 @@
var/list/firealarms
var/firedoors_last_closed_on = 0
/// The air alarm to use for atmos_alert consoles
var/obj/machinery/alarm/master_air_alarm
/// The air alarms present in this area.
var/list/air_alarms = list()
/// The list of vents in our area.
var/list/obj/machinery/atmospherics/unary/vent_pump/vents = list()
/// The list of scrubbers in our area.
+22 -19
View File
@@ -19,26 +19,29 @@
parent_area_type = machine_area.get_top_parent_type()
/obj/machinery/computer/atmos_alert/process()
// This is relatively cheap because the areas list is pretty small
for(var/obj/machinery/alarm/air_alarm as anything in GLOB.air_alarms)
if(!((get_area(air_alarm)).type in typesof(parent_area_type)) || air_alarm.z != z)
continue // Not an area we monitor, or outside our z-level
if(!air_alarm.report_danger_level)
alarm_cache = list()
alarm_cache["priority"] = list()
alarm_cache["minor"] = list()
alarm_cache["mode"] = list()
for(var/area/A in GLOB.all_areas)
if(!istype(A, parent_area_type))
continue
switch(air_alarm.alarm_area.atmosalm)
if(ATMOS_ALARM_DANGER)
alarm_cache["priority"] |= air_alarm.alarm_area.name
alarm_cache["minor"] -= air_alarm.alarm_area.name
if(ATMOS_ALARM_WARNING)
alarm_cache["priority"] -= air_alarm.alarm_area.name
alarm_cache["minor"] |= air_alarm.alarm_area.name
else
alarm_cache["priority"] -= air_alarm.alarm_area.name
alarm_cache["minor"] -= air_alarm.alarm_area.name
if(air_alarm.mode == AALARM_MODE_FILTERING)
alarm_cache["mode"] -= air_alarm.alarm_area.name
else
alarm_cache["mode"][air_alarm.alarm_area.name] = GLOB.aalarm_modes["[air_alarm.mode]"]
var/alarm_level = null
for(var/obj/machinery/alarm/air_alarm in A.air_alarms)
if(!istype(air_alarm))
continue
if(!air_alarm.report_danger_level)
continue
switch(air_alarm.alarm_area.atmosalm)
if(ATMOS_ALARM_DANGER)
alarm_level = "priority"
if(ATMOS_ALARM_WARNING)
if(isnull(alarm_level))
alarm_level = "minor"
if(!isnull(alarm_level))
alarm_cache[alarm_level] += A.name
if(air_alarm.mode != AALARM_MODE_FILTERING)
alarm_cache["mode"][A.name] = GLOB.aalarm_modes["[air_alarm.mode]"]
update_icon()
+1 -7
View File
@@ -85,13 +85,7 @@
/// The effect used to render a pressure overlay from this tile.
var/obj/effect/pressure_overlay/pressure_overlay
var/list/milla_atmos_airtight = list(FALSE, FALSE, FALSE, FALSE)
var/list/milla_superconductivity = list(
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT)
var/list/milla_data = list()
var/list/milla_data = null
new_attack_chain = TRUE
@@ -173,9 +173,9 @@
/turf/proc/Initialize_Atmos(times_fired)
// This is one of two places expected to call this otherwise-unsafe method.
private_unsafe_recalculate_atmos_connectivity()
var/list/connectivity = private_unsafe_recalculate_atmos_connectivity()
var/list/air = list(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b, temperature)
milla_data = milla_atmos_airtight + list(atmos_mode, SSmapping.environments[atmos_environment]) + air + milla_superconductivity
milla_data = connectivity[1] + list(atmos_mode, SSmapping.environments[atmos_environment]) + air + connectivity[2]
/turf/proc/recalculate_atmos_connectivity()
var/datum/milla_safe/recalculate_atmos_connectivity/milla = new()
@@ -188,26 +188,26 @@
return
// This is one of two places expected to call this otherwise-unsafe method.
T.private_unsafe_recalculate_atmos_connectivity()
var/list/connectivity = T.private_unsafe_recalculate_atmos_connectivity()
set_tile_airtight(T, T.milla_atmos_airtight)
set_tile_airtight(T, connectivity[1])
reset_superconductivity(T)
reduce_superconductivity(T, T.milla_superconductivity)
reduce_superconductivity(T, connectivity[2])
/// This method is unsafe to use because it only updates milla_* properties, but does not write them to MILLA. Use recalculate_atmos_connectivity() instead.
/turf/proc/private_unsafe_recalculate_atmos_connectivity()
if(blocks_air)
milla_atmos_airtight = list(TRUE, TRUE, TRUE, TRUE)
milla_superconductivity = list(0, 0, 0, 0)
return
var/milla_atmos_airtight = list(TRUE, TRUE, TRUE, TRUE)
var/milla_superconductivity = list(0, 0, 0, 0)
return list(milla_atmos_airtight, milla_superconductivity)
milla_atmos_airtight = list(
var/milla_atmos_airtight = list(
!CanAtmosPass(NORTH, FALSE),
!CanAtmosPass(EAST, FALSE),
!CanAtmosPass(SOUTH, FALSE),
!CanAtmosPass(WEST, FALSE))
milla_superconductivity = list(
var/milla_superconductivity = list(
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
OPEN_HEAT_TRANSFER_COEFFICIENT,
@@ -230,6 +230,8 @@
milla_superconductivity[INDEX_SOUTH] = min(milla_superconductivity[INDEX_SOUTH], O.get_superconductivity(SOUTH))
milla_superconductivity[INDEX_WEST] = min(milla_superconductivity[INDEX_WEST], O.get_superconductivity(WEST))
return list(milla_atmos_airtight, milla_superconductivity)
/obj/effect/wind
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
@@ -114,12 +114,10 @@ What are the archived variables for?
/// Calculate moles
/datum/gas_mixture/proc/total_moles()
var/moles = private_oxygen + private_carbon_dioxide + private_nitrogen + private_toxins + private_sleeping_agent + private_agent_b
return moles
return private_oxygen + private_carbon_dioxide + private_nitrogen + private_toxins + private_sleeping_agent + private_agent_b
/datum/gas_mixture/proc/total_trace_moles()
var/moles = private_agent_b
return moles
return private_agent_b
/// Calculate pressure in kilopascals
/datum/gas_mixture/proc/return_pressure()
@@ -662,23 +660,26 @@ What are the archived variables for?
/proc/share_many_airs(list/mixtures)
var/total_volume = 0
var/total_thermal_energy = 0
var/total_heat_capacity = 0
var/total_oxygen = 0
var/total_nitrogen = 0
var/total_toxins = 0
var/total_carbon_dioxide = 0
var/total_sleeping_agent = 0
var/total_agent_b = 0
var/must_share = FALSE
// Collect all the cheap data and check if there's a significant temperature difference.
var/temperature = null
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
stack_trace("share_many_airs had [G] in mixtures ([json_encode(mixtures)])")
continue
total_volume += G.volume
var/heat_capacity = G.heat_capacity()
total_heat_capacity += heat_capacity
total_thermal_energy += G.private_temperature * heat_capacity
if(isnull(temperature))
temperature = G.private_temperature
else if(abs(temperature - G.private_temperature) >= 1)
must_share = TRUE
total_oxygen += G.private_oxygen
total_nitrogen += G.private_nitrogen
@@ -687,26 +688,65 @@ What are the archived variables for?
total_sleeping_agent += G.private_sleeping_agent
total_agent_b += G.private_agent_b
if(total_volume > 0)
//Calculate temperature
var/temperature = 0
if(total_volume <= 0)
return
if(total_heat_capacity > 0)
temperature = total_thermal_energy/total_heat_capacity
//Update individual gas_mixtures by volume ratio
// If we don't have a significant temperature difference, check for a significant gas amount difference.
if(!must_share)
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
continue
G.private_oxygen = total_oxygen * G.volume / total_volume
G.private_nitrogen = total_nitrogen * G.volume / total_volume
G.private_toxins = total_toxins * G.volume / total_volume
G.private_carbon_dioxide = total_carbon_dioxide * G.volume / total_volume
G.private_sleeping_agent = total_sleeping_agent * G.volume / total_volume
G.private_agent_b = total_agent_b * G.volume / total_volume
if(abs(G.private_oxygen - total_oxygen * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_nitrogen - total_nitrogen * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_toxins - total_toxins * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_carbon_dioxide - total_carbon_dioxide * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_sleeping_agent - total_sleeping_agent * G.volume / total_volume) > 0.1)
must_share = TRUE
break
if(abs(G.private_agent_b - total_agent_b * G.volume / total_volume) > 0.1)
must_share = TRUE
break
G.private_temperature = temperature
G.set_dirty()
if(!must_share)
// Nothing significant, don't do any more work.
return
// Collect the more expensive data.
var/total_thermal_energy = 0
var/total_heat_capacity = 0
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
continue
var/heat_capacity = G.heat_capacity()
total_heat_capacity += heat_capacity
total_thermal_energy += G.private_temperature * heat_capacity
// Calculate shared temperature.
temperature = TCMB
if(total_heat_capacity > 0)
temperature = total_thermal_energy/total_heat_capacity
// Update individual gas_mixtures by volume ratio.
for(var/datum/gas_mixture/G as anything in mixtures)
if(!istype(G))
continue
G.private_oxygen = total_oxygen * G.volume / total_volume
G.private_nitrogen = total_nitrogen * G.volume / total_volume
G.private_toxins = total_toxins * G.volume / total_volume
G.private_carbon_dioxide = total_carbon_dioxide * G.volume / total_volume
G.private_sleeping_agent = total_sleeping_agent * G.volume / total_volume
G.private_agent_b = total_agent_b * G.volume / total_volume
G.private_temperature = temperature
// In theory, we should G.set_dirty() here, but that's only useful for bound mixtures, and these can't be.
/datum/gas_mixture/proc/hotspot_expose(temperature, volume)
return
@@ -222,6 +222,7 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
set_pixel_offsets_from_dir(24, -24, 24, -24)
GLOB.air_alarms += src
alarm_area.air_alarms += src
if(!mapload)
GLOB.air_alarms = sortAtom(GLOB.air_alarms)
@@ -231,17 +232,12 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
if(!building)
first_run()
if(!master_is_operating())
elect_master()
/obj/machinery/alarm/Destroy()
SStgui.close_uis(wires)
GLOB.air_alarms -= src
alarm_area.air_alarms -= src
GLOB.air_alarm_repository.update_cache(src)
QDEL_NULL(wires)
if(alarm_area && alarm_area.master_air_alarm == src)
alarm_area.master_air_alarm = null
elect_master(exclude_self = 1)
alarm_area = null
return ..()
@@ -249,24 +245,6 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
apply_preset(AALARM_PRESET_HUMAN) // Don't cycle.
GLOB.air_alarm_repository.update_cache(src)
/obj/machinery/alarm/proc/master_is_operating()
if(!alarm_area)
alarm_area = get_area(src)
if(!alarm_area)
. = FALSE
CRASH("Air alarm /obj/machinery/alarm lacks alarm_area vars during proc/master_is_operating()")
return alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER|BROKEN))
/obj/machinery/alarm/proc/elect_master(exclude_self = 0) //Why is this an alarm and not area proc?
for(var/obj/machinery/alarm/AA in alarm_area)
if(exclude_self && AA == src)
continue
if(!(AA.stat & (NOPOWER|BROKEN)))
alarm_area.master_air_alarm = AA
return 1
return 0
/obj/machinery/alarm/process()
if((stat & (NOPOWER|BROKEN)) || shorted || buildstage != 2)
return
@@ -275,8 +253,9 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
if(!istype(location))
return 0
var/datum/milla_safe/airalarm_heat_cool/milla = new()
milla.invoke_async(src)
if(thermostat_state)
var/datum/milla_safe/airalarm_heat_cool/milla = new()
milla.invoke_async(src)
var/datum/gas_mixture/environment = location.get_readonly_air()
var/GET_PP = R_IDEAL_GAS_EQUATION * environment.temperature() / environment.volume
@@ -342,8 +321,6 @@ GLOBAL_LIST_INIT(aalarm_modes, list(
var/turf/location = get_turf(alarm)
var/datum/gas_mixture/environment = get_turf_air(location)
if(!alarm.thermostat_state)
return
var/datum/tlv/cur_tlv = alarm.TLV["temperature"]
//Handle temperature adjustment here.
if(environment.temperature() < alarm.target_temperature - 2 || environment.temperature() > alarm.target_temperature + 2 || alarm.regulating_temperature)
@@ -94,21 +94,14 @@
if(Old == parent)
parent = New
/obj/machinery/atmospherics/unary/unsafe_pressure_release(mob/user, pressures)
/obj/machinery/atmospherics/unary/unsafe_pressure_release(mob/user, pressure)
..()
var/turf/T = get_turf(src)
if(T)
var/datum/milla_safe/unary_unsafe_pressure_release/milla = new()
milla.invoke_async(src, pressures)
if(!T)
return
/datum/milla_safe/unary_unsafe_pressure_release
var/lost = pressure * CELL_VOLUME / (air_contents.temperature() * R_IDEAL_GAS_EQUATION)
/datum/milla_safe/unary_unsafe_pressure_release/on_run(obj/machinery/atmospherics/unary/device, pressures)
//Remove the gas from air_contents and assume it
var/turf/T = get_turf(device)
var/datum/gas_mixture/environment = get_turf_air(T)
var/lost = pressures * environment.volume / (device.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/to_release = device.air_contents.remove(lost)
environment.merge(to_release)
var/datum/gas_mixture/to_release = air_contents.remove(lost)
T.blind_release_air(to_release)
@@ -108,69 +108,71 @@
update_underlays()
/obj/machinery/atmospherics/unary/vent_pump/process_atmos()
var/datum/milla_safe/vent_pump_process/milla = new()
milla.invoke_async(src)
/datum/milla_safe/vent_pump_process
/datum/milla_safe/vent_pump_process/on_run(obj/machinery/atmospherics/unary/vent_pump/vent_pump)
if(vent_pump.stat & (NOPOWER|BROKEN))
if(stat & (NOPOWER|BROKEN))
return FALSE
if(QDELETED(vent_pump.parent))
if(QDELETED(parent))
// We're orphaned!
return FALSE
var/turf/T = get_turf(vent_pump)
var/turf/T = get_turf(src)
if(T.density) //No, you should not be able to get free air from walls
return
if(!vent_pump.node)
vent_pump.on = FALSE
if(!vent_pump.on)
if(!node)
on = FALSE
if(!on)
return FALSE
if(vent_pump.welded)
if(vent_pump.air_contents.return_pressure() >= vent_pump.weld_burst_pressure && prob(5)) //the weld is on but the cover is welded shut, can it withstand the internal pressure?
vent_pump.visible_message("<span class='danger'>The welded cover of [vent_pump] bursts open!</span>")
if(welded)
if(air_contents.return_pressure() >= weld_burst_pressure && prob(5)) //the weld is on but the cover is welded shut, can it withstand the internal pressure?
visible_message("<span class='danger'>The welded cover of [src] bursts open!</span>")
for(var/mob/living/M in range(1))
vent_pump.unsafe_pressure_release(M, vent_pump.air_contents.return_pressure()) //let's send everyone flying
vent_pump.welded = FALSE
vent_pump.update_icon()
unsafe_pressure_release(M, air_contents.return_pressure()) //let's send everyone flying
welded = FALSE
update_icon()
return FALSE
var/datum/gas_mixture/environment = get_turf_air(T)
var/environment_pressure = environment.return_pressure()
if(vent_pump.releasing) //internal -> external
var/datum/gas_mixture/environment = T.get_readonly_air()
if(releasing) //internal -> external
var/pressure_delta = 10000
if(vent_pump.pressure_checks == ONLY_CHECK_EXT_PRESSURE)
if(pressure_checks == ONLY_CHECK_EXT_PRESSURE)
// Only checks difference between set pressure and environment pressure
pressure_delta = min(pressure_delta, (vent_pump.external_pressure_bound - environment_pressure))
if(vent_pump.pressure_checks == ONLY_CHECK_INT_PRESSURE)
pressure_delta = min(pressure_delta, (vent_pump.air_contents.return_pressure() - vent_pump.internal_pressure_bound))
pressure_delta = min(pressure_delta, (external_pressure_bound - environment.return_pressure()))
if(pressure_checks == ONLY_CHECK_INT_PRESSURE)
pressure_delta = min(pressure_delta, (air_contents.return_pressure() - internal_pressure_bound))
if(pressure_delta > 0.5 && vent_pump.air_contents.temperature() > 0)
if(pressure_delta > 0.5 && air_contents.temperature() > 0)
// 1kPa * 1L = 1J
var/wanted_joules = pressure_delta * environment.volume
var/transfer_moles = min(vent_pump.max_transfer_joules, wanted_joules) / (vent_pump.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = vent_pump.air_contents.remove(transfer_moles)
environment.merge(removed)
vent_pump.parent.update = TRUE
var/transfer_moles = min(max_transfer_joules, wanted_joules) / (air_contents.temperature() * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
// This isn't exactly "blind", but using the data from last tick is good enough for a vent.
T.blind_release_air(removed)
parent.update = TRUE
else //external -> internal
var/pressure_delta = 10000
if(vent_pump.pressure_checks == ONLY_CHECK_EXT_PRESSURE)
pressure_delta = min(pressure_delta, (environment_pressure - vent_pump.external_pressure_bound))
if(vent_pump.pressure_checks == ONLY_CHECK_INT_PRESSURE)
pressure_delta = min(pressure_delta, (vent_pump.internal_pressure_bound - vent_pump.air_contents.return_pressure()))
if(pressure_delta > 0.5 && environment.temperature() > 0)
// 1kPa * 1L = 1J
var/wanted_joules = pressure_delta * environment.volume
var/transfer_moles = min(vent_pump.max_transfer_joules, wanted_joules) / (environment.temperature() * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = environment.remove(transfer_moles)
vent_pump.air_contents.merge(removed)
vent_pump.parent.update = TRUE
var/datum/milla_safe/vent_pump_siphon/milla = new()
milla.invoke_async(src)
return TRUE
/datum/milla_safe/vent_pump_siphon
/datum/milla_safe/vent_pump_siphon/on_run(obj/machinery/atmospherics/unary/vent_pump/vent_pump)
var/turf/T = get_turf(vent_pump)
var/datum/gas_mixture/environment = get_turf_air(T)
var/pressure_delta = 10000
if(vent_pump.pressure_checks == ONLY_CHECK_EXT_PRESSURE)
pressure_delta = min(pressure_delta, (environment.return_pressure() - vent_pump.external_pressure_bound))
if(vent_pump.pressure_checks == ONLY_CHECK_INT_PRESSURE)
pressure_delta = min(pressure_delta, (vent_pump.internal_pressure_bound - vent_pump.air_contents.return_pressure()))
if(pressure_delta > 0.5 && environment.temperature() > 0)
// 1kPa * 1L = 1J
var/wanted_joules = pressure_delta * environment.volume
var/transfer_moles = min(vent_pump.max_transfer_joules, wanted_joules) / (environment.temperature() * R_IDEAL_GAS_EQUATION)
var/datum/gas_mixture/removed = environment.remove(transfer_moles)
vent_pump.air_contents.merge(removed)
vent_pump.parent.update = TRUE
/obj/machinery/atmospherics/unary/vent_pump/can_crawl_through()
return !welded
@@ -140,9 +140,31 @@
adjacent_turfs = T.GetAtmosAdjacentTurfs(alldir=1)
/obj/machinery/atmospherics/unary/vent_scrubber/proc/scrub(turf/simulated/tile)
if(!tile || !istype(tile))
return 0
if(scrubbing && !should_scrub(tile.get_readonly_air()))
return 0
var/datum/milla_safe/vent_scrubber_process/milla = new()
milla.invoke_async(src, tile)
/obj/machinery/atmospherics/unary/vent_scrubber/proc/should_scrub(datum/gas_mixture/environment)
if(scrub_O2 && environment.oxygen() > 0.001)
return TRUE
if(scrub_N2 && environment.nitrogen() > 0.001)
return TRUE
if(scrub_CO2 && environment.carbon_dioxide() > 0.001)
return TRUE
if(scrub_Toxins && environment.toxins() > 0.001)
return TRUE
if(environment.sleeping_agent() > 0.001)
return TRUE
if(environment.agent_b() > 0.001)
return TRUE
return FALSE
/datum/milla_safe/vent_scrubber_process
/datum/milla_safe/vent_scrubber_process/on_run(obj/machinery/atmospherics/unary/vent_scrubber/scrubber, turf/simulated/tile)
@@ -152,7 +174,7 @@
var/datum/gas_mixture/environment = get_turf_air(tile)
if(scrubber.scrubbing)
if((scrubber.scrub_O2 && environment.oxygen() > 0.001) || (scrubber.scrub_N2 && environment.nitrogen() > 0.001) || (scrubber.scrub_CO2 && environment.carbon_dioxide() > 0.001) || (scrubber.scrub_Toxins && environment.toxins() > 0.001) || (environment.sleeping_agent()) || (environment.agent_b()))
if(scrubber.should_scrub(environment))
var/transfer_moles = min(1, scrubber.volume_rate / environment.volume) * environment.total_moles()
//Take a gas sample
@@ -210,41 +210,41 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
/obj/machinery/atmospherics/portable/canister/process_atmos()
..()
sync_pressure_appearance()
var/datum/milla_safe/canister_process/milla = new()
milla.invoke_async(src)
/datum/milla_safe/canister_process
/datum/milla_safe/canister_process/on_run(obj/machinery/atmospherics/portable/canister/canister)
if(canister.stat & BROKEN)
if(stat & BROKEN)
return
if(canister.valve_open)
var/datum/gas_mixture/environment
if(canister.holding_tank)
environment = canister.holding_tank.air_contents
else
var/turf/T = get_turf(canister)
environment = get_turf_air(T)
if(valve_open)
var/datum/milla_safe/canister_release/milla = new()
milla.invoke_async(src)
var/env_pressure = environment.return_pressure()
var/pressure_delta = min(canister.release_pressure - env_pressure, (canister.air_contents.return_pressure() - env_pressure) / 2)
//Can not have a pressure delta that would cause environment pressure > tank pressure
var/transfer_moles = 0
if((canister.air_contents.temperature() > 0) && (pressure_delta > 0))
transfer_moles = pressure_delta * environment.volume / (canister.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = canister.air_contents.remove(transfer_moles)
environment.merge(removed)
canister.sync_pressure_appearance()
if(canister.air_contents.return_pressure() < 1)
canister.can_label = TRUE
if(air_contents.return_pressure() < 1)
can_label = TRUE
else
canister.can_label = FALSE
can_label = FALSE
/datum/milla_safe/canister_release
/datum/milla_safe/canister_release/on_run(obj/machinery/atmospherics/portable/canister/canister)
var/datum/gas_mixture/environment
if(canister.holding_tank)
environment = canister.holding_tank.air_contents
else
var/turf/T = get_turf(canister)
environment = get_turf_air(T)
var/env_pressure = environment.return_pressure()
var/pressure_delta = min(canister.release_pressure - env_pressure, (canister.air_contents.return_pressure() - env_pressure) / 2)
//Can not have a pressure delta that would cause environment pressure > tank pressure
var/transfer_moles = 0
if((canister.air_contents.temperature() > 0) && (pressure_delta > 0))
transfer_moles = pressure_delta * environment.volume / (canister.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = canister.air_contents.remove(transfer_moles)
environment.merge(removed)
canister.sync_pressure_appearance()
/obj/machinery/atmospherics/portable/canister/return_obj_air()
RETURN_TYPE(/datum/gas_mixture)
@@ -61,44 +61,44 @@
/obj/machinery/atmospherics/portable/pump/process_atmos()
..()
var/datum/milla_safe/portable_pump_process/milla = new()
milla.invoke_async(src)
if(on)
var/datum/milla_safe/portable_pump_process/milla = new()
milla.invoke_async(src)
/datum/milla_safe/portable_pump_process
/datum/milla_safe/portable_pump_process/on_run(obj/machinery/atmospherics/portable/pump/pump)
if(pump.on)
var/datum/gas_mixture/environment
if(pump.holding_tank)
environment = pump.holding_tank.air_contents
else
var/turf/T = get_turf(pump)
environment = get_turf_air(T)
if(pump.direction == DIRECTION_OUT)
var/pressure_delta = pump.target_pressure - environment.return_pressure()
//Can not have a pressure delta that would cause environment pressure > tank pressure
var/datum/gas_mixture/environment
if(pump.holding_tank)
environment = pump.holding_tank.air_contents
else
var/turf/T = get_turf(pump)
environment = get_turf_air(T)
if(pump.direction == DIRECTION_OUT)
var/pressure_delta = pump.target_pressure - environment.return_pressure()
//Can not have a pressure delta that would cause environment pressure > tank pressure
var/transfer_moles = 0
if(pump.air_contents.temperature() > 0)
transfer_moles = pressure_delta*environment.volume/(pump.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
var/transfer_moles = 0
if(pump.air_contents.temperature() > 0)
transfer_moles = pressure_delta*environment.volume/(pump.air_contents.temperature() * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = pump.air_contents.remove(transfer_moles)
//Actually transfer the gas
var/datum/gas_mixture/removed = pump.air_contents.remove(transfer_moles)
environment.merge(removed)
else
var/pressure_delta = pump.target_pressure - pump.air_contents.return_pressure()
//Can not have a pressure delta that would cause environment pressure > tank pressure
environment.merge(removed)
else
var/pressure_delta = pump.target_pressure - pump.air_contents.return_pressure()
//Can not have a pressure delta that would cause environment pressure > tank pressure
var/transfer_moles = 0
if(environment.temperature() > 0)
transfer_moles = pressure_delta*pump.air_contents.volume/(environment.temperature() * R_IDEAL_GAS_EQUATION)
var/transfer_moles = 0
if(environment.temperature() > 0)
transfer_moles = pressure_delta*pump.air_contents.volume/(environment.temperature() * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed
removed = environment.remove(transfer_moles)
//Actually transfer the gas
var/datum/gas_mixture/removed
removed = environment.remove(transfer_moles)
pump.air_contents.merge(removed)
pump.air_contents.merge(removed)
/obj/machinery/atmospherics/portable/pump/return_obj_air()
RETURN_TYPE(/datum/gas_mixture)
@@ -46,18 +46,18 @@
/obj/machinery/atmospherics/portable/scrubber/process_atmos()
..()
var/datum/milla_safe/portable_scrubber_process/milla = new()
if(!on)
return
if(holding_tank)
scrub(holding_tank.air_contents)
return
var/datum/milla_safe/portable_scrubber_scrub/milla = new()
milla.invoke_async(src)
/datum/milla_safe/portable_scrubber_process
/datum/milla_safe/portable_scrubber_process/on_run(obj/machinery/atmospherics/portable/scrubber/scrubber)
if(!scrubber.on)
return
if(scrubber.holding_tank)
scrubber.scrub(scrubber.holding_tank.air_contents)
return
/datum/milla_safe/portable_scrubber_scrub
/datum/milla_safe/portable_scrubber_scrub/on_run(obj/machinery/atmospherics/portable/scrubber/scrubber)
var/turf/T = get_turf(scrubber)
scrubber.scrub(get_turf_air(T))
if(scrubber.widenet)