From fa5183d5da71f1afedb179d19eaefc078dfa35af Mon Sep 17 00:00:00 2001 From: Exxion Date: Wed, 1 Nov 2017 03:16:32 -0400 Subject: [PATCH] More ZAS updating (#15922) * Clean up the diff * Some new defines * Guess I missed that * Remove the flipped * Move direct * More SSair stuff * Tidier * Unsimulated edge tick * Removes useless shit * Remove additional garbage * Dusting * More unticked files * I missed another, apparently * Yet another * Wow, an actual change! Sort of * god damn it * Oh, that was commented out * These are actually very slightly different * bad * More readable * Does nothing * Roughly mimics old behavior * Unnecessary * Equivalent * This makes sense above, but not here * *shrug * Missed these * Destroy AirflowCanMove() * Some cleanup * Unification * Bools * Actually it SHOULD be like this * Alright that would have been too hardcore * Update doc * Oops * I have OCD * Cleanup * Fuck group_multiplier * This was all unused * Also unused * Add some stuff * Move stuff * Add nothing * Remove old * Unused * Move * Some new procs * I touched it bitch * Compiles, probably * Air no longer flows nonsensically * Probably makes compare() work * [Another synonym for cleaner] * Potentially done * Last thing I know of * Oh yeah this too --- __DEFINES/atmos.dm | 3 + __DEFINES/math_physics.dm | 9 + code/ATMOSPHERICS/datum_pipeline.dm | 33 +- code/ZAS/Airflow.dm | 431 ++----- code/ZAS/ConnectionGroup.dm | 362 +----- code/ZAS/Diagnostic.dm | 1 - code/ZAS/Functions.dm | 176 --- code/ZAS/Turf.dm | 6 +- code/ZAS/Variable Settings.dm | 345 ------ code/ZAS/Zone.dm | 49 +- code/ZAS/_gas_mixture.dm | 1124 +++++-------------- code/controllers/subsystem/air.dm | 60 +- code/game/machinery/atmoalter/gas_mine.dm | 3 +- code/game/machinery/atmoalter/zvent.dm | 2 +- code/game/objects/items/devices/scanners.dm | 8 +- code/modules/mob/dead/observer/observer.dm | 35 +- 16 files changed, 550 insertions(+), 2097 deletions(-) delete mode 100644 code/ZAS/Functions.dm delete mode 100644 code/ZAS/Variable Settings.dm diff --git a/__DEFINES/atmos.dm b/__DEFINES/atmos.dm index e4f45b266be..44116ba3e04 100644 --- a/__DEFINES/atmos.dm +++ b/__DEFINES/atmos.dm @@ -35,6 +35,9 @@ #define MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND 4 //Minimum temperature difference before group processing is suspended #define MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER 0.5 //Minimum temperature difference before the gas temperatures are just set to be equal +#define MINIMUM_PRESSURE_DELTA_TO_SUSPEND 0.1 //The minimum pressure difference required for groups to remain separate (unless they meet other conditions). Chosen arbitrarily. +#define MINIMUM_PRESSURE_RATIO_TO_SUSPEND 0.05 //Minimum RELATIVE difference in pressure for groups to stay separate (unless they meet other conditions). Also chosen arbitrarily. + #define MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION T20C+10 #define MINIMUM_TEMPERATURE_START_SUPERCONDUCTION T20C+200 diff --git a/__DEFINES/math_physics.dm b/__DEFINES/math_physics.dm index ccd7b62ea5a..4c0caf00380 100644 --- a/__DEFINES/math_physics.dm +++ b/__DEFINES/math_physics.dm @@ -12,10 +12,19 @@ #define R_IDEAL_GAS_EQUATION 8.314 //kPa*L/(K*mol) #define ONE_ATMOSPHERE 101.325 //kPa +// Radiation constants. +#define STEFAN_BOLTZMANN_CONSTANT 5.6704e-8 // W/(m^2*K^4). +#define COSMIC_RADIATION_TEMPERATURE 3.15 // K. +#define AVERAGE_SOLAR_RADIATION 200 // W/m^2. Kind of arbitrary. Really this should depend on the sun position much like solars. +#define RADIATOR_OPTIMUM_PRESSURE 3771 // kPa at 20 C. This should be higher as gases aren't great conductors until they are dense. Used the critical pressure for air. +#define GAS_CRITICAL_TEMPERATURE 132.65 // K. The critical point temperature for air. + #define T0C 273.15 // 0degC #define T20C 293.15 // 20degC #define TCMB 2.73 // -270.42degC +#define QUANTIZE(variable) (round(variable, 0.0001)) + #define INFINITY 1e31 //closer than enough #define SPEED_OF_LIGHT 3e8 //not exact but hey! diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 09b1813c9a2..c4812ded2a2 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -137,36 +137,13 @@ return network /datum/pipeline/proc/mingle_with_turf(turf/simulated/target, mingle_volume) - var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) - air_sample.volume = mingle_volume + var/datum/gas_mixture/air_sample = air.remove_volume(mingle_volume) - if(istype(target) && target.zone) - //Have to consider preservation of group statuses - var/datum/gas_mixture/turf_copy = new + var/datum/gas_mixture/turf_air = target.return_air() - turf_copy.copy_from(target.zone.air) - turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group + equalize_gases(list(air_sample, turf_air)) + air.merge(air_sample) - equalize_gases(list(air_sample, turf_copy)) - air.merge(air_sample) - - turf_copy.subtract(target.zone.air) - - target.zone.air.merge(turf_copy) - - else - var/datum/gas_mixture/turf_air = target.return_air() - - equalize_gases(list(air_sample, turf_air)) - air.merge(air_sample) - //turf_air already modified by equalize_gases() - - /* - if(istype(target) && !target.processing) - if(target.air) - if(target.air.check_tile_graphic()) - target.update_visuals(target.air) - */ if(network) network.update = 1 @@ -214,7 +191,7 @@ air.temperature += self_temperature_delta if(modeled_location.zone) - modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier + modeled_location.zone.air.temperature += sharer_temperature_delta else modeled_location.air.temperature += sharer_temperature_delta diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index 4165add57ec..3298e24f315 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -32,282 +32,143 @@ atom/movable/airflow_hit(atom/A) AUTOMATIC PROCS: -Airflow(zone/A, zone/B) - Causes objects to fly along a pressure gradient. - Called by zone updates. A and B are two connected zones. - -AirflowSpace(zone/A) - Causes objects to fly into space. - Called by zone updates. A is a zone connected to space. - atom/movable/GotoAirflowDest(n) -atom/movable/RepelAirflowDest(n) - Called by main airflow procs to cause the object to fly to or away from destination at speed n. + Called by main airflow procs to cause the object to fly to (n > 0) or away from (n < 0) destination at speed scaled by abs(n). Probably shouldn't call this directly unless you know what you're doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle. */ -mob/var/tmp/last_airflow_stun = 0 -mob/proc/airflow_stun() - if(stat == 2 || (flags & INVULNERABLE)) - return 0 - if(last_airflow_stun > world.time - zas_settings.Get(/datum/ZAS_Setting/airflow_stun_cooldown)) - return 0 +/mob/var/tmp/last_airflow_stun = 0 +/mob/proc/airflow_stun() + if(isDead() || (flags & INVULNERABLE) || (status_flags & GODMODE)) + return FALSE + if(world.time < last_airflow_stun + zas_settings.Get(/datum/ZAS_Setting/airflow_stun_cooldown)) + return FALSE +// if(!zas_settings.Get(/datum/ZAS_Setting/airflow_push) || !(M_HARDCORE in mutations)) //This block was added in the original XGM PR, but, again, I don't want to bundle balance with system. +// return FALSE +// if(locked_to) +// to_chat(src, "Air suddenly rushes past you!") +// return FALSE if(!(status_flags & CANSTUN) && !(status_flags & CANKNOCKDOWN)) to_chat(src, "You stay upright as the air rushes past you.") - return 0 - + return FALSE if(knockdown <= 0) to_chat(src, "The sudden rush of air knocks you over!") SetKnockdown(5) last_airflow_stun = world.time + +/mob/living/silicon/airflow_stun() return -mob/living/silicon/airflow_stun() +/mob/living/carbon/slime/airflow_stun() return -mob/living/carbon/metroid/airflow_stun() - return - -mob/living/carbon/human/airflow_stun() - if(last_airflow_stun > world.time - zas_settings.Get(/datum/ZAS_Setting/airflow_stun_cooldown)) - return 0 +/mob/living/carbon/human/airflow_stun() + if(world.time < last_airflow_stun + zas_settings.Get(/datum/ZAS_Setting/airflow_stun_cooldown)) + return FALSE if(locked_to || (flags & INVULNERABLE)) - return 0 + return FALSE if(shoes) if(CheckSlip() < 1) - return 0 + return FALSE if(!(status_flags & CANSTUN) && !(status_flags & CANKNOCKDOWN)) to_chat(src, "You stay upright as the air rushes past you.") - return 0 + return FALSE if(knockdown <= 0) to_chat(src, "The sudden rush of air knocks you over!") SetKnockdown(rand(1,5)) last_airflow_stun = world.time - return -atom/movable/proc/check_airflow_movable(n) - if(anchored && !ismob(src)) - return 0 - if(!istype(src,/obj/item) && n < zas_settings.Get(/datum/ZAS_Setting/airflow_dense_pressure)) - return 0 +/atom/movable/proc/check_airflow_movable(n) + return (!anchored && n >= zas_settings.Get(/datum/ZAS_Setting/airflow_dense_pressure)) - return 1 +/mob/check_airflow_movable(n) +// if(M_HARDCORE in mutations) +// return TRUE //It really is hardcore //TOO hardcore, probably -mob/check_airflow_movable(n) if(n < zas_settings.Get(/datum/ZAS_Setting/airflow_heavy_pressure)) - return 0 - return 1 + return FALSE + if(status_flags & GODMODE || (flags & INVULNERABLE)) + return FALSE + if(locked_to) + return FALSE + if(CheckSlip() < 0) + return FALSE -mob/dead/observer/check_airflow_movable() - return 0 + if (grabbed_by.len) + return FALSE -mob/living/silicon/check_airflow_movable() - return 0 + return TRUE -mob/virtualhearer/check_airflow_movable() - return 0 +/mob/living/carbon/human/check_airflow_movable(n) + if(reagents.has_reagent(MEDCORES)) + return FALSE + return ..() +/mob/dead/observer/check_airflow_movable() + return FALSE -obj/item/check_airflow_movable(n) - . = ..() - switch(w_class) - if(2) - if(n < zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) - return 0 - if(3) - if(n < zas_settings.Get(/datum/ZAS_Setting/airflow_light_pressure)) - return 0 - if(4,5) - if(n < zas_settings.Get(/datum/ZAS_Setting/airflow_medium_pressure)) - return 0 +/mob/living/silicon/check_airflow_movable() + return FALSE -/* -//The main airflow code. Called by zone updates. -//Zones A and B are air zones. n represents the amount of air moved. +/mob/virtualhearer/check_airflow_movable() + return FALSE -proc/Airflow(zone/A, zone/B) - set background = 1 - var/n = B.air.return_pressure() - A.air.return_pressure() - - //Don't go any further if n is lower than the lowest value needed for airflow. - if(abs(n) < zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) - return - - //These turfs are the midway point between A and B, and will be the destination point for thrown objects. - var/list/connection/connections_A = A.connections - var/list/turf/connected_turfs = list() - for(var/connection/C in connections_A) //Grab the turf that is in the zone we are flowing to (determined by n) - if( ( A == C.A.zone || A == C.zone_A ) && ( B == C.B.zone || B == C.zone_B ) ) - if(n < 0) - connected_turfs |= C.B - else - connected_turfs |= C.A - else if( ( A == C.B.zone || A == C.zone_B ) && ( B == C.A.zone || B == C.zone_A ) ) - if(n < 0) - connected_turfs |= C.A - else - connected_turfs |= C.B - - //Get lists of things that can be thrown across the room for each zone (assumes air is moving from zone B to zone A) - spawn() - var/list/air_sucked = B.movables() - var/list/air_repelled = A.movables() - if(n < 0) - //air is moving from zone A to zone B - var/list/temporary_pplz = air_sucked - air_sucked = air_repelled - air_repelled = temporary_pplz - - if(zas_settings.Get(/datum/ZAS_Setting/airflow_push) || 1) // If enabled - for(var/atom/movable/M in air_sucked) - if(M.last_airflow > world.time - zas_settings.Get(/datum/ZAS_Setting/airflow_delay)) - continue - - //Check for knocking people over - if(ismob(M) && n > zas_settings.Get(/datum/ZAS_Setting/airflow_stun_pressure)) - if(M:status_flags & GODMODE) - continue - M:airflow_stun() - - if(M.check_airflow_movable(n)) - - //Check for things that are in range of the midpoint turfs. - var/list/close_turfs = list() - for(var/turf/U in connected_turfs) - if(M in range(U)) - close_turfs += U - if(!close_turfs.len) - continue - - //If they're already being tossed, don't do it again. - if(!M.airflow_speed) - - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - - M.GotoAirflowDest(abs(n)/5) - - //Do it again for the stuff in the other zone, making it fly away. - for(var/atom/movable/M in air_repelled) - - if(M.last_airflow > world.time - zas_settings.Get(/datum/ZAS_Setting/airflow_delay)) - continue - - if(ismob(M) && abs(n) > zas_settings.Get(/datum/ZAS_Setting/airflow_medium_pressure)) - if(M:status_flags & GODMODE) - continue - M:airflow_stun() - - if(M.check_airflow_movable(abs(n))) - - var/list/close_turfs = list() - for(var/turf/U in connected_turfs) - if(M in range(U)) - close_turfs += U - if(!close_turfs.len) - continue - - //If they're already being tossed, don't do it again. - if(!M.airflow_speed) - - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - - spawn M.RepelAirflowDest(abs(n)/5) - -proc/AirflowSpace(zone/A) - spawn() - //The space version of the Airflow(A,B,n) proc. - - var/n = A.air.return_pressure() - //Here, n is determined by only the pressure in the room. - - if(n < zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) - return - - var/list/connected_turfs = A.unsimulated_tiles //The midpoints are now all the space connections. - var/list/pplz = A.movables() //We only need to worry about things in the zone, not things in space. - - if(zas_settings.Get(/datum/ZAS_Setting/airflow_push) || 1) // If enabled - for(var/atom/movable/M in pplz) - if(M.last_airflow > world.time - zas_settings.Get(/datum/ZAS_Setting/airflow_delay)) - continue - - if(ismob(M) && n > zas_settings.Get(/datum/ZAS_Setting/airflow_stun_pressure)) - var/mob/O = M - if(O.status_flags & GODMODE) - continue - O.airflow_stun() - - if(M.check_airflow_movable(n)) - - var/list/close_turfs = list() - for(var/turf/U in connected_turfs) - if(M in range(U)) - close_turfs += U - if(!close_turfs.len) - continue - - //If they're already being tossed, don't do it again. - if(!M.airflow_speed) - - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - M.GotoAirflowDest(n/10) - //Sometimes shit breaks, and M isn't there after the spawn. -*/ +/obj/item/check_airflow_movable(n) + if(anchored) + return FALSE + switch(w_class) //Note that switch() evaluates the FIRST matching case, so the case that executes for a given w_class is the one for which it is the UPPER bound. + if(0 to W_CLASS_TINY) + return TRUE + if(W_CLASS_TINY to W_CLASS_SMALL) + return (n >= zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) + if(W_CLASS_SMALL to W_CLASS_MEDIUM) + return (n >= zas_settings.Get(/datum/ZAS_Setting/airflow_light_pressure)) + if(W_CLASS_MEDIUM to INFINITY) + return (n >= zas_settings.Get(/datum/ZAS_Setting/airflow_medium_pressure)) /atom/movable/var/tmp/turf/airflow_dest /atom/movable/var/tmp/airflow_speed = 0 /atom/movable/var/tmp/airflow_time = 0 /atom/movable/var/tmp/last_airflow = 0 -// Mainly for bustanuts. - -/atom/movable/proc/AirflowCanPush() - return 1 - -/mob/AirflowCanPush() - return 1 - -/mob/living/carbon/human/AirflowCanPush() - if(reagents.has_reagent(MEDCORES)) - return 0 - return ..() - /atom/movable/proc/GotoAirflowDest(n) - last_airflow = world.time - if(pulledby) + if(!airflow_dest || pulledby) + return + if(world.time < last_airflow + zas_settings.Get(/datum/ZAS_Setting/airflow_delay)) return if(airflow_dest == loc) return if(ismob(src)) - var/mob/M = src - if(M.status_flags & GODMODE || (flags & INVULNERABLE)) - return - if(M.grabbed_by.len) - return - if(istype(src, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(H.locked_to) - return - if(H.shoes) - if(H.CheckSlip() < 0) - return - to_chat(src, "You are sucked away by airflow!") - var/airflow_falloff = 9 - ul_FalloffAmount(airflow_dest) //It's a fast falloff calc. Very useful. + to_chat(src, "You are sucked away by airflow!") + + var/xo = airflow_dest.x - x + var/yo = airflow_dest.y - y + + var/airflow_falloff = 9 - sqrt(xo ** 2 + yo ** 2) + if(airflow_falloff < 1) airflow_dest = null return + + if(n < 0) + n *= -2 //Back when GotoAirflowDest() and RepelAirflowDest() were separate procs, the latter was called with differential/5 rather than differential/10. This is to maintain consistency. + xo *= -1 + yo *= -1 + airflow_speed = Clamp(n * (9 / airflow_falloff), 1, 9) - var - xo = airflow_dest.x - src.x - yo = airflow_dest.y - src.y - od = 0 + airflow_dest = null + + var/od = FALSE if(!density) - density = 1 - od = 1 + density = TRUE + od = TRUE + + last_airflow = world.time + spawn(0) while(airflow_speed > 0 && Process_Spacemove(1)) airflow_speed = min(airflow_speed,15) @@ -315,14 +176,14 @@ proc/AirflowSpace(zone/A) if(airflow_speed > 7) if(airflow_time++ >= airflow_speed - 7) if(od) - density = 0 + density = FALSE sleep(tick_multiplier) else if(od) - density = 0 + density = FALSE sleep(max(1,10-(airflow_speed+3)) * tick_multiplier) if(od) - density = 1 + density = TRUE if ((!( src.airflow_dest ) || src.loc == src.airflow_dest)) airflow_dest = locate(Clamp(x + xo, 1, world.maxx), Clamp(y + yo, 1, world.maxy), z) if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy)) @@ -330,73 +191,14 @@ proc/AirflowSpace(zone/A) if(!isturf(loc)) break step_towards(src, src.airflow_dest) - if(ismob(src) && src:client) - var/mob/M = src + var/mob/M = src + if(istype(M) && M.client) M.delayNextMove(zas_settings.Get(/datum/ZAS_Setting/airflow_mob_slowdown)) airflow_dest = null airflow_speed = 0 airflow_time = 0 if(od) - density = 0 - - -/atom/movable/proc/RepelAirflowDest(n) - if(pulledby) - return - if(airflow_dest == loc) - step_away(src,loc) - if(ismob(src)) - var/mob/M = src - if(M.status_flags & GODMODE || (flags & INVULNERABLE)) - return - if(M.grabbed_by.len) - return - if(istype(src, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(H.locked_to) - return - if(H.shoes) - if(H.CheckSlip() < 0) - return - to_chat(src, "You are pushed away by airflow!") - last_airflow = world.time - var/airflow_falloff = 9 - ul_FalloffAmount(airflow_dest) //It's a fast falloff calc. Very useful. - if(airflow_falloff < 1) - airflow_dest = null - return - airflow_speed = Clamp(n * (9 / airflow_falloff), 1, 9) - var - xo = -(airflow_dest.x - src.x) - yo = -(airflow_dest.y - src.y) - od = 0 - airflow_dest = null - if(!density) - density = 1 - od = 1 - spawn(0) - while(airflow_speed > 0) - airflow_speed = min(airflow_speed,15) - airflow_speed -= zas_settings.Get(/datum/ZAS_Setting/airflow_speed_decay) - if(airflow_speed > 7) - if(airflow_time++ >= airflow_speed - 7) - sleep(tick_multiplier) - else - sleep(max(1,10-(airflow_speed+3)) * tick_multiplier) - if ((!( src.airflow_dest ) || src.loc == src.airflow_dest)) - airflow_dest = locate(Clamp(x + xo, 1, world.maxx), Clamp(y + yo, 1, world.maxy), z) - if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy)) - break - if (!isturf(loc)) - break - step_towards(src, src.airflow_dest) - if(ismob(src) && src:client) - var/mob/M = src - M.delayNextMove(zas_settings.Get(/datum/ZAS_Setting/airflow_mob_slowdown)) - airflow_dest = null - airflow_speed = 0 - airflow_time = 0 - if(od) - density = 0 + density = FALSE /atom/movable/to_bump(atom/Obstacle) if(airflow_speed > 0 && airflow_dest) @@ -407,17 +209,15 @@ proc/AirflowSpace(zone/A) . = ..() sound_override = 0 -atom/movable/proc/airflow_hit(atom/A) +/atom/movable/proc/airflow_hit(atom/A) airflow_speed = 0 airflow_dest = null -mob/airflow_hit(atom/A) +/mob/airflow_hit(atom/A) if(size == SIZE_TINY) return //Slamming into a mouse/roach doesn't make much sense - if(!sound_override) - for(var/mob/M in hearers(src)) - M.show_message("\The [src] slams into \a [A]!",1,"You hear a loud slam!",2) + visible_message(message = "\The [src] slams into \a [A]!", blind_message = "You hear a loud slam!") //playsound(get_turf(src), "smash.ogg", 25, 1, -1) if(istype(A,/obj/item)) var/obj/item/item = A @@ -426,40 +226,17 @@ mob/airflow_hit(atom/A) SetKnockdown(rand(1,5)) . = ..() -obj/airflow_hit(atom/A) +/obj/airflow_hit(atom/A) if(!sound_override) - for(var/mob/M in hearers(src)) - M.show_message("\The [src] slams into \a [A]!",1,"You hear a loud slam!",2) + visible_message(message = "\The [src] slams into \a [A]!", blind_message = "You hear a loud slam!") //playsound(get_turf(src), "smash.ogg", 25, 1, -1) . = ..() -obj/item/airflow_hit(atom/A) +/obj/item/airflow_hit(atom/A) airflow_speed = 0 airflow_dest = null -mob/living/carbon/human/airflow_hit(atom/A) -// for(var/mob/M in hearers(src)) -// M.show_message("[src] slams into [A]!",1,"You hear a loud slam!",2) - //playsound(get_turf(src), "punch", 25, 1, -1) - - - /*See this? This is how you DON'T handle armor values for protection - if(prob(33)) - loc:add_blood(src) - bloody_body(src) - - var/b_loss = airflow_speed * zas_settings.Get(/datum/ZAS_Setting/airflow_damage) - - var/blocked = run_armor_check(LIMB_HEAD,"melee") - apply_damage(b_loss/3, BRUTE, LIMB_HEAD, blocked, 0, used_weapon = "Airflow") - - blocked = run_armor_check(LIMB_CHEST,"melee") - apply_damage(b_loss/3, BRUTE, LIMB_CHEST, blocked, 0, used_weapon = "Airflow") - - blocked = run_armor_check(LIMB_GROIN,"melee") - apply_damage(b_loss/3, BRUTE, LIMB_GROIN, blocked, 0, used_weapon = "Airflow") - */ - +/mob/living/carbon/human/airflow_hit(atom/A) var/b_loss = airflow_speed * zas_settings.Get(/datum/ZAS_Setting/airflow_damage) for(var/i in contents) @@ -483,19 +260,11 @@ mob/living/carbon/human/airflow_hit(atom/A) T.add_blood(src) bloody_body(src) - if(zas_settings.Get(/datum/ZAS_Setting/airflow_push) || AirflowCanPush()) + if(zas_settings.Get(/datum/ZAS_Setting/airflow_push) || (M_HARDCORE in mutations)) if(airflow_speed > 10) - paralysis += round(airflow_speed * zas_settings.Get(/datum/ZAS_Setting/airflow_stun)) - stunned = max(stunned,paralysis + 3) + Paralyse(round(airflow_speed * zas_settings.Get(/datum/ZAS_Setting/airflow_stun))) + Stun(paralysis + 3) else - stunned += round(airflow_speed * zas_settings.Get(/datum/ZAS_Setting/airflow_stun)/2) + Stun(round(airflow_speed * zas_settings.Get(/datum/ZAS_Setting/airflow_stun)/2)) . = ..() - -zone/proc/movables() - . = list() - for(var/turf/T in contents) - for(var/atom/A in T) - if(istype(A, /obj/effect) || isobserver(A) || isAIEye(A)) - continue - . += A diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm index 836d6abfbb3..b97ec4f18bb 100644 --- a/code/ZAS/ConnectionGroup.dm +++ b/code/ZAS/ConnectionGroup.dm @@ -1,141 +1,101 @@ /* - Overview: These are what handle gas transfers between zones and into space. They are found in a zone's edges list and in SSair.edges. Each edge updates every air tick due to their role in gas transfer. They come in two flavors, /connection_edge/zone and /connection_edge/unsimulated. As the type names might suggest, they handle inter-zone and spacelike connections respectively. - Class Vars: - A - This always holds a zone. In unsimulated edges, it holds the only zone. - connecting_turfs - This holds a list of connected turfs, mainly for the sake of airflow. - coefficent - This is a marker for how many connections are on this edge. Used to determine the ratio of flow. - connection_edge/zone - B - This holds the second zone with which the first zone equalizes. - direct - This counts the number of direct (i.e. with no doors) connections on this edge. Any value of this is sufficient to make the zones mergeable. - connection_edge/unsimulated - B - This holds an unsimulated turf which has the gas values this edge is mimicing. - air - Retrieved from B on creation and used as an argument for the legacy ShareSpace() proc. - Class Procs: - add_connection(connection/c) Adds a connection to this edge. Usually increments the coefficient and adds a turf to connecting_turfs. - remove_connection(connection/c) Removes a connection from this edge. This works even if c is not in the edge, so be careful. If the coefficient reaches zero as a result, the edge is erased. - contains_zone(zone/Z) Returns true if either A or B is equal to Z. Unsimulated connections return true only on A. - erase() Removes this connection from processing and zone edge lists. - tick() Called every air tick on edges in the processing list. Equalizes gas. - flow(list/movable, differential, repelled) Airflow proc causing all objects in movable to be checked against a pressure differential. If repelled is true, the objects move away from any turf in connecting_turfs, otherwise they approach. A check against vsc.lightest_airflow_pressure should generally be performed before calling this. - get_connected_zone(zone/from) Helper proc that allows getting the other zone of an edge given one of them. Only on /connection_edge/zone, otherwise use A. - */ -/connection_edge/var/zone/A +/connection_edge + var/zone/A -/connection_edge/var/list/connecting_turfs = list() + var/list/connecting_turfs = list() + var/direct = 0 + var/sleeping = 1 -/connection_edge/var/coefficient = 0 + var/coefficient = 0 /connection_edge/New() CRASH("Cannot make connection edge without specifications.") /connection_edge/proc/add_connection(connection/c) coefficient++ -// to_chat(world, "Connection added: [type] Coefficient: [coefficient]") + if(c.direct()) + direct++ /connection_edge/proc/remove_connection(connection/c) -// to_chat(world, "Connection removed: [type] Coefficient: [coefficient-1]") coefficient-- if(coefficient <= 0) erase() + if(c.direct()) + direct-- /connection_edge/proc/contains_zone(zone/Z) /connection_edge/proc/erase() SSair.remove_edge(src) -// to_chat(world, "[type] Erased.") /connection_edge/proc/tick() -/connection_edge/proc/flow(list/movable, differential, repelled, flipped = 0) - //Flipped tells us if we are going from A to B or from B to A. +/connection_edge/proc/recheck() + +/connection_edge/proc/flow(list/blown, differential) if(!zas_settings.Get(/datum/ZAS_Setting/airflow_push)) return - for(var/atom/movable/M in movable) - if(!M.AirflowCanPush()) - continue - //If they're already being tossed, don't do it again. - if(M.last_airflow > world.time - zas_settings.Get(/datum/ZAS_Setting/airflow_delay)) - continue - if(M.airflow_speed) - continue - + for(var/atom/movable/AM in blown) //Check for knocking people over - if(ismob(M) && differential > zas_settings.Get(/datum/ZAS_Setting/airflow_stun_pressure)) - if(M:status_flags & GODMODE) - continue - M:airflow_stun() + if(ismob(AM) && differential > zas_settings.Get(/datum/ZAS_Setting/airflow_stun_pressure)) + var/mob/M = AM + M.airflow_stun() - if(M.check_airflow_movable(differential)) + if(AM.check_airflow_movable(differential)) //Check for things that are in range of the midpoint turfs. var/list/close_turfs = list() for(var/turf/U in connecting_turfs) - if(get_dist(M,U) < world.view) + if(get_dist(AM,U) < world.view) close_turfs += U if(!close_turfs.len) continue - M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. + AM.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - if(M) - if(repelled) - if(flipped) - if(!(M.loc in src:A.contents)) - continue - else if(!(M.loc in src:B.contents)) - continue - M.RepelAirflowDest(differential/5) - else - if(flipped) - if(!(M.loc in src:B.contents)) - continue - else if(!(M.loc in src:A.contents)) - continue - M.GotoAirflowDest(differential/10) + AM.GotoAirflowDest(differential/10) - - -/connection_edge/zone/var/zone/B -/connection_edge/zone/var/direct = 0 +/connection_edge/zone + var/zone/B /connection_edge/zone/New(zone/A, zone/B) @@ -144,18 +104,13 @@ Class Procs: A.edges.Add(src) B.edges.Add(src) //id = edge_id(A,B) -// to_chat(world, "New edge between [A] and [B]") /connection_edge/zone/add_connection(connection/c) . = ..() connecting_turfs.Add(c.A) - if(c.direct()) - direct++ /connection_edge/zone/remove_connection(connection/c) connecting_turfs.Remove(c.A) - if(c.direct()) - direct-- . = ..() /connection_edge/zone/contains_zone(zone/Z) @@ -170,38 +125,29 @@ Class Procs: if(A.invalid || B.invalid) erase() return -// to_chat(world, "[id]: Tick [SSair.current_cycle]: \...") - if(direct) - if(SSair.equivalent_pressure(A, B)) -// to_chat(world, "merged.") - erase() - SSair.merge(A, B) -// to_chat(world, "zones merged.") - return - //SSair.equalize(A, B) - ShareRatio(A.air,B.air,coefficient) - SSair.mark_zone_update(A) - SSair.mark_zone_update(B) -// to_chat(world, "equalized.") + var/equiv = A.air.share_tiles(B.air, coefficient) var/differential = A.air.return_pressure() - B.air.return_pressure() - if(abs(differential) < zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) - return + if(abs(differential) >= zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) + flow(A.contents, differential) + flow(B.contents, -differential) - var/list/attracted - var/list/repelled - var/flipped = 0 - if(differential > 0) - attracted = A.movables() - repelled = B.movables() - else - flipped = 1 - attracted = B.movables() - repelled = A.movables() + if(equiv) + if(direct) + erase() + SSair.merge(A, B) + return + else + SSair.mark_edge_sleeping(src) + equalize_gases(A.get_equalized_zone_air()) - flow(attracted, abs(differential), 0, flipped) - flow(repelled, abs(differential), 1, flipped) + SSair.mark_zone_update(A) + SSair.mark_zone_update(B) + +/connection_edge/zone/recheck() + if(!A.air.compare(B.air)) + SSair.mark_edge_active(src) //Helper proc to get connections for a zone. /connection_edge/zone/proc/get_connected_zone(zone/from) @@ -210,8 +156,9 @@ Class Procs: else return A -/connection_edge/unsimulated/var/turf/B -/connection_edge/unsimulated/var/datum/gas_mixture/air +/connection_edge/unsimulated + var/turf/B + var/datum/gas_mixture/air /connection_edge/unsimulated/New(zone/A, turf/B) src.A = A @@ -219,16 +166,13 @@ Class Procs: A.edges.Add(src) air = B.return_air() //id = 52*A.id -// to_chat(world, "New edge from [A] to [B].") /connection_edge/unsimulated/add_connection(connection/c) . = ..() connecting_turfs.Add(c.B) - air.group_multiplier = coefficient /connection_edge/unsimulated/remove_connection(connection/c) connecting_turfs.Remove(c.B) - air.group_multiplier = coefficient . = ..() /connection_edge/unsimulated/erase() @@ -242,219 +186,31 @@ Class Procs: if(A.invalid) erase() return -// to_chat(world, "[id]: Tick [SSair.current_cycle]: To [B]!") - //A.air.mimic(B, coefficient) - ShareSpace(A.air,air,dbg_out) - SSair.mark_zone_update(A) + + var/equiv = A.air.share_space(air, coefficient) var/differential = A.air.return_pressure() - air.return_pressure() - if(abs(differential) < zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) - return + if(abs(differential) >= zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure)) + flow(A.contents, abs(differential), differential < 0) - var/list/attracted = A.movables() - flow(attracted, abs(differential), differential < 0) + if(equiv) + A.air.copy_from(air) + SSair.mark_edge_sleeping(src) -var/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66) - -proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles) - //Shares a specific ratio of gas between mixtures using simple weighted averages. - var - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - ratio = sharing_lookup_table[6] - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - - size = max(1,A.group_multiplier) - share_size = max(1,B.group_multiplier) - - full_oxy = A.oxygen * size - full_nitro = A.nitrogen * size - full_co2 = A.carbon_dioxide * size - full_plasma = A.toxins * size - - full_heat_capacity = A.heat_capacity() * size - - s_full_oxy = B.oxygen * share_size - s_full_nitro = B.nitrogen * share_size - s_full_co2 = B.carbon_dioxide * share_size - s_full_plasma = B.toxins * share_size - - s_full_heat_capacity = B.heat_capacity() * share_size - - oxy_avg = (full_oxy + s_full_oxy) / (size + share_size) - nit_avg = (full_nitro + s_full_nitro) / (size + share_size) - co2_avg = (full_co2 + s_full_co2) / (size + share_size) - plasma_avg = (full_plasma + s_full_plasma) / (size + share_size) - - temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity) - - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - if(connecting_tiles && sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick. - ratio = sharing_lookup_table[connecting_tiles] - //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD - - A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg ) - A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg ) - A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg ) - A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg ) - - A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg ) - - B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg ) - B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg ) - B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg ) - B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg ) - - B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg ) - - for(var/datum/gas/G in A.trace_gases) - var/datum/gas/H = locate(G.type) in B.trace_gases - if(H) - var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size) - G.moles = (G.moles - G_avg) * (1-ratio) + G_avg - - H.moles = (H.moles - G_avg) * (1-ratio) + G_avg - else - H = new G.type - B.trace_gases += H - var/G_avg = (G.moles*size) / (size+share_size) - G.moles = (G.moles - G_avg) * (1-ratio) + G_avg - H.moles = (H.moles - G_avg) * (1-ratio) + G_avg - - for(var/datum/gas/G in B.trace_gases) - var/datum/gas/H = locate(G.type) in A.trace_gases - if(!H) - H = new G.type - A.trace_gases += H - var/G_avg = (G.moles*size) / (size+share_size) - G.moles = (G.moles - G_avg) * (1-ratio) + G_avg - H.moles = (H.moles - G_avg) * (1-ratio) + G_avg - - A.update_values() - B.update_values() - - if(A.compare(B)) - return 1 - else - return 0 - -proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output) - //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room. - if(!unsimulated_tiles) - return 0 - - var - unsim_oxygen = 0 - unsim_nitrogen = 0 - unsim_co2 = 0 - unsim_plasma = 0 - unsim_heat_capacity = 0 - unsim_temperature = 0 - - size = max(1,A.group_multiplier) - - var/tileslen - var/share_size - - if(istype(unsimulated_tiles, /datum/gas_mixture)) - var/datum/gas_mixture/avg_unsim = unsimulated_tiles - unsim_oxygen = avg_unsim.oxygen - unsim_co2 = avg_unsim.carbon_dioxide - unsim_nitrogen = avg_unsim.nitrogen - unsim_plasma = avg_unsim.toxins - unsim_temperature = avg_unsim.temperature - share_size = max(1, max(size + 3, 1) + avg_unsim.group_multiplier) - tileslen = avg_unsim.group_multiplier - - if(dbg_output) - to_chat(world, "O2: [unsim_oxygen] N2: [unsim_nitrogen] Size: [share_size] Tiles: [tileslen]") - - else if(istype(unsimulated_tiles, /list)) - if(!unsimulated_tiles.len) - return 0 - // We use the same size for the potentially single space tile - // as we use for the entire room. Why is this? - // Short answer: We do not want larger rooms to depressurize more - // slowly than small rooms, preserving our good old "hollywood-style" - // oh-shit effect when large rooms get breached, but still having small - // rooms remain pressurized for long enough to make escape possible. - share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len) - var/correction_ratio = share_size / unsimulated_tiles.len - - for(var/turf/T in unsimulated_tiles) - unsim_oxygen += T.oxygen - unsim_co2 += T.carbon_dioxide - unsim_nitrogen += T.nitrogen - unsim_plasma += T.toxins - unsim_temperature += T.temperature/unsimulated_tiles.len - - //These values require adjustment in order to properly represent a room of the specified size. - unsim_oxygen *= correction_ratio - unsim_co2 *= correction_ratio - unsim_nitrogen *= correction_ratio - unsim_plasma *= correction_ratio - tileslen = unsimulated_tiles.len - - else //invalid input type - return 0 - - unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen, unsim_co2, unsim_nitrogen, unsim_plasma) - - var - ratio = sharing_lookup_table[6] - - old_pressure = A.return_pressure() - - full_oxy = A.oxygen * size - full_nitro = A.nitrogen * size - full_co2 = A.carbon_dioxide * size - full_plasma = A.toxins * size - - full_heat_capacity = A.heat_capacity() * size - - oxy_avg = (full_oxy + unsim_oxygen*share_size) / (size + share_size) - nit_avg = (full_nitro + unsim_nitrogen*share_size) / (size + share_size) - co2_avg = (full_co2 + unsim_co2*share_size) / (size + share_size) - plasma_avg = (full_plasma + unsim_plasma*share_size) / (size + share_size) - - temp_avg = 0 - - if((full_heat_capacity + unsim_heat_capacity) > 0) - temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity) - - if(sharing_lookup_table.len >= tileslen) //6 or more interconnecting tiles will max at 42% of air moved per tick. - ratio = sharing_lookup_table[tileslen] - - if(dbg_output) - to_chat(world, "Ratio: [ratio]") - to_chat(world, "Avg O2: [oxy_avg] N2: [nit_avg]") - - A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg ) - A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg ) - A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg ) - A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg ) - - A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg ) - - for(var/datum/gas/G in A.trace_gases) - var/G_avg = (G.moles * size) / (size + share_size) - G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg - - A.update_values() - - if(dbg_output) - to_chat(world, "Result: [abs(old_pressure - A.return_pressure())] kPa") - - return abs(old_pressure - A.return_pressure()) + SSair.mark_zone_update(A) +/connection_edge/unsimulated/recheck() + if(!A.air.compare(air)) + SSair.mark_edge_active(src) proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles) //This implements a simplistic version of the Stefan-Boltzmann law. - var/energy_delta = ((A.temperature - B.temperature) ** 4) * 5.6704e-8 * connecting_tiles * 2.5 - var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier)) + var/energy_delta = ((A.temperature - B.temperature) ** 4) * STEFAN_BOLTZMANN_CONSTANT * connecting_tiles * 2.5 + var/maximum_energy_delta = max(0, min(A.temperature * A.heat_capacity(), B.temperature * B.heat_capacity())) if(maximum_energy_delta > abs(energy_delta)) if(energy_delta < 0) maximum_energy_delta *= -1 energy_delta = maximum_energy_delta - A.temperature -= energy_delta / (A.heat_capacity() * A.group_multiplier) - B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier) + A.temperature -= energy_delta / A.heat_capacity() + B.temperature += energy_delta / B.heat_capacity() diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm index a9ede7acf76..2878732c318 100644 --- a/code/ZAS/Diagnostic.dm +++ b/code/ZAS/Diagnostic.dm @@ -101,7 +101,6 @@ client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf) to_chat(client, "Pressure: [air.return_pressure()] KPa") to_chat(client, "") to_chat(client, "Unsimulated Zone(space/catwalk) Tiles: [length(unsimulated_tiles)]") - to_chat(client, "Movable Objects: [length(movables())]") to_chat(client, "Connections: [length(connections)]") for(var/connection/C in connections) diff --git a/code/ZAS/Functions.dm b/code/ZAS/Functions.dm deleted file mode 100644 index a5c69d8a63a..00000000000 --- a/code/ZAS/Functions.dm +++ /dev/null @@ -1,176 +0,0 @@ -//Global Functions -//Contents: FloodFill, ZMerge, ZConnect - -//Floods outward from an initial turf to fill everywhere it's zone would reach. -proc/FloodFill(turf/simulated/start) - - - if(!istype(start)) - return list() - - //The list of tiles waiting to be evaulated. - var/list/open = list(start) - //The list of tiles which have been evaulated. - var/list/closed = list() - - //Loop through the turfs in the open list in order to find which adjacent turfs should be added to the zone. - while(open.len) - var/turf/simulated/T = pick(open) - - //sanity! - if(!istype(T) || iscatwalk(T)) - open -= T - continue - - //Check all cardinal directions - for(var/d in cardinal) - var/turf/simulated/O = get_step(T,d) - - //Ensure the turf is of proper type, that it is not in either list, and that air can reach it. - if(istype(O) && !iscatwalk(O) && !(O in open) && !(O in closed) && O.ZCanPass(T)) - - //Handle connections from a tile with a door. - if(T.HasDoor()) - //If they both have doors, then they are not able to connect period. - if(O.HasDoor()) - continue - - //Connect first to north and west - if(d == NORTH || d == WEST) - open += O - - //If that fails, and north/west cannot be connected to, see if west or south can be connected instead. - else - var/turf/simulated/W = get_step(O, WEST) - var/turf/simulated/N = get_step(O, NORTH) - - if(!O.ZCanPass(N) && !O.ZCanPass(W) ) - //If it cannot connect either to the north or west, connect it! - open += O - - //If no doors are involved, add it immediately. - else if(!O.HasDoor()) - open += O - - //Handle connecting to a tile with a door. - else - if(d == SOUTH || d == EAST) - //doors prefer connecting to zones to the north or west - closed += O - - else - //see if we need to force an attempted connection - //(there are no potentially viable zones to the north/west of the door) - var/turf/simulated/W = get_step(O, WEST) - var/turf/simulated/N = get_step(O, NORTH) - - if( !O.ZCanPass(N) && !O.ZCanPass(W) ) - //If it cannot connect either to the north or west, connect it! - closed += O - - //This tile is now evaluated, and can be moved to the list of evaluated tiles. - open -= T - closed += T - - return closed - - -//Procedure to merge two zones together. -proc/ZMerge(zone/A,zone/B) - - - //Sanity~ - if(!istype(A) || !istype(B)) - return - - var/new_contents = A.contents + B.contents - - //Set all the zone vars. - for(var/turf/simulated/T in B.contents) - T.zone = A - - if(istype(A.air) && istype(B.air)) - //Merges two zones so that they are one. - var/a_size = A.air.group_multiplier - var/b_size = B.air.group_multiplier - var/c_size = a_size + b_size - - //Set air multipliers to one so air represents gas per tile. - A.air.group_multiplier = 1 - B.air.group_multiplier = 1 - - //Remove some air proportional to the size of this zone. - A.air.remove_ratio(a_size/c_size) - B.air.remove_ratio(b_size/c_size) - - //Merge the gases and set the multiplier to the sum of the old ones. - A.air.merge(B.air) - A.air.group_multiplier = c_size - - //I hate when the air datum somehow disappears. - // Try to make it sorta work anyways. Fakit - else if(istype(B.air)) - A.air = B.air - A.air.group_multiplier = A.contents.len - - else if(istype(A.air)) - A.air.group_multiplier = A.contents.len - - //Doublefakit. - else - A.air = new - - //Check for connections to merge into the new zone. - for(var/connection/C in B.connections) - //The Cleanup proc will delete the connection if the zones are the same. - // It will also set the zone variables correctly. - C.Cleanup() - - //Add space tiles. - if(A.unsimulated_tiles && B.unsimulated_tiles) - A.unsimulated_tiles |= B.unsimulated_tiles - else if (B.unsimulated_tiles) - A.unsimulated_tiles = B.unsimulated_tiles - - //Add contents. - A.contents = new_contents - - //Remove the "B" zone, finally. - B.SoftDelete() - - -//Connects two zones by forming a connection object representing turfs A and B. -proc/ZConnect(turf/simulated/A,turf/simulated/B) - - - //Make sure that if it's space, it gets added to unsimulated_tiles instead. - if(!istype(B) || iscatwalk(B)) - if(A.zone) - A.zone.AddTurf(B) - return - if(!istype(A) || iscatwalk(A)) - if(B.zone) - B.zone.AddTurf(A) - return - - if(!istype(A) || !istype(B)) - return - - //Make some preliminary checks to see if the connection is valid. - if(!A.zone || !B.zone) - return - if(A.zone == B.zone) - return - - if(A.CanPass(null,B,0,1)) - return ZMerge(A.zone,B.zone) - - //Ensure the connection isn't already made. - if("\ref[A]" in SSair.turfs_with_connections) - for(var/connection/C in SSair.turfs_with_connections["\ref[A]"]) - C.Cleanup() - if(C && (C.B == B || C.A == B)) - return - - //Make the connection. - new /connection(A,B) diff --git a/code/ZAS/Turf.dm b/code/ZAS/Turf.dm index 12c8eb46648..24f93264063 100644 --- a/code/ZAS/Turf.dm +++ b/code/ZAS/Turf.dm @@ -248,8 +248,6 @@ SSair.mark_zone_update(zone) return zone.air else - if(!air) - make_air() c_copy_air() return air else @@ -261,15 +259,13 @@ air = new/datum/gas_mixture air.temperature = temperature air.adjust(oxygen, carbon_dioxide, nitrogen, toxins) - air.group_multiplier = 1 air.volume = CELL_VOLUME /turf/simulated/proc/c_copy_air() if(!air) air = new/datum/gas_mixture air.copy_from(zone.air) - air.group_multiplier = 1 /turf/attack_hand(mob/user as mob) - user.Move_Pulled(src) \ No newline at end of file + user.Move_Pulled(src) diff --git a/code/ZAS/Variable Settings.dm b/code/ZAS/Variable Settings.dm deleted file mode 100644 index dd11b326c9a..00000000000 --- a/code/ZAS/Variable Settings.dm +++ /dev/null @@ -1,345 +0,0 @@ -var/global/vs_control/vsc = new - -// Whoever made this fucking thing: I hate you so much. -vs_control/var - // N3X15 - Added back in so we can tweak performance. - airflow_push = 0 - airflow_push_NAME="Airflow - Push shit around" - airflow_push_DESC="1=yes please rape my server, 0=no" - airflow_push_METHOD="Toggle" // See ChangeSettings(). I'd rather not let people break this. - - fire_consuption_rate = 0.75 - fire_consuption_rate_NAME = "Fire - Air Consumption Ratio" - fire_consuption_rate_DESC = "Ratio of air removed and combusted per tick." - - fire_firelevel_multiplier = 25 - fire_firelevel_multiplier_NAME = "Fire - Firelevel Constant" - fire_firelevel_multiplier_DESC = "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires." - - fire_fuel_energy_release = 550000 - fire_fuel_energy_release_NAME = "Fire - Fuel energy release" - fire_fuel_energy_release_DESC = "The energy in joule released when burning one mol of a burnable substance" - - - airflow_lightest_pressure = 20 - airflow_lightest_pressure_NAME = "Airflow - Small Movement Threshold %" - airflow_lightest_pressure_DESC = "Percent of 1 Atm. at which items with the small weight classes will move." - - airflow_light_pressure = 35 - airflow_light_pressure_NAME = "Airflow - Medium Movement Threshold %" - airflow_light_pressure_DESC = "Percent of 1 Atm. at which items with the medium weight classes will move." - - airflow_medium_pressure = 50 - airflow_medium_pressure_NAME = "Airflow - Heavy Movement Threshold %" - airflow_medium_pressure_DESC = "Percent of 1 Atm. at which items with the largest weight classes will move." - - airflow_heavy_pressure = 65 - airflow_heavy_pressure_NAME = "Airflow - Mob Movement Threshold %" - airflow_heavy_pressure_DESC = "Percent of 1 Atm. at which mobs will move." - - airflow_dense_pressure = 85 - airflow_dense_pressure_NAME = "Airflow - Dense Movement Threshold %" - airflow_dense_pressure_DESC = "Percent of 1 Atm. at which items with canisters and closets will move." - - airflow_stun_pressure = 60 - airflow_stun_pressure_NAME = "Airflow - Mob Stunning Threshold %" - airflow_stun_pressure_DESC = "Percent of 1 Atm. at which mobs will be stunned by airflow." - - airflow_stun_cooldown = 60 - airflow_stun_cooldown_NAME = "Aiflow Stunning - Cooldown" - airflow_stun_cooldown_DESC = "How long, in tenths of a second, to wait before stunning them again." - - airflow_stun = 1 - airflow_stun_NAME = "Airflow Impact - Stunning" - airflow_stun_DESC = "How much a mob is stunned when hit by an object." - - airflow_damage = 2 - airflow_damage_NAME = "Airflow Impact - Damage" - airflow_damage_DESC = "Damage from airflow impacts." - - airflow_speed_decay = 1.5 - airflow_speed_decay_NAME = "Airflow Speed Decay" - airflow_speed_decay_DESC = "How rapidly the speed gained from airflow decays." - - airflow_delay = 30 - airflow_delay_NAME = "Airflow Retrigger Delay" - airflow_delay_DESC = "Time in deciseconds before things can be moved by airflow again." - - airflow_mob_slowdown = 1 - airflow_mob_slowdown_NAME = "Airflow Slowdown" - airflow_mob_slowdown_DESC = "Time in tenths of a second to add as a delay to each movement by a mob if they are fighting the pull of the airflow." - - var/connection_insulation = 0.4 - var/connection_insulation_NAME = "Connections - Insulation" - var/connection_insulation_DESC = "How insulative a connection is, in terms of heat transfer. 1 is perfectly insulative, and 0 is perfectly conductive." - - var/connection_temperature_delta = 10 - var/connection_temperature_delta_NAME = "Connections - Temperature Difference" - var/connection_temperature_delta_DESC = "The smallest temperature difference which will cause heat to travel through doors." - -vs_control - var - list/settings = list() - list/bitflags = list("1","2","4","8","16","32","64","128","256","512","1024") // Oh jesus why. Learn to shift bits, you idiots. - pl_control/plc = new() - - New() - . = ..() - settings = vars.Copy() - - var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars. - for(var/V in D.vars) - settings -= V - - for(var/V in settings) - if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC") || findtextEx(V,"_METHOD")) - settings -= V - - settings -= "settings" - settings -= "bitflags" - settings -= "plc" - - proc/ChangeSettingsDialog(mob/user,list/L) - //var/which = input(user,"Choose a setting:") in L - var/dat = "" - for(var/ch in L) - if(findtextEx(ch,"_RANDOM") || findtextEx(ch,"_DESC") || findtextEx(ch,"_METHOD") || findtextEx(ch,"_NAME")) - continue - var/vw - var/vw_desc = "No Description." - var/vw_name = ch - if(ch in plc.settings) - vw = plc.vars[ch] - if("[ch]_DESC" in plc.vars) - vw_desc = plc.vars["[ch]_DESC"] - if("[ch]_NAME" in plc.vars) - vw_name = plc.vars["[ch]_NAME"] - else - vw = vars[ch] - if("[ch]_DESC" in vars) - vw_desc = vars["[ch]_DESC"] - if("[ch]_NAME" in vars) - vw_name = vars["[ch]_NAME"] - dat += "[vw_name] = [vw] \[Change\]
" - dat += "[vw_desc]

" - user << browse(dat,"window=settings") - Topic(href,href_list) - if("changevar" in href_list) - ChangeSetting(usr,href_list["changevar"]) - proc/ChangeSetting(mob/user,ch) - var/vw - var/how = "Text" - var/display_description = ch - if(ch in plc.settings) - vw = plc.vars[ch] - if("[ch]_NAME" in plc.vars) - display_description = plc.vars["[ch]_NAME"] - if("[ch]_METHOD" in plc.vars) - how = plc.vars["[ch]_METHOD"] - else - if(isnum(vw)) - how = "Numeric" - else - how = "Text" - else - vw = vars[ch] - if("[ch]_NAME" in vars) - display_description = vars["[ch]_NAME"] - if("[ch]_METHOD" in vars) - how = vars["[ch]_METHOD"] - else - if(isnum(vw)) - how = "Numeric" - else - how = "Text" - var/newvar = vw - switch(how) - if("Numeric") - newvar = input(user,"Enter a number:","Settings",newvar) as num - if("Bit Flag") - var/flag = input(user,"Toggle which bit?","Settings") in bitflags - flag = text2num(flag) - if(newvar & flag) - newvar &= ~flag - else - newvar |= flag - if("Toggle") - newvar = !newvar - if("Text") - newvar = input(user,"Enter a string:","Settings",newvar) as text - if("Long Text") - newvar = input(user,"Enter text:","Settings",newvar) as message - vw = newvar - if(ch in plc.settings) - plc.vars[ch] = vw - else - vars[ch] = vw - if(how == "Toggle") - newvar = (newvar?"ON":"OFF") - to_chat(world, "[key_name(user)] changed the setting [display_description] to [newvar].") - if(ch in plc.settings) - ChangeSettingsDialog(user,plc.settings) - else - ChangeSettingsDialog(user,settings) - proc/RandomizeWithProbability() - for(var/V in settings) - var/newvalue - if("[V]_RANDOM" in vars) - if(isnum(vars["[V]_RANDOM"])) - newvalue = prob(vars["[V]_RANDOM"]) - else if(istext(vars["[V]_RANDOM"])) - newvalue = roll(vars["[V]_RANDOM"]) - else - newvalue = vars[V] - V = newvalue - - proc/ChangePlasma() - for(var/V in plc.settings) - plc.Randomize(V) - - proc/SetDefault(var/mob/user) - var/list/setting_choices = list("Plasma - Standard", "Plasma - Low Hazard", "Plasma - High Hazard", "Plasma - Oh Shit!",\ - "ZAS - Normal", "ZAS - Forgiving", "ZAS - Dangerous", "ZAS - Hellish") - var/def = input(user, "Which of these presets should be used?") as null|anything in setting_choices - if(!def) - return - switch(def) - if("Plasma - Standard") - plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. - plc.PLASMAGUARD_ONLY = 0 - plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000. - plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited. - plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection. - plc.PLASMA_HALLUCINATION = 0 - plc.CONTAMINATION_LOSS = 0.02 - - if("Plasma - Low Hazard") - plc.CLOTH_CONTAMINATION = 0 //If this is on, plasma does damage by getting into cloth. - plc.PLASMAGUARD_ONLY = 0 - plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000 - plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited. - plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection. - plc.PLASMA_HALLUCINATION = 0 - plc.CONTAMINATION_LOSS = 0.01 - - if("Plasma - High Hazard") - plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. - plc.PLASMAGUARD_ONLY = 0 - plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000. - plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited. - plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection. - plc.PLASMA_HALLUCINATION = 1 - plc.CONTAMINATION_LOSS = 0.05 - - if("Plasma - Oh Shit!") - plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. - plc.PLASMAGUARD_ONLY = 1 - plc.GENETIC_CORRUPTION = 5 //Chance of genetic corruption as well as toxic damage, X in 1000. - plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited. - plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection. - plc.PLASMA_HALLUCINATION = 1 - plc.CONTAMINATION_LOSS = 0.075 - - if("ZAS - Normal") - airflow_push=0 - airflow_lightest_pressure = 20 - airflow_light_pressure = 35 - airflow_medium_pressure = 50 - airflow_heavy_pressure = 65 - airflow_dense_pressure = 85 - airflow_stun_pressure = 60 - airflow_stun_cooldown = 60 - airflow_stun = 1 - airflow_damage = 2 - airflow_speed_decay = 1.5 - airflow_delay = 30 - airflow_mob_slowdown = 1 - - if("ZAS - Forgiving") - airflow_push=0 - airflow_lightest_pressure = 45 - airflow_light_pressure = 60 - airflow_medium_pressure = 120 - airflow_heavy_pressure = 110 - airflow_dense_pressure = 200 - airflow_stun_pressure = 150 - airflow_stun_cooldown = 90 - airflow_stun = 0.15 - airflow_damage = 0.15 - airflow_speed_decay = 1.5 - airflow_delay = 50 - airflow_mob_slowdown = 0 - - if("ZAS - Dangerous") - airflow_push=1 - airflow_lightest_pressure = 15 - airflow_light_pressure = 30 - airflow_medium_pressure = 45 - airflow_heavy_pressure = 55 - airflow_dense_pressure = 70 - airflow_stun_pressure = 50 - airflow_stun_cooldown = 50 - airflow_stun = 2 - airflow_damage = 3 - airflow_speed_decay = 1.2 - airflow_delay = 25 - airflow_mob_slowdown = 2 - - if("ZAS - Hellish") - airflow_push=1 - airflow_lightest_pressure = 20 - airflow_light_pressure = 30 - airflow_medium_pressure = 40 - airflow_heavy_pressure = 50 - airflow_dense_pressure = 60 - airflow_stun_pressure = 40 - airflow_stun_cooldown = 40 - airflow_stun = 3 - airflow_damage = 4 - airflow_speed_decay = 1 - airflow_delay = 20 - airflow_mob_slowdown = 3 - - - to_chat(world, "[key_name(user)] changed the global plasma/ZAS settings to \"[def]\"") - -pl_control - var/list/settings = list() - New() - . = ..() - settings = vars.Copy() - - var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars. - for(var/V in D.vars) - settings -= V - - for(var/V in settings) - if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC")) - settings -= V - - settings -= "settings" - proc/Randomize(V) - var/newvalue - if("[V]_RANDOM" in vars) - if(isnum(vars["[V]_RANDOM"])) - newvalue = prob(vars["[V]_RANDOM"]) - else if(istext(vars["[V]_RANDOM"])) - var/txt = vars["[V]_RANDOM"] - if(findtextEx(txt,"PROB")) - txt = splittext(txt,"/") - txt[1] = replacetext(txt[1],"PROB","") - var/p = text2num(txt[1]) - var/r = txt[2] - if(prob(p)) - newvalue = roll(r) - else - newvalue = vars[V] - else if(findtextEx(txt,"PICK")) - txt = replacetext(txt,"PICK","") - txt = splittext(txt,",") - newvalue = pick(txt) - else - newvalue = roll(txt) - else - newvalue = vars[V] - vars[V] = newvalue diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm index fec27766a54..44db469efe0 100644 --- a/code/ZAS/Zone.dm +++ b/code/ZAS/Zone.dm @@ -28,11 +28,8 @@ Class Procs: rebuild() Invalidates the zone and marks all its former tiles for updates. - add_tile_air(turf/simulated/T) - Adds the air contained in T.air to the zone's air supply. Called when adding a turf. - tick() - Called only when the gas content is changed. Archives values and changes gas graphics. + Called only when the gas content is changed. Changes gas graphics. dbg_data(mob/M) Sends M a printout of important figures for the zone. @@ -51,8 +48,7 @@ Class Procs: /zone/New() SSair.add_zone(src) air.temperature = TCMB - air.group_multiplier = 1 - air.volume = CELL_VOLUME + air.volume = 0 /zone/proc/add(turf/simulated/T) #ifdef ZASDBG @@ -62,7 +58,8 @@ Class Procs: #endif var/datum/gas_mixture/turf_air = T.return_air() - add_tile_air(turf_air) + air.volume += turf_air.volume + air.merge(turf_air) T.zone = src contents.Add(T) T.set_graphic(air.graphics) @@ -74,12 +71,14 @@ Class Procs: ASSERT(T.zone == src) soft_assert(T in contents, "Lists are weird broseph") #endif - contents.Remove(T) + T.zone = null + var/datum/gas_mixture/turf_air = T.return_air() + air.volume -= turf_air.volume + air.divide(1 + turf_air.volume / air.volume) + contents.Remove(T) T.set_graphic(0) - if(contents.len) - air.group_multiplier = contents.len - else + if(!contents.len) c_invalidate() /zone/proc/c_merge(zone/into) @@ -113,26 +112,36 @@ Class Procs: T.needs_air_update = 0 //Reset the marker so that it will be added to the list. SSair.mark_for_update(T) -/zone/proc/add_tile_air(datum/gas_mixture/tile_air) - //air.volume += CELL_VOLUME - air.group_multiplier = 1 - air.multiply(contents.len) - air.merge(tile_air) - air.divide(contents.len+1) - air.group_multiplier = contents.len+1 +//Gets a list of the gas_mixtures of all zones connected to this one through arbitrarily many sleeping edges. +//This is to cut down somewhat on differentials across open doors. +//Yes, recursion is slow, but this will generally not be called very often, and will rarely have to recurse more than a few levels deep. +//That said, feel free to optimize it if you want. +// +//At the top level, just call it with no arg. The arg generally is for internal use. +/zone/proc/get_equalized_zone_air(list/found = list()) + found += air + . = found //I want to minimize the call stack left over after the recursive call. Honestly the implicit return is probably the same as an explicit one, but I'd rather play it safe. + for(var/connection_edge/zone/E in edges) + if(E.sleeping) + var/zone/Z = E.get_connected_zone(src) + if(!(Z.air in found)) + Z.get_equalized_zone_air(found) /zone/proc/tick() - air.archive() if(air.check_tile_graphic()) for(var/turf/simulated/T in contents) T.set_graphic(air.graphics) + for(var/connection_edge/E in edges) + if(E.sleeping) + E.recheck() + /zone/proc/dbg_data(mob/M) to_chat(M, name) to_chat(M, "O2: [air.oxygen] N2: [air.nitrogen] CO2: [air.carbon_dioxide] P: [air.toxins]") to_chat(M, "P: [air.return_pressure()] kPa V: [air.volume]L T: [air.temperature]�K ([air.temperature - T0C]�C)") to_chat(M, "O2 per N2: [(air.nitrogen ? air.oxygen/air.nitrogen : "N/A")] Moles: [air.total_moles]") - to_chat(M, "Simulated: [contents.len] ([air.group_multiplier])") + to_chat(M, "Simulated: [contents.len] ([air.volume / CELL_VOLUME])") // to_chat(M, "Unsimulated: [unsimulated_contents.len]") // to_chat(M, "Edges: [edges.len]") if(invalid) diff --git a/code/ZAS/_gas_mixture.dm b/code/ZAS/_gas_mixture.dm index 8d8e6b20fb9..bd7e855f005 100644 --- a/code/ZAS/_gas_mixture.dm +++ b/code/ZAS/_gas_mixture.dm @@ -1,9 +1,3 @@ -/* -What are the archived variables for? - Calculations are done using the archived variables with the results merged into the regular variables. - This prevents race conditions that arise based on the order of tile processing. -*/ - #define SPECIFIC_HEAT_TOXIN 200 #define SPECIFIC_HEAT_AIR 20 #define SPECIFIC_HEAT_CDO 30 @@ -11,7 +5,6 @@ What are the archived variables for? max(0, carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN) #define MINIMUM_HEAT_CAPACITY 0.0003 -#define QUANTIZE(variable) (round(variable,0.0001)) #define TRANSFER_FRACTION 5 //What fraction (1/#) of the air difference to try and transfer // /vg/ SHIT @@ -20,7 +13,7 @@ What are the archived variables for? #define GRAPHICS_PLASMA 1 #define GRAPHICS_N2O 2 -#define GRAPHICS_REAGENTS 4 // Not used. Yet. +#define GRAPHICS_REAGENTS 4 //Not used. Yet. #define GRAPHICS_COLD 8 // END /vg/SHIT @@ -51,9 +44,7 @@ What are the archived variables for? var/specific_heat = 0 - var/moles_archived = 0 - -/datum/gas_mixture/ +/datum/gas_mixture var/oxygen = 0 //Holds the "moles" of each of the four gases. var/carbon_dioxide = 0 var/nitrogen = 0 @@ -65,38 +56,25 @@ What are the archived variables for? var/temperature = 0 //in Kelvin, use calculate_temperature() to modify - var/group_multiplier = 1 - //Size of the group this gas_mixture is representing. - //=1 for singletons - var/graphics=0 var/pressure=0 var/list/datum/gas/trace_gases = list() //Seemed to be a good idea that was abandoned - var/tmp/oxygen_archived //These are variables for use with the archived data - var/tmp/carbon_dioxide_archived - var/tmp/nitrogen_archived - var/tmp/toxins_archived - - var/tmp/temperature_archived - - var/tmp/graphics_archived = 0 var/tmp/fuel_burnt = 0 - //var/datum/reagents/aerosols -/* -/datum/gas_mixture/New() - //create_reagents(10) -*/ - -//FOR THE LOVE OF GOD PLEASE USE THIS PROC -//Call it with negative numbers to remove gases. +/datum/gas_mixture/New(datum/gas_mixture/to_copy) + ..() + if(istype(to_copy)) + volume = to_copy.volume + copy_from(to_copy) +//Turns out that most of the time, people only want to adjust a single gas at a time, and using a proc set up like this just encourages bad behavior. +//To be purged along with the trace gas system later. /datum/gas_mixture/proc/adjust(o2 = 0, co2 = 0, n2 = 0, tx = 0, list/datum/gas/traces = list()) //Purpose: Adjusting the gases within a airmix - //Called by: Nothing, yet! + //Called by: Fucking everything! //Inputs: The values of the gases to adjust //Outputs: null @@ -115,43 +93,105 @@ What are the archived variables for? update_values() return -//Takes a gas string, and the amount of moles to adjust by. Calls update_values() if update isn't 0. -/datum/gas_mixture/proc/adjust_gas(gasid, moles, update = 1) - if(moles == 0) + +//Takes a gas string, and the number of moles to adjust by. Calls update_values() if update isn't 0. +/datum/gas_mixture/proc/adjust_gas(gasid, moles, update = TRUE) + if(!moles) return switch(gasid) if("oxygen") - if (group_multiplier != 1) - oxygen += moles/group_multiplier - else - oxygen += moles + oxygen += moles if("plasma") - if (group_multiplier != 1) - toxins += moles/group_multiplier - else - toxins += moles + toxins += moles if("carbon_dioxide") - if (group_multiplier != 1) - carbon_dioxide += moles/group_multiplier - else - carbon_dioxide += moles + carbon_dioxide += moles if("nitrogen") - if (group_multiplier != 1) - nitrogen += moles/group_multiplier - else - nitrogen += moles - + nitrogen += moles + else + CRASH("Invalid gasid!") if(update) update_values() -/* -/datum/gas_mixture/proc/create_reagents(var/max_vol) - aerosols = new /datum/reagents(max_vol) - aerosols.my_atom = src -*/ -//tg seems to like using these a lot +//Same as adjust_gas(), but takes a temperature which is mixed in with the gas. +/datum/gas_mixture/proc/adjust_gas_temp(gasid, moles, temp, update = TRUE) + if(moles > 0 && abs(temperature - temp) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity() + + var/giver_heat_capacity = moles + switch(gasid) + if("oxygen", "nitrogen") + giver_heat_capacity *= SPECIFIC_HEAT_AIR + if("plasma") + giver_heat_capacity *= SPECIFIC_HEAT_TOXIN + if("carbon_dioxide") + giver_heat_capacity *= SPECIFIC_HEAT_CDO + else + CRASH("Invalid gasid!") + + var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity + temperature = (temp * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity + + adjust_gas(gasid, moles, update) + + if(update) + update_values() + + +//Variadic version of adjust_gas(). Takes any number of gas and mole pairs and applies them. +/datum/gas_mixture/proc/adjust_multi() + ASSERT(!(args.len % 2)) + + for(var/i = 1; i < args.len; i += 2) + adjust_gas(args[i], args[i + 1], update = FALSE) + + update_values() + + +//Variadic version of adjust_gas_temp(). Takes any number of gas, mole and temperature associations and applies them. +/datum/gas_mixture/proc/adjust_multi_temp() + ASSERT(!(args.len % 3)) + + for(var/i = 1; i < args.len; i += 3) + adjust_gas_temp(args[i], args[i + 1], args[i + 2], update = FALSE) + + update_values() + + +//Merges all the gas from another mixture into this one. Adjusts temperature correctly. +//Does not modify giver in any way. +/datum/gas_mixture/proc/merge(datum/gas_mixture/giver) + if(!giver) + return 0 + + adjust_multi_temp(\ + "oxygen", giver.oxygen, giver.temperature,\ + "nitrogen", giver.nitrogen, giver.temperature,\ + "plasma", giver.toxins, giver.temperature,\ + "carbon_dioxide", giver.carbon_dioxide, giver.temperature) + + if(giver.trace_gases.len) //This really should use adjust(), but I think it would break things, and I don't care enough to fix a system I'm removing soon anyway. + for(var/datum/gas/trace_gas in giver.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + trace_gases += corresponding + corresponding.moles += trace_gas.moles + update_values() + + return 1 + + +//Equalizes this mixture's gases with another's, changing both mixtures. Essentially, fully mixes the two, but keeps them separate. +//Modifies sharer as well as src. (I'll laugh if someone poorly regexes out src. and this comment becomes incomprehensible) +/datum/gas_mixture/proc/equalize(datum/gas_mixture/sharer) + merge(sharer) + sharer.multiply(0) //Empty it out. + sharer.merge(remove_ratio(sharer.volume / (volume + sharer.volume))) + return 1 + + /datum/gas_mixture/proc/return_temperature() return temperature @@ -181,56 +221,65 @@ What are the archived variables for? return max(MINIMUM_HEAT_CAPACITY,heat_capacity) -/datum/gas_mixture/proc/heat_capacity_archived() - //Purpose: Returning the archived heat capacity of the gas mix - //Called by: UNKNOWN - //Inputs: None - //Outputs: Archived heat capacity - var/heat_capacity_archived = HEAT_CAPACITY_CALCULATION(oxygen_archived,carbon_dioxide_archived,nitrogen_archived,toxins_archived) +//Adds or removes thermal energy. Returns the actual thermal energy change, as in the case of removing energy we can't go below TCMB. +/datum/gas_mixture/proc/add_thermal_energy(var/thermal_energy) + if (total_moles == 0) + return 0 - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - heat_capacity_archived += trace_gas.moles_archived*trace_gas.specific_heat + var/heat_capacity = heat_capacity() + if (thermal_energy < 0) + if (temperature < TCMB) + return 0 + var/thermal_energy_limit = -(temperature - TCMB)*heat_capacity //ensure temperature does not go below TCMB + thermal_energy = max( thermal_energy, thermal_energy_limit ) //thermal_energy and thermal_energy_limit are negative here. + temperature += thermal_energy/heat_capacity + return thermal_energy - return max(MINIMUM_HEAT_CAPACITY,heat_capacity_archived) -/datum/gas_mixture/proc/total_moles() - return total_moles - /*var/moles = oxygen + carbon_dioxide + nitrogen + toxins +//Returns the thermal energy change required to get to a new temperature +/datum/gas_mixture/proc/get_thermal_energy_change(var/new_temperature) + return heat_capacity()*(max(new_temperature, 0) - temperature) - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - moles += trace_gas.moles - return moles*/ -/datum/gas_mixture/proc/return_pressure() - //Purpose: Calculating Current Pressure - //Called by: - //Inputs: None - //Outputs: Gas pressure. - return pressure +//The below wasn't even implemented yet in the big XGM PR. Just saving it here for later. -// proc/return_temperature() - //Purpose: - //Inputs: - //Outputs: +////Technically vacuum doesn't have a specific entropy. Just use a really big number (infinity would be ideal) here so that it's easy to add gas to vacuum and hard to take gas out. +//#define SPECIFIC_ENTROPY_VACUUM 150000 +// +// +////Returns the ideal gas specific entropy of the whole mix. This is the entropy per mole of /mixed/ gas. +///datum/gas_mixture/proc/specific_entropy() +// if (!gas.len || total_moles == 0) +// return SPECIFIC_ENTROPY_VACUUM +// +// . = 0 +// for(var/g in gas) +// . += gas[g] * specific_entropy_gas(g) +// . /= total_moles +// +// +///* +// It's arguable whether this should even be called entropy anymore. It's more "based on" entropy than actually entropy now. +// Returns the ideal gas specific entropy of a specific gas in the mix. This is the entropy due to that gas per mole of /that/ gas in the mixture, not the entropy due to that gas per mole of gas mixture. +// For the purposes of SS13, the specific entropy is just a number that tells you how hard it is to move gas. You can replace this with whatever you want. +// Just remember that returning a SMALL number == adding gas to this gas mix is HARD, taking gas away is EASY, and that returning a LARGE number means the opposite (so a vacuum should approach infinity). +// So returning a constant/(partial pressure) would probably do what most players expect. Although the version I have implemented below is a bit more nuanced than simply 1/P in that it scales in a way +// which is bit more realistic (natural log), and returns a fairly accurate entropy around room temperatures and pressures. +//*/ +///datum/gas_mixture/proc/specific_entropy_gas(var/gasid) +// if (!(gasid in gas) || gas[gasid] == 0) +// return SPECIFIC_ENTROPY_VACUUM //that gas isn't here +// +// //V/(m*T) = R/(partial pressure) +// var/molar_mass = XGM.molar_mass[gasid] +// var/specific_heat = XGM.specific_heat[gasid] +// return R_IDEAL_GAS_EQUATION * ( log( (IDEAL_GAS_ENTROPY_CONSTANT*volume/(gas[gasid] * temperature)) * (molar_mass*specific_heat*temperature)**(2/3) + 1 ) + 15 ) +// +// //alternative, simpler equation +// //var/partial_pressure = gas[gasid] * R_IDEAL_GAS_EQUATION * temperature / volume +// //return R_IDEAL_GAS_EQUATION * ( log (1 + IDEAL_GAS_ENTROPY_CONSTANT/partial_pressure) + 20 ) -// return temperature - -// proc/return_volume() - //Purpose: - //Inputs: - //Outputs: - -// return max(0, volume) - -// proc/thermal_energy() - //Purpose: - //Inputs: - //Outputs: - -// return temperature*heat_capacity() /datum/gas_mixture/proc/update_values() //Purpose: Calculating and storing values which were normally called CONSTANTLY @@ -244,11 +293,6 @@ What are the archived variables for? for(var/datum/gas/trace_gas in trace_gases) total_moles += trace_gas.moles -/* - if(aerosols.total_volume) - total_moles += aerosols.total_volume -*/ - if(volume>0) pressure = total_moles()*R_IDEAL_GAS_EQUATION*temperature/volume else @@ -256,205 +300,18 @@ What are the archived variables for? return -//////////////////////////////////////////// -//Procedures used for very specific events// -//////////////////////////////////////////// -/datum/gas_mixture/proc/check_tile_graphic() - //Purpose: Calculating the graphic for a tile - //Called by: Turfs updating +/datum/gas_mixture/proc/total_moles() + return total_moles + + +/datum/gas_mixture/proc/return_pressure() + //Purpose: Calculating Current Pressure + //Called by: //Inputs: None - //Outputs: 1 if graphic changed, 0 if unchanged + //Outputs: Gas pressure. + return pressure - graphics = 0 - - // If configured and cold, maek ice - if(zas_settings.Get(/datum/ZAS_Setting/ice_formation)) - if(temperature <= TEMPERATURE_ICE_FORMATION && return_pressure()>MIN_PRESSURE_ICE_FORMATION) - // If we're just forming, do a probability check. Otherwise, KEEP IT ON~ - // This ordering will hopefully keep it from sampling random noise every damn tick. - //if(was_icy || (!was_icy && prob(25))) - graphics |= GRAPHICS_COLD - - if(toxins > MOLES_PLASMA_VISIBLE) - graphics |= GRAPHICS_PLASMA - - if(length(trace_gases)) - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - graphics |= GRAPHICS_N2O -/* - if(aerosols && aerosols.total_volume >= 1) - graphics |= GRAPHICS_REAGENTS -*/ - - return graphics != graphics_archived - -/datum/gas_mixture/proc/react(atom/dump_location) - //Purpose: Calculating if it is possible for a fire to occur in the airmix - //Called by: Air mixes updating? - //Inputs: None - //Outputs: If a fire occured - - //set to 1 if a notable reaction occured (used by pipe_network) - - return zburn(null) // ? (was: return reacting) - -/datum/gas_mixture/proc/fire() - //Purpose: Calculating any fire reactions. - //Called by: react() (See above) - //Inputs: None - //Outputs: How much fuel burned - - return zburn(null) - - /*var/energy_released = 0 - var/old_heat_capacity = heat_capacity() - - var/datum/gas/volatile_fuel/fuel_store = locate(/datum/gas/volatile_fuel) in trace_gases - if(fuel_store) //General volatile gas burn - var/burned_fuel = 0 - - if(oxygen < fuel_store.moles) - burned_fuel = oxygen - fuel_store.moles -= burned_fuel - oxygen = 0 - else - burned_fuel = fuel_store.moles - oxygen -= fuel_store.moles - del(fuel_store) - - energy_released += FIRE_CARBON_ENERGY_RELEASED * burned_fuel - carbon_dioxide += burned_fuel - fuel_burnt += burned_fuel - - //Handle plasma burning - if(toxins > MINIMUM_HEAT_CAPACITY) - var/plasma_burn_rate = 0 - var/oxygen_burn_rate = 0 - //more plasma released at higher temperatures - var/temperature_scale - if(temperature > PLASMA_UPPER_TEMPERATURE) - temperature_scale = 1 - else - temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE) - if(temperature_scale > 0) - oxygen_burn_rate = 1.4 - temperature_scale - if(oxygen > toxins*PLASMA_OXYGEN_FULLBURN) - plasma_burn_rate = (toxins*temperature_scale)/4 - else - plasma_burn_rate = (temperature_scale*(oxygen/PLASMA_OXYGEN_FULLBURN))/4 - if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY) - toxins -= plasma_burn_rate - oxygen -= plasma_burn_rate*oxygen_burn_rate - carbon_dioxide += plasma_burn_rate - - energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate) - - fuel_burnt += (plasma_burn_rate)*(1+oxygen_burn_rate) - - if(energy_released > 0) - var/new_heat_capacity = heat_capacity() - if(new_heat_capacity > MINIMUM_HEAT_CAPACITY) - temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity - update_values() - - return fuel_burnt*/ - -////////////////////////////////////////////// -//Procs for general gas spread calculations.// -////////////////////////////////////////////// - - -/datum/gas_mixture/proc/archive() - //Purpose: Archives the current gas values - //Called by: UNKNOWN - //Inputs: None - //Outputs: 1 - - oxygen_archived = oxygen - carbon_dioxide_archived = carbon_dioxide - nitrogen_archived = nitrogen - toxins_archived = toxins - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - trace_gas.moles_archived = trace_gas.moles - - temperature_archived = temperature - - graphics_archived = graphics - - return 1 - -/datum/gas_mixture/proc/check_then_merge(datum/gas_mixture/giver) - //Purpose: Similar to merge(...) but first checks to see if the amount of air assumed is small enough - // that group processing is still accurate for source (aborts if not) - //Called by: airgroups/machinery expelling air, ? - //Inputs: The gas to try and merge - //Outputs: 1 on successful merge. 0 otherwise. - - if(!giver) - return 0 - if(((giver.oxygen > MINIMUM_AIR_TO_SUSPEND) && (giver.oxygen >= oxygen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((giver.carbon_dioxide > MINIMUM_AIR_TO_SUSPEND) && (giver.carbon_dioxide >= carbon_dioxide*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((giver.nitrogen > MINIMUM_AIR_TO_SUSPEND) && (giver.nitrogen >= nitrogen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((giver.toxins > MINIMUM_AIR_TO_SUSPEND) && (giver.toxins >= toxins*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - if(abs(giver.temperature - temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) - return 0 - - if(giver.trace_gases.len) - for(var/datum/gas/trace_gas in giver.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if((trace_gas.moles > MINIMUM_AIR_TO_SUSPEND) && (!corresponding || (trace_gas.moles >= corresponding.moles*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - - return merge(giver) - -/datum/gas_mixture/proc/merge(datum/gas_mixture/giver) - //Purpose: Merges all air from giver into self. Deletes giver. - //Called by: Machinery expelling air, check_then_merge, ? - //Inputs: The gas to merge. - //Outputs: 1 - - if(!giver) - return 0 - - if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity()*group_multiplier - var/giver_heat_capacity = giver.heat_capacity()*giver.group_multiplier - var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity - if(combined_heat_capacity != 0) - temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity - - if((group_multiplier>1)||(giver.group_multiplier>1)) - oxygen += giver.oxygen*giver.group_multiplier/group_multiplier - carbon_dioxide += giver.carbon_dioxide*giver.group_multiplier/group_multiplier - nitrogen += giver.nitrogen*giver.group_multiplier/group_multiplier - toxins += giver.toxins*giver.group_multiplier/group_multiplier - else - oxygen += giver.oxygen - carbon_dioxide += giver.carbon_dioxide - nitrogen += giver.nitrogen - toxins += giver.toxins - - if(giver.trace_gases.len) - for(var/datum/gas/trace_gas in giver.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - trace_gases += corresponding - corresponding.moles += trace_gas.moles*giver.group_multiplier/group_multiplier -/* - if(giver.aerosols.total_volume > 1) - giver.aerosols.trans_to_atmos(src,aerosols.total_volume) -*/ - update_values() - - // Let the garbage collector handle it, faster according to /tg/ testers - //del(giver) - return 1 /datum/gas_mixture/proc/remove(amount) //Purpose: Removes a certain number of moles from the air. @@ -462,10 +319,6 @@ What are the archived variables for? //Inputs: How many moles to remove. //Outputs: Removed air. - // Fix a singuloth problem - if(group_multiplier==0) - return null - var/sum = total_moles() amount = min(amount,sum) //Can not take more air than tile has! if(amount <= 0) @@ -479,10 +332,10 @@ What are the archived variables for? removed.carbon_dioxide = QUANTIZE((carbon_dioxide/sum)*amount) removed.toxins = QUANTIZE((toxins/sum)*amount) - oxygen -= removed.oxygen/group_multiplier - nitrogen -= removed.nitrogen/group_multiplier - carbon_dioxide -= removed.carbon_dioxide/group_multiplier - toxins -= removed.toxins/group_multiplier + oxygen -= removed.oxygen + nitrogen -= removed.nitrogen + carbon_dioxide -= removed.carbon_dioxide + toxins -= removed.toxins if(trace_gases.len) for(var/datum/gas/trace_gas in trace_gases) @@ -490,7 +343,7 @@ What are the archived variables for? removed.trace_gases += corresponding corresponding.moles = (trace_gas.moles/sum)*amount - trace_gas.moles -= corresponding.moles/group_multiplier + trace_gas.moles -= corresponding.moles /* if(aerosols.total_volume > 1) removed.aerosols.trans_to_atmos(src,(aerosols.total_volume/sum)*amount) @@ -502,6 +355,7 @@ What are the archived variables for? return removed + /datum/gas_mixture/proc/remove_ratio(ratio) //Purpose: Removes a certain ratio of the air. //Called by: ? @@ -520,10 +374,10 @@ What are the archived variables for? removed.carbon_dioxide = QUANTIZE(carbon_dioxide*ratio) removed.toxins = QUANTIZE(toxins*ratio) - oxygen -= removed.oxygen/group_multiplier - nitrogen -= removed.nitrogen/group_multiplier - carbon_dioxide -= removed.carbon_dioxide/group_multiplier - toxins -= removed.toxins/group_multiplier + oxygen -= removed.oxygen + nitrogen -= removed.nitrogen + carbon_dioxide -= removed.carbon_dioxide + toxins -= removed.toxins if(trace_gases.len) for(var/datum/gas/trace_gas in trace_gases) @@ -531,7 +385,7 @@ What are the archived variables for? removed.trace_gases += corresponding corresponding.moles = trace_gas.moles*ratio - trace_gas.moles -= corresponding.moles/group_multiplier + trace_gas.moles -= corresponding.moles removed.temperature = temperature update_values() @@ -539,22 +393,69 @@ What are the archived variables for? return removed -/datum/gas_mixture/proc/check_then_remove(amount) - //Purpose: Similar to remove(...) but first checks to see if the amount of air removed is small enough - // that group processing is still accurate for source (aborts if not) - //Called by: ? - //Inputs: Number of moles to remove - //Outputs: Removed air or 0 if it can remove air or not. - amount = min(amount,total_moles()) //Can not take more air than tile has! +//Removes a volume of gas from the mixture and returns a gas_mixture containing the removed air with the given volume. +/datum/gas_mixture/proc/remove_volume(removed_volume) + var/datum/gas_mixture/removed = remove_ratio(removed_volume/volume) + removed.volume = removed_volume + removed.update_values() + return removed - if((amount > MINIMUM_AIR_RATIO_TO_SUSPEND) && (amount > total_moles()*MINIMUM_AIR_RATIO_TO_SUSPEND)) - return 0 - return remove(amount) +//////////////////////////////////////////// +//Procedures used for very specific events// +//////////////////////////////////////////// + +/datum/gas_mixture/proc/check_tile_graphic() + //Purpose: Calculating the graphic for a tile + //Called by: Turfs updating + //Inputs: None + //Outputs: 1 if graphic changed, 0 if unchanged + + var/old_graphics = graphics + + graphics = 0 + + // If configured and cold, maek ice + if(zas_settings.Get(/datum/ZAS_Setting/ice_formation)) + if(temperature <= TEMPERATURE_ICE_FORMATION && return_pressure()>MIN_PRESSURE_ICE_FORMATION) + // If we're just forming, do a probability check. Otherwise, KEEP IT ON~ + // This ordering will hopefully keep it from sampling random noise every damn tick. + //if(was_icy || (!was_icy && prob(25))) + graphics |= GRAPHICS_COLD + + if(toxins > MOLES_PLASMA_VISIBLE) + graphics |= GRAPHICS_PLASMA + + if(length(trace_gases)) + var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in trace_gases + if(sleeping_agent && (sleeping_agent.moles > 1)) + graphics |= GRAPHICS_N2O +/* + if(aerosols && aerosols.total_volume >= 1) + graphics |= GRAPHICS_REAGENTS +*/ + + return graphics != old_graphics + +/datum/gas_mixture/proc/react(atom/dump_location) + //Purpose: Calculating if it is possible for a fire to occur in the airmix + //Called by: Air mixes updating? + //Inputs: None + //Outputs: If a fire occured + + //set to 1 if a notable reaction occured (used by pipe_network) + + return zburn(null) // ? (was: return reacting) + + +////////////////////////////////////////////// +//Procs for general gas spread calculations.// +////////////////////////////////////////////// + /datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample) - //Purpose: Duplicates the sample air mixture. + //Purpose: Copies the per unit volume properties of sample. //Called by: airgroups splitting, ? //Inputs: Gas to copy //Outputs: 1 @@ -563,9 +464,10 @@ What are the archived variables for? carbon_dioxide = sample.carbon_dioxide nitrogen = sample.nitrogen toxins = sample.toxins - total_moles = sample.total_moles() - trace_gases.len=null + temperature = sample.temperature + + trace_gases.len = 0 if(sample.trace_gases.len > 0) for(var/datum/gas/trace_gas in sample.trace_gases) var/datum/gas/corresponding = new trace_gas.type() @@ -573,541 +475,69 @@ What are the archived variables for? corresponding.moles = trace_gas.moles - temperature = sample.temperature - + multiply(volume / sample.volume) return 1 -/datum/gas_mixture/proc/check_gas_mixture(datum/gas_mixture/sharer) - //Purpose: Telling if one or both airgroups needs to disable group processing. - //Called by: Airgroups sharing air, checking if group processing needs disabled. - //Inputs: Gas to compare from other airgroup - //Outputs: 0 if the self-check failed (local airgroup breaks?) - // then -1 if sharer-check failed (sharing airgroup breaks?) - // then 1 if both checks pass (share succesful?) - if(!istype(sharer)) - return +//The general form of the calculation used in compare() to check if two numbers are separated by at least a given abslute value AND relative value. +//Not guaranteed to produce the same result if the order of the vars to check is switched, but since its purpose is approximation anyway, it should be fine. +#define FAIL_SIMILARITY_CHECK(ownvar, samplevar, absolute, relative) (abs((ownvar) - (samplevar)) > (absolute) && \ + (((ownvar) < (1 - (relative)) * (samplevar)) || ((ownvar) > (1 + (relative)) * (samplevar)))) - var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION - var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION - var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION - var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) - return 0 - - if(sharer.trace_gases.len) - for(var/datum/gas/trace_gas in sharer.trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases - if(corresponding) - if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4) - return 0 - else - return 0 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - if(!locate(trace_gas.type) in sharer.trace_gases) - return 0 - - if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= sharer.oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= sharer.carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= sharer.nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= sharer.toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return -1 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases - if(corresponding) - if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4) - return -1 - else - return -1 - - return 1 - -/datum/gas_mixture/proc/check_turf(turf/model) - //Purpose: Used to compare the gases in an unsimulated turf with the gas in a simulated one. - //Called by: Sharing air (mimicing) with adjacent unsimulated turfs - //Inputs: Unsimulated turf - //Outputs: 1 if safe to mimic, 0 if needs to break airgroup. - - var/delta_oxygen = (oxygen_archived - model.oxygen)/TRANSFER_FRACTION - var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION - var/delta_nitrogen = (nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION - var/delta_toxins = (toxins_archived - model.toxins)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - model.temperature) - - if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ - || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) - return 0 - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) - return 0 - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) - return 0 - - return 1 - -/datum/gas_mixture/proc/share(datum/gas_mixture/sharer) - //Purpose: Used to transfer gas from a more pressurised tile to a less presurised tile - // (Two directional, if the other tile is more pressurised, air travels to current tile) - //Called by: Sharing air with adjacent simulated turfs - //Inputs: Air datum to share with - //Outputs: Amount of gas exchanged (Negative if lost air, positive if gained.) - - - if(!istype(sharer)) - return - - var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION - var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION - var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION - var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - var/old_self_heat_capacity = 0 - var/old_sharer_heat_capacity = 0 - - var/heat_self_to_sharer = 0 - var/heat_capacity_self_to_sharer = 0 - var/heat_sharer_to_self = 0 - var/heat_capacity_sharer_to_self = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - - var/delta_air = delta_oxygen+delta_nitrogen - if(delta_air) - var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air - if(delta_air > 0) - heat_self_to_sharer += air_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += air_heat_capacity - else - heat_sharer_to_self -= air_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= air_heat_capacity - - if(delta_carbon_dioxide) - var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide - if(delta_carbon_dioxide > 0) - heat_self_to_sharer += carbon_dioxide_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += carbon_dioxide_heat_capacity - else - heat_sharer_to_self -= carbon_dioxide_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= carbon_dioxide_heat_capacity - - if(delta_toxins) - var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins - if(delta_toxins > 0) - heat_self_to_sharer += toxins_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += toxins_heat_capacity - else - heat_sharer_to_self -= toxins_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= toxins_heat_capacity - - old_self_heat_capacity = heat_capacity()*group_multiplier - old_sharer_heat_capacity = sharer.heat_capacity()*sharer.group_multiplier - - oxygen -= delta_oxygen/group_multiplier - sharer.oxygen += delta_oxygen/sharer.group_multiplier - - carbon_dioxide -= delta_carbon_dioxide/group_multiplier - sharer.carbon_dioxide += delta_carbon_dioxide/sharer.group_multiplier - - nitrogen -= delta_nitrogen/group_multiplier - sharer.nitrogen += delta_nitrogen/sharer.group_multiplier - - toxins -= delta_toxins/group_multiplier - sharer.toxins += delta_toxins/sharer.group_multiplier - - var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins) - - var/list/trace_types_considered = list() - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - - var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases - var/delta = 0 - - if(corresponding) - delta = QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)/TRANSFER_FRACTION - else - corresponding = new trace_gas.type() - sharer.trace_gases += corresponding - - delta = trace_gas.moles_archived/TRANSFER_FRACTION - - trace_gas.moles -= delta/group_multiplier - corresponding.moles += delta/sharer.group_multiplier - - if(delta) - var/individual_heat_capacity = trace_gas.specific_heat*delta - if(delta > 0) - heat_self_to_sharer += individual_heat_capacity*temperature_archived - heat_capacity_self_to_sharer += individual_heat_capacity - else - heat_sharer_to_self -= individual_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self -= individual_heat_capacity - - moved_moles += delta - - trace_types_considered += trace_gas.type - - - if(sharer.trace_gases.len) - for(var/datum/gas/trace_gas in sharer.trace_gases) - if(trace_gas.type in trace_types_considered) - continue - else - var/datum/gas/corresponding - var/delta = 0 - - corresponding = new trace_gas.type() - trace_gases += corresponding - - delta = trace_gas.moles_archived/TRANSFER_FRACTION - - trace_gas.moles -= delta/sharer.group_multiplier - corresponding.moles += delta/group_multiplier - - //Guaranteed transfer from sharer to self - var/individual_heat_capacity = trace_gas.specific_heat*delta - heat_sharer_to_self += individual_heat_capacity*sharer.temperature_archived - heat_capacity_sharer_to_self += individual_heat_capacity - - moved_moles += -delta - update_values() - sharer.update_values() - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/new_self_heat_capacity = old_self_heat_capacity + heat_capacity_sharer_to_self - heat_capacity_self_to_sharer - var/new_sharer_heat_capacity = old_sharer_heat_capacity + heat_capacity_self_to_sharer - heat_capacity_sharer_to_self - - if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY) - temperature = (old_self_heat_capacity*temperature - heat_capacity_self_to_sharer*temperature_archived + heat_capacity_sharer_to_self*sharer.temperature_archived)/new_self_heat_capacity - - if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) - sharer.temperature = (old_sharer_heat_capacity*sharer.temperature-heat_capacity_sharer_to_self*sharer.temperature_archived + heat_capacity_self_to_sharer*temperature_archived)/new_sharer_heat_capacity - - if(abs(old_sharer_heat_capacity) > MINIMUM_HEAT_CAPACITY) - if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity - temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT) - - if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) - var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - sharer.temperature_archived*(sharer.total_moles() - moved_moles) - return delta_pressure*R_IDEAL_GAS_EQUATION/volume - - else - return 0 - -/datum/gas_mixture/proc/mimic(turf/model, border_multiplier) - //Purpose: Used transfer gas from a more pressurised tile to a less presurised unsimulated tile. - //Called by: "sharing" from unsimulated to simulated turfs. - //Inputs: Unsimulated turf, Multiplier for gas transfer (optional) - //Outputs: Amount of gas exchanged - - var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen)/TRANSFER_FRACTION - var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION - var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION - var/delta_toxins = QUANTIZE(toxins_archived - model.toxins)/TRANSFER_FRACTION - - var/delta_temperature = (temperature_archived - model.temperature) - - var/heat_transferred = 0 - var/old_self_heat_capacity = 0 - var/heat_capacity_transferred = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - - var/delta_air = delta_oxygen+delta_nitrogen - if(delta_air) - var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air - heat_transferred -= air_heat_capacity*model.temperature - heat_capacity_transferred -= air_heat_capacity - - if(delta_carbon_dioxide) - var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide - heat_transferred -= carbon_dioxide_heat_capacity*model.temperature - heat_capacity_transferred -= carbon_dioxide_heat_capacity - - if(delta_toxins) - var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins - heat_transferred -= toxins_heat_capacity*model.temperature - heat_capacity_transferred -= toxins_heat_capacity - - old_self_heat_capacity = heat_capacity()*group_multiplier - - if(border_multiplier) - oxygen -= delta_oxygen*border_multiplier/group_multiplier - carbon_dioxide -= delta_carbon_dioxide*border_multiplier/group_multiplier - nitrogen -= delta_nitrogen*border_multiplier/group_multiplier - toxins -= delta_toxins*border_multiplier/group_multiplier - else - oxygen -= delta_oxygen/group_multiplier - carbon_dioxide -= delta_carbon_dioxide/group_multiplier - nitrogen -= delta_nitrogen/group_multiplier - toxins -= delta_toxins/group_multiplier - - var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins) - - if(trace_gases.len) - for(var/datum/gas/trace_gas in trace_gases) - var/delta = 0 - - delta = trace_gas.moles_archived/TRANSFER_FRACTION - - if(border_multiplier) - trace_gas.moles -= delta*border_multiplier/group_multiplier - else - trace_gas.moles -= delta/group_multiplier - - var/heat_cap_transferred = delta*trace_gas.specific_heat - heat_transferred += heat_cap_transferred*temperature_archived - heat_capacity_transferred += heat_cap_transferred - moved_moles += delta - update_values() - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/new_self_heat_capacity = old_self_heat_capacity - heat_capacity_transferred - if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY) - if(border_multiplier) - temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity - else - temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity - - temperature_mimic(model, model.thermal_conductivity, border_multiplier) - - if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) - var/delta_pressure = temperature_archived*(total_moles() + moved_moles) - model.temperature*(model.oxygen+model.carbon_dioxide+model.nitrogen+model.toxins) - return delta_pressure*R_IDEAL_GAS_EQUATION/volume - else - return 0 - -/datum/gas_mixture/proc/check_both_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - var/self_heat_capacity = heat_capacity_archived() - var/sharer_heat_capacity = sharer.heat_capacity_archived() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier) - else - return 1 - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - if((abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*sharer.temperature_archived)) - return -1 - - temperature += self_temperature_delta - sharer.temperature += sharer_temperature_delta - - return 1 - //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/check_me_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - - var/self_heat_capacity = heat_capacity_archived() - var/sharer_heat_capacity = sharer.heat_capacity_archived() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier) - else - return 1 - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - temperature += self_temperature_delta - sharer.temperature += sharer_temperature_delta - - return 1 - //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/check_me_then_temperature_turf_share(turf/simulated/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature) - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity_archived() - - if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - sharer_temperature_delta = heat/sharer.heat_capacity - else - return 1 - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - temperature += self_temperature_delta - sharer.temperature += sharer_temperature_delta - - return 1 - //Logic integrated from: temperature_turf_share(sharer, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/check_me_then_temperature_mimic(turf/model, conduction_coefficient) - var/delta_temperature = (temperature_archived - model.temperature) - var/self_temperature_delta = 0 - - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity_archived() - - if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity)) - - self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) - - if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ - && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) - return 0 - - temperature += self_temperature_delta - - return 1 - //Logic integrated from: temperature_mimic(model, conduction_coefficient) for efficiency - -/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature_archived) - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity_archived() - var/sharer_heat_capacity = sharer.heat_capacity_archived() - if(!group_multiplier) - message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!") - return - - if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) - - temperature -= heat/(self_heat_capacity*group_multiplier) - sharer.temperature += heat/(sharer_heat_capacity*sharer.group_multiplier) - -/datum/gas_mixture/proc/temperature_mimic(turf/model, conduction_coefficient, border_multiplier) - var/delta_temperature = (temperature - model.temperature) - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity()//_archived() - if(!group_multiplier) - message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!") - return - - if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity)) - - if(border_multiplier) - temperature -= heat*border_multiplier/(self_heat_capacity*group_multiplier) - else - temperature -= heat/(self_heat_capacity*group_multiplier) - -/datum/gas_mixture/proc/temperature_turf_share(turf/simulated/sharer, conduction_coefficient) - var/delta_temperature = (temperature_archived - sharer.temperature) - if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) - var/self_heat_capacity = heat_capacity() - - if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) - var/heat = conduction_coefficient*delta_temperature* \ - (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity)) - - temperature -= heat/(self_heat_capacity*group_multiplier) - sharer.temperature += heat/sharer.heat_capacity +//The above except for gases specifically. +#define FAIL_AIR_SIMILARITY_CHECK(ownvar, samplevar) (FAIL_SIMILARITY_CHECK((ownvar), (samplevar), MINIMUM_AIR_TO_SUSPEND, MINIMUM_AIR_RATIO_TO_SUSPEND)) /datum/gas_mixture/proc/compare(datum/gas_mixture/sample) //Purpose: Compares sample to self to see if within acceptable ranges that group processing may be enabled //Called by: Airgroups trying to rebuild //Inputs: Gas mix to compare //Outputs: 1 if can rebuild, 0 if not. - if(!sample) + if(!istype(sample)) return 0 - if((abs(oxygen-sample.oxygen) > MINIMUM_AIR_TO_SUSPEND) && \ - ((oxygen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen))) + if(FAIL_SIMILARITY_CHECK(temperature, sample.temperature, MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND, MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)) return 0 - if((abs(nitrogen-sample.nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \ - ((nitrogen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen) || (nitrogen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen))) - return 0 - if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \ - ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (carbon_dioxide > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) - return 0 - if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \ - ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins))) + if(FAIL_SIMILARITY_CHECK(pressure, sample.pressure, MINIMUM_PRESSURE_DELTA_TO_SUSPEND, MINIMUM_PRESSURE_RATIO_TO_SUSPEND)) + return 0 + + var/volume_ratio = volume / sample.volume //We want to compare the number of moles per unit volume, not total. + + if(FAIL_AIR_SIMILARITY_CHECK(oxygen, sample.oxygen * volume_ratio)) + return 0 + if(FAIL_AIR_SIMILARITY_CHECK(nitrogen, sample.nitrogen * volume_ratio)) + return 0 + if(FAIL_AIR_SIMILARITY_CHECK(carbon_dioxide, sample.carbon_dioxide * volume_ratio)) + return 0 + if(FAIL_AIR_SIMILARITY_CHECK(toxins, sample.toxins * volume_ratio)) return 0 - if(total_moles() > MINIMUM_AIR_TO_SUSPEND) - if((abs(temperature-sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \ - ((temperature < (1-MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature) || (temperature > (1+MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature))) -// to_chat(world, "temp fail [temperature] & [sample.temperature]") - return 0 var/check_moles if(sample.trace_gases.len) for(var/datum/gas/trace_gas in sample.trace_gases) var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases if(corresponding) - check_moles = corresponding.moles + check_moles = corresponding.moles / volume_ratio else check_moles = 0 - if((abs(trace_gas.moles - check_moles) > MINIMUM_AIR_TO_SUSPEND) && \ - ((check_moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles) || (check_moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles))) + if(FAIL_AIR_SIMILARITY_CHECK(check_moles, trace_gas.moles)) return 0 if(trace_gases.len) for(var/datum/gas/trace_gas in trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + var/datum/gas/corresponding = locate(trace_gas.type) in sample.trace_gases if(corresponding) - check_moles = corresponding.moles + check_moles = corresponding.moles * volume_ratio else check_moles = 0 - if((abs(trace_gas.moles - check_moles) > MINIMUM_AIR_TO_SUSPEND) && \ - ((trace_gas.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*check_moles) || (trace_gas.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*check_moles))) + if(FAIL_AIR_SIMILARITY_CHECK(trace_gas.moles, check_moles)) return 0 return 1 +#undef FAIL_AIR_SIMILARITY_CHECK +#undef FAIL_SIMILARITY_CHECK + /datum/gas_mixture/proc/add(datum/gas_mixture/right_side) if(!right_side) return 0 @@ -1133,10 +563,10 @@ What are the archived variables for? //Inputs: Gas mix to remove //Outputs: 1 - oxygen = max(oxygen - right_side.oxygen) - carbon_dioxide = max(carbon_dioxide - right_side.carbon_dioxide) - nitrogen = max(nitrogen - right_side.nitrogen) - toxins = max(toxins - right_side.toxins) + oxygen = max(0, oxygen - right_side.oxygen) + carbon_dioxide = max(0, carbon_dioxide - right_side.carbon_dioxide) + nitrogen = max(0, nitrogen - right_side.nitrogen) + toxins = max(0, toxins - right_side.toxins) if(trace_gases.len || right_side.trace_gases.len) for(var/datum/gas/trace_gas in right_side.trace_gases) @@ -1173,6 +603,34 @@ What are the archived variables for? update_values() return 1 + +//Mixes the given ratio of the two gas_mixtures. +//Ratio should always be between 0 and 1, of course. +//The exact values 0 and 1 won't break, but are useless, as they are respectively equivalent to doing nothing and using equalize(). +/datum/gas_mixture/proc/share_ratio(datum/gas_mixture/other, ratio) + var/total_volume = volume + other.volume + + var/datum/gas_mixture/holder = remove_ratio(ratio * other.volume / total_volume) + merge(other.remove_ratio(ratio * volume / total_volume)) + other.merge(holder) + + +//Each value in this list corresponds to the proportion of gas shared between the gas_mixtures in share_tiles() when connecting_tiles is equal to its index. +//(If connecting_tiles is greater than 6, it still uses the sixth one.) +var/static/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66) + +//Shares gas with another gas_mixture based on the number of connecting tiles and the above fixed lookup table. +/datum/gas_mixture/proc/share_tiles(datum/gas_mixture/other, connecting_tiles) + var/ratio = sharing_lookup_table[min(connecting_tiles, sharing_lookup_table.len)] //6 or more interconnecting tiles will max at 66% of air moved per tick. + share_ratio(other, ratio) + return compare(other) + +/datum/gas_mixture/proc/share_space(datum/gas_mixture/unsim_air, connecting_tiles) + unsim_air = new(unsim_air) //First, copy unsim_air so it doesn't get changed. + unsim_air.volume += volume + 3 * CELL_VOLUME //Then increase the copy's volume so larger rooms don't drain slowly as fuck. + //Why add the 3 * CELL_VOLUME, you ask? To mirror the old behavior. Why did the old behavior add three tiles to the total? I have no idea. + return share_tiles(unsim_air, connecting_tiles) + /datum/gas_mixture/proc/english_contents_list() var/all_contents = list() if(oxygen) diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 002cbd296da..ea689e7abb8 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -144,8 +144,6 @@ Class Procs: simulated_turf_count++ S.update_air_properties() - processing_parts[SSAIR_EDGES] = edges //A temporary hack to make edges actually work before the later PR to add edge sleeping, etc. - to_chat(world, {"Total Simulated Turfs: [simulated_turf_count] Total Zones: [zones.len] Total Edges: [edges.len] @@ -353,33 +351,31 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun Z.needs_update = 1 -//The following is for a system update coming in a later PR. +/datum/subsystem/air/proc/mark_edge_sleeping(connection_edge/E) + #ifdef ZASDBG + ASSERT(istype(E)) + #endif + if(E.sleeping) + return + processing_parts[SSAIR_EDGES] -= E + E.sleeping = 1 -///datum/subsystem/air/proc/mark_edge_sleeping(connection_edge/E) -// #ifdef ZASDBG -// ASSERT(istype(E)) -// #endif -// if(E.sleeping) -// return -// processing_parts[SSAIR_EDGES] -= E -// E.sleeping = 1 -// -// -///datum/subsystem/air/proc/mark_edge_active(connection_edge/E) -// #ifdef ZASDBG -// ASSERT(istype(E)) -// #endif -// if(!E.sleeping) -// return -// processing_parts[SSAIR_EDGES] |= E -// E.sleeping = 0 -// #ifdef ZASDBG -// if(istype(E, /connection_edge/zone/)) -// var/connection_edge/zone/ZE = E -// world << "ZASDBG: Active edge! Areas: [get_area(pick(ZE.A.contents))] / [get_area(pick(ZE.B.contents))]" -// else -// world << "ZASDBG: Active edge! Area: [get_area(pick(E.A.contents))]" -// #endif + +/datum/subsystem/air/proc/mark_edge_active(connection_edge/E) + #ifdef ZASDBG + ASSERT(istype(E)) + #endif + if(!E.sleeping) + return + processing_parts[SSAIR_EDGES] |= E + E.sleeping = 0 + #ifdef ZASDBG + if(istype(E, /connection_edge/zone/)) + var/connection_edge/zone/ZE = E + world << "ZASDBG: Active edge! Areas: [get_area(pick(ZE.A.contents))] / [get_area(pick(ZE.B.contents))]" + else + world << "ZASDBG: Active edge! Area: [get_area(pick(E.A.contents))]" + #endif /datum/subsystem/air/proc/equivalent_pressure(zone/A, zone/B) @@ -394,7 +390,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun return edge var/connection_edge/edge = new/connection_edge/zone(A,B) edges.Add(edge) -// edge.recheck() + edge.recheck() return edge else for(var/connection_edge/unsimulated/edge in A.edges) @@ -402,7 +398,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun return edge var/connection_edge/edge = new/connection_edge/unsimulated(A,B) edges.Add(edge) -// edge.recheck() + edge.recheck() return edge @@ -422,8 +418,8 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun /datum/subsystem/air/proc/remove_edge(connection_edge/E) edges.Remove(E) -// if(!E.sleeping) -// processing_parts[SSAIR_EDGES] -= E + if(!E.sleeping) + processing_parts[SSAIR_EDGES] -= E /datum/subsystem/air/proc/add_hotspot(var/obj/effect/fire/H) diff --git a/code/game/machinery/atmoalter/gas_mine.dm b/code/game/machinery/atmoalter/gas_mine.dm index 482e2f75725..6c98d632895 100644 --- a/code/game/machinery/atmoalter/gas_mine.dm +++ b/code/game/machinery/atmoalter/gas_mine.dm @@ -23,7 +23,8 @@ /obj/machinery/atmospherics/miner/New() ..() air_contents = new - air_contents.volume=1000 + air_contents.volume = 1000 + pumping.volume = 1000 //Same as above so copying works correctly air_contents.temperature = T20C AddAir() air_contents.update_values() diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm index 7df54a0bb8e..8631d675fca 100644 --- a/code/game/machinery/atmoalter/zvent.dm +++ b/code/game/machinery/atmoalter/zvent.dm @@ -26,4 +26,4 @@ // if (!my_air.compare(conn_air)) // myturf.reset_delay() // zturf_conn.reset_delay() - my_air.share(conn_air) + my_air.share_tiles(conn_air, 1) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 5736d2cdde2..fb1de6d8795 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -349,13 +349,13 @@ Subject's pulse: ??? BPM"}) var/unknown_concentration = 1 - (o2_concentration + n2_concentration + co2_concentration + plasma_concentration) if(n2_concentration > 0.01) - message += "
[human_standard && abs(n2_concentration - N2STANDARD) > 20 ? "" : ""] Nitrogen: [round(scanned.nitrogen, 0.1)] mol, [round(n2_concentration*100)]%" + message += "
[human_standard && abs(n2_concentration - N2STANDARD) > 20 ? "" : ""] Nitrogen: [round(scanned.nitrogen / scanned.volume * CELL_VOLUME, 0.1)] mol, [round(n2_concentration*100)]%" if(o2_concentration > 0.01) - message += "
[human_standard && abs(o2_concentration - O2STANDARD) > 2 ? "" : ""] Oxygen: [round(scanned.oxygen, 0.1)] mol, [round(o2_concentration*100)]%" + message += "
[human_standard && abs(o2_concentration - O2STANDARD) > 2 ? "" : ""] Oxygen: [round(scanned.oxygen / scanned.volume * CELL_VOLUME, 0.1)] mol, [round(o2_concentration*100)]%" if(co2_concentration > 0.01) - message += "
[human_standard ? "" : ""] CO2: [round(scanned.carbon_dioxide, 0.1)] mol, [round(co2_concentration*100)]%" + message += "
[human_standard ? "" : ""] CO2: [round(scanned.carbon_dioxide / scanned.volume * CELL_VOLUME, 0.1)] mol, [round(co2_concentration*100)]%" if(plasma_concentration > 0.01) - message += "
[human_standard ? "" : ""] Plasma: [round(scanned.toxins, 0.1)] mol, [round(plasma_concentration*100)]%" + message += "
[human_standard ? "" : ""] Plasma: [round(scanned.toxins / scanned.volume * CELL_VOLUME, 0.1)] mol, [round(plasma_concentration*100)]%" if(unknown_concentration > 0.01) message += "
Unknown: [round(unknown_concentration*100)]%" diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 3628959f3b0..2b4cc5dff7f 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -623,42 +623,43 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/pressure = environment.return_pressure() var/total_moles = environment.total_moles() + var/tiles = environment.return_volume() / CELL_VOLUME to_chat(src, "Results:") if(abs(pressure - ONE_ATMOSPHERE) < 10) - to_chat(src, "Pressure: [round(pressure,0.1)] kPa") + to_chat(src, "Pressure: [round(pressure, 0.1)] kPa") else - to_chat(src, "Pressure: [round(pressure,0.1)] kPa") + to_chat(src, "Pressure: [round(pressure, 0.1)] kPa") if(total_moles) - var/o2_concentration = environment.oxygen/total_moles - var/n2_concentration = environment.nitrogen/total_moles - var/co2_concentration = environment.carbon_dioxide/total_moles - var/plasma_concentration = environment.toxins/total_moles + var/o2_concentration = environment.oxygen / total_moles + var/n2_concentration = environment.nitrogen / total_moles + var/co2_concentration = environment.carbon_dioxide / total_moles + var/plasma_concentration = environment.toxins / total_moles - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) + var/unknown_concentration = 1 - (o2_concentration + n2_concentration + co2_concentration + plasma_concentration) if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(src, "Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)") + to_chat(src, "Nitrogen: [round(n2_concentration * 100)]% ([round(environment.nitrogen / tiles, 0.01)] moles)") else - to_chat(src, "Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)") + to_chat(src, "Nitrogen: [round(n2_concentration * 100)]% ([round(environment.nitrogen / tiles, 0.01)] moles)") if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(src, "Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)") + to_chat(src, "Oxygen: [round(o2_concentration * 100)]% ([round(environment.oxygen / tiles, 0.01)] moles)") else - to_chat(src, "Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)") + to_chat(src, "Oxygen: [round(o2_concentration * 100)]% ([round(environment.oxygen / tiles, 0.01)] moles)") if(co2_concentration > 0.01) - to_chat(src, "CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)") + to_chat(src, "CO2: [round(co2_concentration * 100)]% ([round(environment.carbon_dioxide / tiles, 0.01)] moles)") else - to_chat(src, "CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)") + to_chat(src, "CO2: [round(co2_concentration * 100)]% ([round(environment.carbon_dioxide / tiles, 0.01)] moles)") if(plasma_concentration > 0.01) - to_chat(src, "Plasma: [round(plasma_concentration*100)]% ([round(environment.toxins,0.01)] moles)") + to_chat(src, "Plasma: [round(plasma_concentration * 100)]% ([round(environment.toxins / tiles, 0.01)] moles)") if(unknown_concentration > 0.01) - to_chat(src, "Unknown: [round(unknown_concentration*100)]% ([round(unknown_concentration*total_moles,0.01)] moles)") + to_chat(src, "Unknown: [round(unknown_concentration * 100)]% ([round(unknown_concentration * total_moles / tiles, 0.01)] moles)") - to_chat(src, "Temperature: [round(environment.temperature-T0C,0.1)]°C") - to_chat(src, "Heat Capacity: [round(environment.heat_capacity(),0.1)]") + to_chat(src, "Temperature: [round(environment.temperature - T0C, 0.1)]°C") + to_chat(src, "Heat Capacity: [round(environment.heat_capacity(), 0.1)]") /mob/dead/observer/verb/toggle_darkness()