diff --git a/code/ZAS/ConnectionGroup.dm b/code/ZAS/ConnectionGroup.dm
index 94731228a6..f377efe74f 100644
--- a/code/ZAS/ConnectionGroup.dm
+++ b/code/ZAS/ConnectionGroup.dm
@@ -62,6 +62,7 @@ Class Procs:
/connection_edge/var/list/connecting_turfs = list()
/connection_edge/var/direct = 0
+/connection_edge/var/sleeping = 1
/connection_edge/var/coefficient = 0
@@ -88,6 +89,8 @@ Class Procs:
/connection_edge/proc/tick()
+/connection_edge/proc/recheck()
+
/connection_edge/proc/flow(list/movable, differential, repelled)
for(var/i = 1; i <= movable.len; i++)
var/atom/movable/M = movable[i]
@@ -147,35 +150,38 @@ Class Procs:
if(A.invalid || B.invalid)
erase()
return
- //world << "[id]: Tick [air_master.current_cycle]: \..."
- if(direct)
- if(air_master.equivalent_pressure(A, B))
- //world << "merged."
- erase()
- air_master.merge(A, B)
- //world << "zones merged."
- return
- //air_master.equalize(A, B)
- A.air.share_ratio(B.air, coefficient)
- air_master.mark_zone_update(A)
- air_master.mark_zone_update(B)
- //world << "equalized."
+ var/equiv = A.air.share_ratio(B.air, coefficient)
var/differential = A.air.return_pressure() - B.air.return_pressure()
- if(abs(differential) < vsc.airflow_lightest_pressure) return
+ if(abs(differential) >= vsc.airflow_lightest_pressure)
+ var/list/attracted
+ var/list/repelled
+ if(differential > 0)
+ attracted = A.movables()
+ repelled = B.movables()
+ else
+ attracted = B.movables()
+ repelled = A.movables()
- var/list/attracted
- var/list/repelled
- if(differential > 0)
- attracted = A.movables()
- repelled = B.movables()
- else
- attracted = B.movables()
- repelled = A.movables()
+ flow(attracted, abs(differential), 0)
+ flow(repelled, abs(differential), 1)
- flow(attracted, abs(differential), 0)
- flow(repelled, abs(differential), 1)
+ if(equiv)
+ if(direct)
+ erase()
+ air_master.merge(A, B)
+ return
+ else
+ A.air.equalize(B.air)
+ air_master.mark_edge_sleeping(src)
+
+ air_master.mark_zone_update(A)
+ air_master.mark_zone_update(B)
+
+/connection_edge/zone/recheck()
+ if(!A.air.compare(B.air))
+ air_master.mark_edge_active(src)
//Helper proc to get connections for a zone.
/connection_edge/zone/proc/get_connected_zone(zone/from)
@@ -214,20 +220,27 @@ Class Procs:
if(A.invalid)
erase()
return
- //world << "[id]: Tick [air_master.current_cycle]: To [B]!"
- //A.air.mimic(B, coefficient)
- A.air.share_space(air, dbg_out)
- air_master.mark_zone_update(A)
+
+ var/equiv = A.air.share_space(air)
var/differential = A.air.return_pressure() - air.return_pressure()
- if(abs(differential) < vsc.airflow_lightest_pressure) return
+ if(abs(differential) >= vsc.airflow_lightest_pressure)
+ var/list/attracted = A.movables()
+ flow(attracted, abs(differential), differential < 0)
- var/list/attracted = A.movables()
- flow(attracted, abs(differential), differential < 0)
+ if(equiv)
+ A.air.copy_from(air)
+ air_master.mark_edge_sleeping(src)
+
+ air_master.mark_zone_update(A)
+
+/connection_edge/unsimulated/recheck()
+ if(!A.air.compare(air))
+ air_master.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/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() * A.group_multiplier, B.temperature * B.heat_capacity() * B.group_multiplier))
if(maximum_energy_delta > abs(energy_delta))
if(energy_delta < 0)
@@ -235,4 +248,4 @@ proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
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)
\ No newline at end of file
+ B.temperature += energy_delta / (B.heat_capacity() * B.group_multiplier)
diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm
index 756887cc09..4ba30f47b3 100644
--- a/code/ZAS/Controller.dm
+++ b/code/ZAS/Controller.dm
@@ -75,6 +75,7 @@ Class Procs:
/datum/controller/air_system/var/list/zones_to_update = list()
/datum/controller/air_system/var/list/active_fire_zones = list()
/datum/controller/air_system/var/list/active_hotspots = list()
+/datum/controller/air_system/var/list/active_edges = list()
/datum/controller/air_system/var/active_zones = 0
@@ -97,7 +98,7 @@ Class Procs:
set background = 1
#endif
- world << "\red \b Processing Geometry..."
+ world << "Processing Geometry..."
sleep(-1)
var/start_time = world.timeofday
@@ -108,10 +109,15 @@ Class Procs:
simulated_turf_count++
S.update_air_properties()
- world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.
+ world << {"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds.
+
Total Simulated Turfs: [simulated_turf_count]
Total Zones: [zones.len]
-Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]"}
+Total Edges: [edges.len]
+Total Active Edges: [active_edges.len ? "[active_edges.len]" : "None"]
+Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]
+"}
+
// spawn Start()
@@ -169,7 +175,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
if(.)
tick_progress = "processing edges"
- for(var/connection_edge/edge in edges)
+ for(var/connection_edge/edge in active_edges)
edge.tick()
//Process fire zones.
@@ -299,6 +305,22 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
zones_to_update.Add(Z)
Z.needs_update = 1
+/datum/controller/air_system/proc/mark_edge_sleeping(connection_edge/E)
+ #ifdef ZASDBG
+ ASSERT(istype(E)
+ #endif
+ if(E.sleeping) return
+ active_edges.Remove(E)
+ E.sleeping = 1
+
+/datum/controller/air_system/proc/mark_edge_active(connection_edge/E)
+ #ifdef ZASDBG
+ ASSERT(istype(E)
+ #endif
+ if(!E.sleeping) return
+ active_edges.Add(E)
+ E.sleeping = 0
+
/datum/controller/air_system/proc/equivalent_pressure(zone/A, zone/B)
return A.air.compare(B.air)
@@ -309,12 +331,14 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
if(edge.contains_zone(B)) return edge
var/connection_edge/edge = new/connection_edge/zone(A,B)
edges.Add(edge)
+ edge.recheck()
return edge
else
for(var/connection_edge/unsimulated/edge in A.edges)
if(has_same_air(edge.B,B)) return edge
var/connection_edge/edge = new/connection_edge/unsimulated(A,B)
edges.Add(edge)
+ edge.recheck()
return edge
/datum/controller/air_system/proc/has_same_air(turf/A, turf/B)
@@ -325,5 +349,6 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
if(A.temperature != B.temperature) return 0
return 1
-/datum/controller/air_system/proc/remove_edge(connection/c)
- edges.Remove(c)
\ No newline at end of file
+/datum/controller/air_system/proc/remove_edge(connection_edge/E)
+ edges.Remove(E)
+ if(!E.sleeping) active_edges.Remove(E)
\ No newline at end of file
diff --git a/code/ZAS/Zone.dm b/code/ZAS/Zone.dm
index 1f2cbee8d3..7c9c5f32ed 100644
--- a/code/ZAS/Zone.dm
+++ b/code/ZAS/Zone.dm
@@ -138,6 +138,10 @@ Class Procs:
graphic_add.len = 0
graphic_remove.len = 0
+ for(var/connection_edge/E in edges)
+ if(E.sleeping)
+ E.recheck()
+
/zone/proc/dbg_data(mob/M)
M << name
for(var/g in air.gas)
diff --git a/code/ZAS/_gas_mixture_xgm.dm b/code/ZAS/_gas_mixture_xgm.dm
index 01f95ee9a5..3390cb9fd3 100644
--- a/code/ZAS/_gas_mixture_xgm.dm
+++ b/code/ZAS/_gas_mixture_xgm.dm
@@ -17,7 +17,8 @@
//List of active tile overlays for this gas_mixture. Updated by check_tile_graphic()
var/list/graphic = list()
-//Takes a gas string, and the amount of moles to adjust by. Calls update_values() if update isn't 0.
+
+//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)
return
@@ -30,6 +31,7 @@
if(update)
update_values()
+
//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 = 1)
if(moles == 0)
@@ -50,7 +52,8 @@
if(update)
update_values()
-//Variadic version of adjust_gas(). Takes any number of gas and mole pairs, and applies them.
+
+//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))
@@ -59,7 +62,8 @@
update_values()
-//Variadic version of adjust_gas_temp(). Takes any number of gas, mole, and temperature tuples, and applies them.
+
+//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))
@@ -68,8 +72,10 @@
update_values()
+
//Merges all the gas from another mixture into this one. Respects group_multipliers and adjusts temperature correctly.
-/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
+//Does not modify giver in any way.
+/datum/gas_mixture/proc/merge(const/datum/gas_mixture/giver)
if(!giver)
return
@@ -89,6 +95,25 @@
update_values()
+
+/datum/gas_mixture/proc/equalize(datum/gas_mixture/sharer)
+ for(var/g in sharer.gas)
+ var/comb = gas[g] + sharer.gas[g]
+ comb /= volume + sharer.volume
+ gas[g] = comb * volume
+ sharer.gas[g] = comb * sharer.volume
+
+ var/our_heatcap = heat_capacity()
+ var/share_heatcap = sharer.heat_capacity()
+
+ temperature = 0
+ if(our_heatcap + share_heatcap)
+ temperature = ((temperature * our_heatcap) + (sharer.temperature * share_heatcap)) / (our_heatcap + share_heatcap)
+ sharer.temperature = temperature
+
+ return 1
+
+
//Returns the heat capacity of the gas mix based on the specific heat of the gases.
/datum/gas_mixture/proc/heat_capacity()
. = 0
@@ -96,11 +121,12 @@
. += gas_data.specific_heat[g] * gas[g]
. *= group_multiplier
+
//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
-
+
var/heat_capacity = heat_capacity()
if (thermal_energy < 0)
if (temperature < TCMB)
@@ -114,43 +140,47 @@
/datum/gas_mixture/proc/get_thermal_energy_change(var/new_temperature)
return heat_capacity()*(max(new_temperature, 0) - temperature)
+
//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
+ 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
-
+
//group_multiplier gets divided out in volume/gas[gasid] - also, V/(m*T) = R/(partial pressure)
var/molar_mass = gas_data.molar_mass[gasid]
var/specific_heat = gas_data.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 )
+
//Updates the total_moles count and trims any empty gases.
/datum/gas_mixture/proc/update_values()
total_moles = 0
@@ -160,12 +190,14 @@
else
total_moles += gas[g]
+
//Returns the pressure of the gas mix. Only accurate if there have been no gas modifications since update_values() has been called.
/datum/gas_mixture/proc/return_pressure()
if(volume)
return total_moles * R_IDEAL_GAS_EQUATION * temperature / volume
return 0
+
//Removes moles from the gas mixture and returns a gas_mixture containing the removed air.
/datum/gas_mixture/proc/remove(amount)
amount = min(amount, total_moles * group_multiplier) //Can not take more air than the gas mixture has!
@@ -184,6 +216,7 @@
return removed
+
//Removes a ratio of gas from the mixture and returns a gas_mixture containing the removed air.
/datum/gas_mixture/proc/remove_ratio(ratio, out_group_multiplier = 1)
if(ratio <= 0)
@@ -205,6 +238,7 @@
return removed
+
//Removes moles from the gas mixture, limited by a given flag. Returns a gax_mixture containing the removed air.
/datum/gas_mixture/proc/remove_by_flag(flag, amount)
if(!flag || amount <= 0)
@@ -228,8 +262,9 @@
return removed
+
//Copies gas and temperature from another gas_mixture.
-/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample)
+/datum/gas_mixture/proc/copy_from(const/datum/gas_mixture/sample)
gas = sample.gas.Copy()
temperature = sample.temperature
@@ -237,8 +272,9 @@
return 1
+
//Checks if we are within acceptable range of another gas_mixture to suspend processing or merge.
-/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
+/datum/gas_mixture/proc/compare(const/datum/gas_mixture/sample)
if(!sample) return 0
var/list/marked = list()
@@ -264,9 +300,11 @@
return 1
+
/datum/gas_mixture/proc/react(atom/dump_location)
zburn(null)
+
//Rechecks the gas_mixture and adjusts the graphic list if needed.
//Two lists can be passed by reference if you need know specifically which graphics were added and removed.
/datum/gas_mixture/proc/check_tile_graphic(list/graphic_add = null, list/graphic_remove = null)
@@ -293,6 +331,7 @@
graphic -= graphic_remove
. = 1
+
//Simpler version of merge(), adjusts gas amounts directly and doesn't account for temperature or group_multiplier.
/datum/gas_mixture/proc/add(datum/gas_mixture/right_side)
for(var/g in right_side.gas)
@@ -301,6 +340,7 @@
update_values()
return 1
+
//Simpler version of remove(), adjusts gas amounts directly and doesn't account for group_multiplier.
/datum/gas_mixture/proc/subtract(datum/gas_mixture/right_side)
for(var/g in right_side.gas)
@@ -309,6 +349,7 @@
update_values()
return 1
+
//Multiply all gas amounts by a factor.
/datum/gas_mixture/proc/multiply(factor)
for(var/g in gas)
@@ -317,6 +358,7 @@
update_values()
return 1
+
//Divide all gas amounts by a factor.
/datum/gas_mixture/proc/divide(factor)
for(var/g in gas)
@@ -325,6 +367,7 @@
update_values()
return 1
+
//Shares gas with another gas_mixture based on the amount of connecting tiles and a fixed lookup table.
/datum/gas_mixture/proc/share_ratio(datum/gas_mixture/other, connecting_tiles, share_size = null, one_way = 0)
var/static/list/sharing_lookup_table = list(0.30, 0.40, 0.48, 0.54, 0.60, 0.66)
@@ -369,19 +412,13 @@
update_values()
other.update_values()
- if(compare(other)) return 1
- else return 0
+ return compare(other)
+
//A wrapper around share_ratio for spacing gas at the same rate as if it were going into a large airless room.
/datum/gas_mixture/proc/share_space(datum/gas_mixture/unsim_air)
- if(!unsim_air)
- return 0
+ return share_ratio(unsim_air, unsim_air.group_multiplier, max(1, max(group_multiplier + 3, 1) + unsim_air.group_multiplier), one_way = 1)
- var/old_pressure = return_pressure()
-
- share_ratio(unsim_air, unsim_air.group_multiplier, max(1, max(group_multiplier + 3, 1) + unsim_air.group_multiplier), one_way = 1)
-
- return abs(old_pressure - return_pressure())
//Equalizes a list of gas mixtures. Used for pipe networks.
/proc/equalize_gases(datum/gas_mixture/list/gases)
diff --git a/code/setup.dm b/code/setup.dm
index ab3b065ae0..5ebde2b9a3 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -9,7 +9,7 @@
#define IDEAL_GAS_ENTROPY_CONSTANT 1164 //(mol^3 * s^3) / (kg^3 * L). Equal to (4*pi/(avrogadro's number * planck's constant)^2)^(3/2) / (avrogadro's number * 1000 Liters per m^3).
//radiation constants
-#define STEFAN_BOLTZMANN_CONSTANT 0.0000000567 //W/(m^2*K^4)
+#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 110 //kPa at 20 C