Files
vgstation13/code/ZAS/_gas_mixture.dm
Exxion fa5183d5da 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
2017-11-01 08:16:32 +01:00

657 lines
22 KiB
Plaintext

#define SPECIFIC_HEAT_TOXIN 200
#define SPECIFIC_HEAT_AIR 20
#define SPECIFIC_HEAT_CDO 30
#define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) \
max(0, carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN)
#define MINIMUM_HEAT_CAPACITY 0.0003
#define TRANSFER_FRACTION 5 //What fraction (1/#) of the air difference to try and transfer
// /vg/ SHIT
#define TEMPERATURE_ICE_FORMATION 273.15 // 273 kelvin is the freezing point of water.
#define MIN_PRESSURE_ICE_FORMATION 10 // 10kPa should be okay
#define GRAPHICS_PLASMA 1
#define GRAPHICS_N2O 2
#define GRAPHICS_REAGENTS 4 //Not used. Yet.
#define GRAPHICS_COLD 8
// END /vg/SHIT
/hook/startup/proc/createGasOverlays()
plmaster = new /obj/effect/overlay()
plmaster.icon = 'icons/effects/tile_effects.dmi'
plmaster.icon_state = "plasma"
plmaster.layer = FLY_LAYER
plmaster.plane = EFFECTS_PLANE
plmaster.mouse_opacity = 0
slmaster = new /obj/effect/overlay()
slmaster.icon = 'icons/effects/tile_effects.dmi'
slmaster.icon_state = "sleeping_agent"
slmaster.layer = FLY_LAYER
slmaster.plane = EFFECTS_PLANE
slmaster.mouse_opacity = 0
return 1
/datum/gas/sleeping_agent/specific_heat = 40 //These are used for the "Trace Gases" stuff, but is buggy.
/datum/gas/oxygen_agent_b/specific_heat = 300
/datum/gas/volatile_fuel/specific_heat = 30
/datum/gas
var/moles = 0
var/specific_heat = 0
/datum/gas_mixture
var/oxygen = 0 //Holds the "moles" of each of the four gases.
var/carbon_dioxide = 0
var/nitrogen = 0
var/toxins = 0
var/total_moles = 0 //Updated when a reaction occurs.
var/volume = CELL_VOLUME
var/temperature = 0 //in Kelvin, use calculate_temperature() to modify
var/graphics=0
var/pressure=0
var/list/datum/gas/trace_gases = list() //Seemed to be a good idea that was abandoned
var/tmp/fuel_burnt = 0
/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: Fucking everything!
//Inputs: The values of the gases to adjust
//Outputs: null
oxygen = max(0, oxygen + o2)
carbon_dioxide = max(0, carbon_dioxide + co2)
nitrogen = max(0, nitrogen + n2)
toxins = max(0, toxins + tx)
//handle trace gasses
for(var/datum/gas/G in traces)
var/datum/gas/T = locate(G.type) in trace_gases
if(T)
T.moles = max(G.moles + T.moles, 0)
else if(G.moles > 0)
trace_gases |= G
update_values()
return
//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")
oxygen += moles
if("plasma")
toxins += moles
if("carbon_dioxide")
carbon_dioxide += moles
if("nitrogen")
nitrogen += moles
else
CRASH("Invalid gasid!")
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 = 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
/datum/gas_mixture/proc/return_volume()
return max(0, volume)
/datum/gas_mixture/proc/thermal_energy()
return temperature*heat_capacity()
///////////////////////////////
//PV=nRT - related procedures//
///////////////////////////////
/datum/gas_mixture/proc/heat_capacity()
//Purpose: Returning the heat capacity of the gas mix
//Called by: UNKNOWN
//Inputs: None
//Outputs: Heat capacity
var/heat_capacity = HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins)
if(trace_gases && trace_gases.len) //sanity check because somehow the tracegases gets nulled?
for(var/datum/gas/trace_gas in trace_gases)
heat_capacity += trace_gas.moles*trace_gas.specific_heat
return max(MINIMUM_HEAT_CAPACITY,heat_capacity)
//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)
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
//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)
//The below wasn't even implemented yet in the big XGM PR. Just saving it here for later.
////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 )
/datum/gas_mixture/proc/update_values()
//Purpose: Calculating and storing values which were normally called CONSTANTLY
//Called by: Anything that changes values within a gas mix.
//Inputs: None
//Outputs: None
total_moles = oxygen + carbon_dioxide + nitrogen + toxins
if(trace_gases.len)
for(var/datum/gas/trace_gas in trace_gases)
total_moles += trace_gas.moles
if(volume>0)
pressure = total_moles()*R_IDEAL_GAS_EQUATION*temperature/volume
else
pressure = 0
return
/datum/gas_mixture/proc/total_moles()
return total_moles
/datum/gas_mixture/proc/return_pressure()
//Purpose: Calculating Current Pressure
//Called by:
//Inputs: None
//Outputs: Gas pressure.
return pressure
/datum/gas_mixture/proc/remove(amount)
//Purpose: Removes a certain number of moles from the air.
//Called by: ?
//Inputs: How many moles to remove.
//Outputs: Removed air.
var/sum = total_moles()
amount = min(amount,sum) //Can not take more air than tile has!
if(amount <= 0)
return null
var/datum/gas_mixture/removed = new
removed.oxygen = QUANTIZE((oxygen/sum)*amount)
removed.nitrogen = QUANTIZE((nitrogen/sum)*amount)
removed.carbon_dioxide = QUANTIZE((carbon_dioxide/sum)*amount)
removed.toxins = QUANTIZE((toxins/sum)*amount)
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)
var/datum/gas/corresponding = new trace_gas.type()
removed.trace_gases += corresponding
corresponding.moles = (trace_gas.moles/sum)*amount
trace_gas.moles -= corresponding.moles
/*
if(aerosols.total_volume > 1)
removed.aerosols.trans_to_atmos(src,(aerosols.total_volume/sum)*amount)
*/
removed.temperature = temperature
update_values()
removed.update_values()
return removed
/datum/gas_mixture/proc/remove_ratio(ratio)
//Purpose: Removes a certain ratio of the air.
//Called by: ?
//Inputs: Percentage to remove.
//Outputs: Removed air.
if(ratio <= 0)
return null
ratio = min(ratio, 1)
var/datum/gas_mixture/removed = new
removed.oxygen = QUANTIZE(oxygen*ratio)
removed.nitrogen = QUANTIZE(nitrogen*ratio)
removed.carbon_dioxide = QUANTIZE(carbon_dioxide*ratio)
removed.toxins = QUANTIZE(toxins*ratio)
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)
var/datum/gas/corresponding = new trace_gas.type()
removed.trace_gases += corresponding
corresponding.moles = trace_gas.moles*ratio
trace_gas.moles -= corresponding.moles
removed.temperature = temperature
update_values()
removed.update_values()
return removed
//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
////////////////////////////////////////////
//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: Copies the per unit volume properties of sample.
//Called by: airgroups splitting, ?
//Inputs: Gas to copy
//Outputs: 1
oxygen = sample.oxygen
carbon_dioxide = sample.carbon_dioxide
nitrogen = sample.nitrogen
toxins = sample.toxins
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()
trace_gases += corresponding
corresponding.moles = trace_gas.moles
multiply(volume / sample.volume)
return 1
//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))))
//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(!istype(sample))
return 0
if(FAIL_SIMILARITY_CHECK(temperature, sample.temperature, MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND, MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND))
return 0
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
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 / volume_ratio
else
check_moles = 0
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 sample.trace_gases
if(corresponding)
check_moles = corresponding.moles * volume_ratio
else
check_moles = 0
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
oxygen += right_side.oxygen
carbon_dioxide += right_side.carbon_dioxide
nitrogen += right_side.nitrogen
toxins += right_side.toxins
if(trace_gases.len || right_side.trace_gases.len)
for(var/datum/gas/trace_gas in right_side.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
/datum/gas_mixture/proc/subtract(datum/gas_mixture/right_side)
//Purpose: Subtracts right_side from air_mixture. Used to help turfs mingle
//Called by: Pipelines ending in a break (or something)
//Inputs: Gas mix to remove
//Outputs: 1
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)
var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases
if(corresponding)
corresponding.moles = max(0, corresponding.moles - trace_gas.moles)
update_values()
return 1
/datum/gas_mixture/proc/multiply(factor)
oxygen *= factor
carbon_dioxide *= factor
nitrogen *= factor
toxins *= factor
if(trace_gases && trace_gases.len)
for(var/datum/gas/trace_gas in trace_gases)
trace_gas.moles *= factor
update_values()
return 1
/datum/gas_mixture/proc/divide(factor)
oxygen /= factor
carbon_dioxide /= factor
nitrogen /= factor
toxins /= factor
if(trace_gases && trace_gases.len)
for(var/datum/gas/trace_gas in trace_gases)
trace_gas.moles /= factor
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)
all_contents += "Oxygen"
if(nitrogen)
all_contents += "Nitrogen"
if(carbon_dioxide)
all_contents += "CO<sub>2</sub>"
if(toxins)
all_contents += "Plasma"
if(locate(/datum/gas/sleeping_agent) in trace_gases)
all_contents += "N<sub>2</sub>O"
return english_list(all_contents)
/datum/gas_mixture/proc/loggable_contents()
var/naughty_stuff = list()
if(toxins)
naughty_stuff += "<b><font color='red'>Plasma</font></b>"
if(carbon_dioxide)
naughty_stuff += "<b><font color='red'>CO<sub>2</sub></font></b>"
if(locate(/datum/gas/sleeping_agent) in trace_gases)
naughty_stuff += "<b><font color='red'>N<sub>2</sub>O</font>"
return english_list(naughty_stuff, nothing_text = "")