Files
MigratingcocofruitandGitHub 07bd88a2ff Fixes N2O not decomposing above 100000 Kelvin (#26817)
* Replaces the N2O decomposition function with one that is slightly above 0 at 1400 Kelvin and approaches 1 as temp increases.

* adds a min check to the calculation of the amount to decompose
2024-10-02 15:20:40 +00:00

818 lines
35 KiB
Plaintext

/*
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
#define SPECIFIC_HEAT_N2O 40
#define SPECIFIC_HEAT_AGENT_B 300
#define HEAT_CAPACITY_CALCULATION(oxygen, carbon_dioxide, nitrogen, toxins, sleeping_agent, agent_b, innate_heat_capacity) \
(carbon_dioxide * SPECIFIC_HEAT_CDO + (oxygen + nitrogen) * SPECIFIC_HEAT_AIR + toxins * SPECIFIC_HEAT_TOXIN + sleeping_agent * SPECIFIC_HEAT_N2O + agent_b * SPECIFIC_HEAT_AGENT_B + innate_heat_capacity)
#define MINIMUM_HEAT_CAPACITY 0.0003
#define MINIMUM_MOLE_COUNT 0.01
#define QUANTIZE(variable) (round(variable, 0.0001))
/datum/gas_mixture
/// The volume this gas mixture fills.
var/volume = CELL_VOLUME
/// Heat capacity intrinsic to the container of this gas mixture.
var/innate_heat_capacity = 0
/// How much fuel this gas mixture burnt last reaction.
var/fuel_burnt = 0
// Private fields. Use the matching procs to get and set them.
// e.g. GM.oxygen(), GM.set_oxygen()
var/private_oxygen = 0
var/private_carbon_dioxide = 0
var/private_nitrogen = 0
var/private_toxins = 0
var/private_sleeping_agent = 0
var/private_agent_b = 0
var/private_temperature = 0 //in Kelvin
// Archived versions of the private fields.
// Only gas_mixture should use these.
var/private_oxygen_archived = 0
var/private_carbon_dioxide_archived = 0
var/private_nitrogen_archived = 0
var/private_toxins_archived = 0
var/private_sleeping_agent_archived = 0
var/private_agent_b_archived = 0
var/private_temperature_archived = 0
/// Is this mixture currently synchronized with MILLA? Always true for non-bound mixtures.
var/synchronized = TRUE
/// Tracks the callbacks from synchronize() that haven't run yet.
var/list/waiting_for_sync = list()
/datum/gas_mixture/Destroy()
waiting_for_sync.Cut()
return ..()
/// Marks this gas mixture as changed from MILLA. Does nothing on non-bound mixtures.
/datum/gas_mixture/proc/set_dirty()
return
/datum/gas_mixture/proc/oxygen()
return private_oxygen
/datum/gas_mixture/proc/set_oxygen(value)
private_oxygen = value
/datum/gas_mixture/proc/carbon_dioxide()
return private_carbon_dioxide
/datum/gas_mixture/proc/set_carbon_dioxide(value)
private_carbon_dioxide = value
/datum/gas_mixture/proc/nitrogen()
return private_nitrogen
/datum/gas_mixture/proc/set_nitrogen(value)
private_nitrogen = value
/datum/gas_mixture/proc/toxins()
return private_toxins
/datum/gas_mixture/proc/set_toxins(value)
private_toxins = value
/datum/gas_mixture/proc/sleeping_agent()
return private_sleeping_agent
/datum/gas_mixture/proc/set_sleeping_agent(value)
private_sleeping_agent = value
/datum/gas_mixture/proc/agent_b()
return private_agent_b
/datum/gas_mixture/proc/set_agent_b(value)
private_agent_b = value
/datum/gas_mixture/proc/temperature()
return private_temperature
/datum/gas_mixture/proc/set_temperature(value)
private_temperature = value
///joules per kelvin
/datum/gas_mixture/proc/heat_capacity()
return HEAT_CAPACITY_CALCULATION(private_oxygen, private_carbon_dioxide, private_nitrogen, private_toxins, private_sleeping_agent, private_agent_b, innate_heat_capacity)
/datum/gas_mixture/proc/heat_capacity_archived()
return HEAT_CAPACITY_CALCULATION(private_oxygen_archived, private_carbon_dioxide_archived, private_nitrogen_archived, private_toxins_archived, private_sleeping_agent_archived, private_agent_b_archived, innate_heat_capacity)
/// Calculate moles
/datum/gas_mixture/proc/total_moles()
var/moles = private_oxygen + private_carbon_dioxide + private_nitrogen + private_toxins + private_sleeping_agent + private_agent_b
return moles
/datum/gas_mixture/proc/total_trace_moles()
var/moles = private_agent_b
return moles
/// Calculate pressure in kilopascals
/datum/gas_mixture/proc/return_pressure()
if(volume > 0)
return total_moles() * R_IDEAL_GAS_EQUATION * private_temperature / volume
return 0
/// Calculate volume in liters
/datum/gas_mixture/proc/return_volume()
return max(0, volume)
/// Calculate thermal energy in joules
/datum/gas_mixture/proc/thermal_energy()
return private_temperature * heat_capacity()
///Update archived versions of variables. Returns: TRUE in all cases
/datum/gas_mixture/proc/archive()
private_oxygen_archived = private_oxygen
private_carbon_dioxide_archived = private_carbon_dioxide
private_nitrogen_archived = private_nitrogen
private_toxins_archived = private_toxins
private_sleeping_agent_archived = private_sleeping_agent
private_agent_b_archived = private_agent_b
private_temperature_archived = private_temperature
return TRUE
///Merges all air from giver into self. Deletes giver. Returns: TRUE if we are mutable, FALSE otherwise
/datum/gas_mixture/proc/merge(datum/gas_mixture/giver)
if(!giver)
return FALSE
if(abs(private_temperature - giver.private_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity()
var/giver_heat_capacity = giver.heat_capacity()
var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity
if(combined_heat_capacity != 0)
private_temperature = (giver.private_temperature * giver_heat_capacity + private_temperature * self_heat_capacity) / combined_heat_capacity
private_oxygen += giver.private_oxygen
private_carbon_dioxide += giver.private_carbon_dioxide
private_nitrogen += giver.private_nitrogen
private_toxins += giver.private_toxins
private_sleeping_agent += giver.private_sleeping_agent
private_agent_b += giver.private_agent_b
set_dirty()
return TRUE
/// Only removes the gas if we have more than the amount
/datum/gas_mixture/proc/boolean_remove(amount)
if(amount > total_moles())
return FALSE
return remove(amount)
///Proportionally removes amount of gas from the gas_mixture.
///Returns: gas_mixture with the gases removed
/datum/gas_mixture/proc/remove(amount)
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.private_oxygen = QUANTIZE((private_oxygen / sum) * amount)
removed.private_nitrogen = QUANTIZE((private_nitrogen/ sum) * amount)
removed.private_carbon_dioxide = QUANTIZE((private_carbon_dioxide / sum) * amount)
removed.private_toxins = QUANTIZE((private_toxins / sum) * amount)
removed.private_sleeping_agent = QUANTIZE((private_sleeping_agent / sum) * amount)
removed.private_agent_b = QUANTIZE((private_agent_b / sum) * amount)
private_oxygen = max(private_oxygen - removed.private_oxygen, 0)
private_nitrogen = max(private_nitrogen - removed.private_nitrogen, 0)
private_carbon_dioxide = max(private_carbon_dioxide - removed.private_carbon_dioxide, 0)
private_toxins = max(private_toxins - removed.private_toxins, 0)
private_sleeping_agent = max(private_sleeping_agent - removed.private_sleeping_agent, 0)
private_agent_b = max(private_agent_b - removed.private_agent_b, 0)
removed.private_temperature = private_temperature
set_dirty()
return removed
///Proportionally removes amount of gas from the gas_mixture.
///Returns: gas_mixture with the gases removed
/datum/gas_mixture/proc/remove_ratio(ratio)
if(ratio <= 0)
return null
ratio = min(ratio, 1)
var/datum/gas_mixture/removed = new
removed.private_oxygen = QUANTIZE(private_oxygen * ratio)
removed.private_nitrogen = QUANTIZE(private_nitrogen * ratio)
removed.private_carbon_dioxide = QUANTIZE(private_carbon_dioxide * ratio)
removed.private_toxins = QUANTIZE(private_toxins * ratio)
removed.private_sleeping_agent = QUANTIZE(private_sleeping_agent * ratio)
removed.private_agent_b = QUANTIZE(private_agent_b * ratio)
private_oxygen = max(private_oxygen - removed.private_oxygen, 0)
private_nitrogen = max(private_nitrogen - removed.private_nitrogen, 0)
private_carbon_dioxide = max(private_carbon_dioxide - removed.private_carbon_dioxide, 0)
private_toxins = max(private_toxins - removed.private_toxins, 0)
private_sleeping_agent = max(private_sleeping_agent - removed.private_sleeping_agent, 0)
private_agent_b = max(private_agent_b - removed.private_agent_b, 0)
removed.private_temperature = private_temperature
set_dirty()
return removed
//Copies variables from sample
/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample)
private_oxygen = sample.private_oxygen
private_carbon_dioxide = sample.private_carbon_dioxide
private_nitrogen = sample.private_nitrogen
private_toxins = sample.private_toxins
private_sleeping_agent = sample.private_sleeping_agent
private_agent_b = sample.private_agent_b
private_temperature = sample.private_temperature
set_dirty()
return TRUE
///Copies all gas info from the turf into the gas list along with temperature
///Returns: TRUE if we are mutable, FALSE otherwise
/datum/gas_mixture/proc/copy_from_turf(turf/model)
private_oxygen = model.oxygen
private_carbon_dioxide = model.carbon_dioxide
private_nitrogen = model.nitrogen
private_toxins = model.toxins
private_sleeping_agent = model.sleeping_agent
private_agent_b = model.agent_b
//acounts for changes in temperature
var/turf/model_parent = model.parent_type
if(model.temperature != initial(model.temperature) || model.temperature != initial(model_parent.temperature))
private_temperature = model.temperature
set_dirty()
return TRUE
///Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length
///Returns: amount of gas exchanged (+ if sharer received)
/datum/gas_mixture/proc/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
if(!sharer)
return 0
/// Don't make calculations if there is no difference.
if(private_oxygen_archived == sharer.private_oxygen_archived && private_carbon_dioxide_archived == sharer.private_carbon_dioxide_archived && private_nitrogen_archived == sharer.private_nitrogen_archived &&\
private_toxins_archived == sharer.private_toxins_archived && private_sleeping_agent_archived == sharer.private_sleeping_agent_archived && private_agent_b_archived == sharer.private_agent_b_archived && private_temperature_archived == sharer.private_temperature_archived)
return 0
var/delta_oxygen = QUANTIZE(private_oxygen_archived - sharer.private_oxygen_archived) / (atmos_adjacent_turfs + 1)
var/delta_carbon_dioxide = QUANTIZE(private_carbon_dioxide_archived - sharer.private_carbon_dioxide_archived) / (atmos_adjacent_turfs + 1)
var/delta_nitrogen = QUANTIZE(private_nitrogen_archived - sharer.private_nitrogen_archived) / (atmos_adjacent_turfs + 1)
var/delta_toxins = QUANTIZE(private_toxins_archived - sharer.private_toxins_archived) / (atmos_adjacent_turfs + 1)
var/delta_sleeping_agent = QUANTIZE(private_sleeping_agent_archived - sharer.private_sleeping_agent_archived) / (atmos_adjacent_turfs + 1)
var/delta_agent_b = QUANTIZE(private_agent_b_archived - sharer.private_agent_b_archived) / (atmos_adjacent_turfs + 1)
var/delta_temperature = (private_temperature_archived - sharer.private_temperature_archived)
var/old_self_heat_capacity = 0
var/old_sharer_heat_capacity = 0
var/heat_capacity_self_to_sharer = 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_capacity_self_to_sharer += air_heat_capacity
else
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_capacity_self_to_sharer += carbon_dioxide_heat_capacity
else
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_capacity_self_to_sharer += toxins_heat_capacity
else
heat_capacity_sharer_to_self -= toxins_heat_capacity
if(delta_sleeping_agent)
var/sleeping_agent_heat_capacity = SPECIFIC_HEAT_N2O * delta_sleeping_agent
if(delta_sleeping_agent > 0)
heat_capacity_self_to_sharer += sleeping_agent_heat_capacity
else
heat_capacity_sharer_to_self -= sleeping_agent_heat_capacity
if(delta_agent_b)
var/agent_b_heat_capacity = SPECIFIC_HEAT_AGENT_B * delta_agent_b
if(delta_agent_b > 0)
heat_capacity_self_to_sharer += agent_b_heat_capacity
else
heat_capacity_sharer_to_self -= agent_b_heat_capacity
old_self_heat_capacity = heat_capacity()
old_sharer_heat_capacity = sharer.heat_capacity()
private_oxygen -= delta_oxygen
sharer.private_oxygen += delta_oxygen
private_carbon_dioxide -= delta_carbon_dioxide
sharer.private_carbon_dioxide += delta_carbon_dioxide
private_nitrogen -= delta_nitrogen
sharer.private_nitrogen += delta_nitrogen
private_toxins -= delta_toxins
sharer.private_toxins += delta_toxins
private_sleeping_agent -= delta_sleeping_agent
sharer.private_sleeping_agent += delta_sleeping_agent
private_agent_b -= delta_agent_b
sharer.private_agent_b += delta_agent_b
var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins + delta_sleeping_agent + delta_agent_b)
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)
private_temperature = (old_self_heat_capacity * private_temperature - heat_capacity_self_to_sharer * private_temperature_archived + heat_capacity_sharer_to_self * sharer.private_temperature_archived) / new_self_heat_capacity
if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY)
sharer.private_temperature = (old_sharer_heat_capacity * sharer.private_temperature - heat_capacity_sharer_to_self * sharer.private_temperature_archived + heat_capacity_self_to_sharer * private_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)
set_dirty()
if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
var/delta_pressure = private_temperature_archived * (total_moles() + moved_moles) - sharer.private_temperature_archived * (sharer.total_moles() - moved_moles)
return delta_pressure * R_IDEAL_GAS_EQUATION / volume
//Similar to share(...), except the model is not modified
//Return: amount of gas exchanged
/datum/gas_mixture/proc/mimic(turf/model, atmos_adjacent_turfs = 4) //I want this proc to die a painful death
var/delta_oxygen = QUANTIZE(private_oxygen_archived - model.oxygen) / (atmos_adjacent_turfs + 1)
var/delta_carbon_dioxide = QUANTIZE(private_carbon_dioxide_archived - model.carbon_dioxide) / (atmos_adjacent_turfs + 1)
var/delta_nitrogen = QUANTIZE(private_nitrogen_archived - model.nitrogen) / (atmos_adjacent_turfs + 1)
var/delta_toxins = QUANTIZE(private_toxins_archived - model.toxins) / (atmos_adjacent_turfs + 1)
var/delta_sleeping_agent = QUANTIZE(private_sleeping_agent_archived - model.sleeping_agent) / (atmos_adjacent_turfs + 1)
var/delta_agent_b = QUANTIZE(private_agent_b_archived - model.agent_b) / (atmos_adjacent_turfs + 1)
var/delta_temperature = (private_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
if(delta_sleeping_agent)
var/sleeping_agent_heat_capacity = SPECIFIC_HEAT_N2O * delta_sleeping_agent
heat_transferred -= sleeping_agent_heat_capacity * model.temperature
heat_capacity_transferred -= sleeping_agent_heat_capacity
if(delta_agent_b)
var/agent_b_heat_capacity = SPECIFIC_HEAT_AGENT_B * delta_agent_b
heat_transferred -= agent_b_heat_capacity * model.temperature
heat_capacity_transferred -= agent_b_heat_capacity
old_self_heat_capacity = heat_capacity()
private_oxygen -= delta_oxygen
private_carbon_dioxide -= delta_carbon_dioxide
private_nitrogen -= delta_nitrogen
private_toxins -= delta_toxins
private_sleeping_agent -= delta_sleeping_agent
private_agent_b -= delta_agent_b
var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins + delta_sleeping_agent + delta_agent_b)
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)
private_temperature = (old_self_heat_capacity * private_temperature - heat_capacity_transferred * private_temperature_archived) / new_self_heat_capacity
temperature_mimic(model, model.thermal_conductivity)
set_dirty()
if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
var/delta_pressure = private_temperature_archived * (total_moles() + moved_moles) - model.temperature * (model.oxygen + model.carbon_dioxide + model.nitrogen + model.toxins + model.sleeping_agent + model.agent_b)
return delta_pressure * R_IDEAL_GAS_EQUATION / volume
else
return 0
//Returns: FALSE if self-check failed or TRUE if check passes
/datum/gas_mixture/proc/check_turf(turf/model, atmos_adjacent_turfs = 4) //I want this proc to die a painful death
var/delta_oxygen = (private_oxygen_archived - model.oxygen) / (atmos_adjacent_turfs + 1)
var/delta_carbon_dioxide = (private_carbon_dioxide_archived - model.carbon_dioxide) / (atmos_adjacent_turfs + 1)
var/delta_nitrogen = (private_nitrogen_archived - model.nitrogen) / (atmos_adjacent_turfs + 1)
var/delta_toxins = (private_toxins_archived - model.toxins) / (atmos_adjacent_turfs + 1)
var/delta_sleeping_agent = (private_sleeping_agent_archived - model.sleeping_agent) / (atmos_adjacent_turfs + 1)
var/delta_agent_b = (private_agent_b_archived - model.agent_b) / (atmos_adjacent_turfs + 1)
var/delta_temperature = (private_temperature_archived - model.temperature)
if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= private_oxygen_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= private_carbon_dioxide_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= private_nitrogen_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= private_toxins_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_sleeping_agent) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_sleeping_agent) >= private_sleeping_agent_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_agent_b) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_agent_b) >= private_agent_b_archived * MINIMUM_AIR_RATIO_TO_SUSPEND)))
return FALSE
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
return FALSE
return TRUE
/datum/gas_mixture/proc/temperature_mimic(turf/model, conduction_coefficient) //I want this proc to die a painful death
set_dirty()
var/delta_temperature = (private_temperature - model.temperature)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
var/self_heat_capacity = heat_capacity()
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))
private_temperature -= heat / self_heat_capacity
///Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
///Returns: new temperature of the sharer
/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
var/delta_temperature = (private_temperature_archived - sharer.private_temperature_archived)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
set_dirty()
var/self_heat_capacity = heat_capacity_archived()
var/sharer_heat_capacity = sharer.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))
private_temperature -= heat / self_heat_capacity
sharer.private_temperature += heat / sharer_heat_capacity
/datum/gas_mixture/proc/temperature_turf_share(turf/simulated/sharer, conduction_coefficient) //I want this proc to die a painful death
var/delta_temperature = (private_temperature_archived - sharer.temperature)
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
set_dirty()
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))
private_temperature -= heat / self_heat_capacity
sharer.temperature += heat / sharer.heat_capacity
//Compares sample to self to see if within acceptable ranges that group processing may be enabled
/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
if((abs(private_oxygen - sample.private_oxygen) > MINIMUM_AIR_TO_SUSPEND) && \
((private_oxygen < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_oxygen) || (private_oxygen > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_oxygen)))
return FALSE
if((abs(private_nitrogen - sample.private_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \
((private_nitrogen < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_nitrogen) || (private_nitrogen > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_nitrogen)))
return FALSE
if((abs(private_carbon_dioxide - sample.private_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \
((private_carbon_dioxide < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_carbon_dioxide) || (private_carbon_dioxide > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_carbon_dioxide)))
return FALSE
if((abs(private_toxins - sample.private_toxins) > MINIMUM_AIR_TO_SUSPEND) && \
((private_toxins < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_toxins) || (private_toxins > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_toxins)))
return FALSE
if((abs(private_sleeping_agent - sample.private_sleeping_agent) > MINIMUM_AIR_TO_SUSPEND) && \
((private_sleeping_agent < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_sleeping_agent) || (private_sleeping_agent > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_sleeping_agent)))
return FALSE
if((abs(private_agent_b - sample.private_agent_b) > MINIMUM_AIR_TO_SUSPEND) && \
((private_agent_b < (1 - MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_agent_b) || (private_agent_b > (1 + MINIMUM_AIR_RATIO_TO_SUSPEND) * sample.private_agent_b)))
return FALSE
if(total_moles() > MINIMUM_AIR_TO_SUSPEND)
if((abs(private_temperature - sample.private_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \
((private_temperature < (1 - MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND) * sample.private_temperature) || (private_temperature > (1 + MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND) * sample.private_temperature)))
return FALSE
return TRUE
/datum/gas_mixture/proc/check_turf_total(turf/model) //I want this proc to die a painful death
var/delta_oxygen = (private_oxygen - model.oxygen)
var/delta_carbon_dioxide = (private_carbon_dioxide - model.carbon_dioxide)
var/delta_nitrogen = (private_nitrogen - model.nitrogen)
var/delta_toxins = (private_toxins - model.toxins)
var/delta_sleeping_agent = (private_sleeping_agent - model.sleeping_agent)
var/delta_agent_b = (private_agent_b - model.agent_b)
var/delta_temperature = (private_temperature - model.temperature)
if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= private_oxygen * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= private_carbon_dioxide * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= private_nitrogen * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= private_toxins * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_sleeping_agent) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_sleeping_agent) >= private_sleeping_agent * MINIMUM_AIR_RATIO_TO_SUSPEND)) \
|| ((abs(delta_agent_b) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_agent_b) >= private_agent_b * MINIMUM_AIR_RATIO_TO_SUSPEND)))
return FALSE
if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
return FALSE
return TRUE
///Performs various reactions such as combustion or fusion (LOL)
///Returns: TRUE if any reaction took place; FALSE otherwise
/datum/gas_mixture/proc/react(atom/dump_location)
var/reacting = FALSE //set to TRUE if a notable reaction occured (used by pipe_network)
if((private_agent_b > MINIMUM_MOLE_COUNT) && private_temperature > 900)
if(private_toxins > MINIMUM_HEAT_CAPACITY && private_carbon_dioxide > MINIMUM_HEAT_CAPACITY)
var/reaction_rate = min(private_carbon_dioxide * 0.75, private_toxins * 0.25, private_agent_b * 0.05)
private_carbon_dioxide -= reaction_rate
private_oxygen += reaction_rate
private_agent_b -= reaction_rate * 0.05
private_temperature += (reaction_rate * 20000) / heat_capacity()
reacting = TRUE
if((private_sleeping_agent > MINIMUM_MOLE_COUNT) && private_temperature > N2O_DECOMPOSITION_MIN_ENERGY)
var/energy_released = 0
var/old_heat_capacity = heat_capacity()
var/burned_fuel = 0
burned_fuel = min((1 - (N2O_DECOMPOSITION_COEFFICIENT_A / ((private_temperature + N2O_DECOMPOSITION_COEFFICIENT_C) ** 2))) * private_sleeping_agent, private_sleeping_agent)
private_sleeping_agent -= burned_fuel
if(burned_fuel)
energy_released += (N2O_DECOMPOSITION_ENERGY_RELEASED * burned_fuel)
private_oxygen += burned_fuel * 0.5
private_nitrogen += burned_fuel
var/new_heat_capacity = heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
private_temperature = (private_temperature * old_heat_capacity + energy_released) / new_heat_capacity
reacting = TRUE
fuel_burnt = 0
//Handle plasma burning
if((private_toxins > MINIMUM_MOLE_COUNT) && (private_oxygen > MINIMUM_MOLE_COUNT) && private_temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
var/energy_released = 0
var/old_heat_capacity = heat_capacity()
var/plasma_burn_rate = 0
var/private_oxygen_burn_rate = 0
//more plasma released at higher temperatures
var/private_temperature_scale = 0
if(private_temperature > PLASMA_UPPER_TEMPERATURE)
private_temperature_scale = 1
else
private_temperature_scale = (private_temperature - PLASMA_MINIMUM_BURN_TEMPERATURE) / (PLASMA_UPPER_TEMPERATURE - PLASMA_MINIMUM_BURN_TEMPERATURE)
if(private_temperature_scale > 0)
private_oxygen_burn_rate = OXYGEN_BURN_RATE_BASE - private_temperature_scale
if(private_oxygen > private_toxins * PLASMA_OXYGEN_FULLBURN)
plasma_burn_rate = (private_toxins * private_temperature_scale) / PLASMA_BURN_RATE_DELTA
else
plasma_burn_rate = (private_temperature_scale * (private_oxygen / PLASMA_OXYGEN_FULLBURN)) / PLASMA_BURN_RATE_DELTA
if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY)
plasma_burn_rate = min(plasma_burn_rate, private_toxins, private_oxygen / private_oxygen_burn_rate) //Ensures matter is conserved properly
private_toxins = QUANTIZE(private_toxins - plasma_burn_rate)
private_oxygen = QUANTIZE(private_oxygen - (plasma_burn_rate * private_oxygen_burn_rate))
private_carbon_dioxide += plasma_burn_rate
energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate)
fuel_burnt += (plasma_burn_rate) * (1 + private_oxygen_burn_rate)
if(energy_released > 0)
var/new_heat_capacity = heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
private_temperature = (private_temperature * old_heat_capacity + energy_released) / new_heat_capacity
if(fuel_burnt)
reacting = TRUE
set_dirty()
return reacting
///Takes the amount of the gas you want to PP as an argument
///So I don't have to do some hacky switches/defines/magic strings
///eg:
///Tox_PP = get_partial_pressure(gas_mixture.toxins)
///O2_PP = get_partial_pressure(gas_mixture.oxygen)
/datum/gas_mixture/proc/get_breath_partial_pressure(gas_pressure)
return (gas_pressure * R_IDEAL_GAS_EQUATION * private_temperature) / BREATH_VOLUME
///inverse
/datum/gas_mixture/proc/get_true_breath_pressure(partial_pressure)
return (partial_pressure * BREATH_VOLUME) / (R_IDEAL_GAS_EQUATION * private_temperature)
/datum/gas_mixture/proc/copy_from_milla(list/milla)
private_oxygen = milla[MILLA_INDEX_OXYGEN]
private_carbon_dioxide = milla[MILLA_INDEX_CARBON_DIOXIDE]
private_nitrogen = milla[MILLA_INDEX_NITROGEN]
private_toxins = milla[MILLA_INDEX_TOXINS]
private_sleeping_agent = milla[MILLA_INDEX_SLEEPING_AGENT]
private_agent_b = milla[MILLA_INDEX_AGENT_B]
innate_heat_capacity = milla[MILLA_INDEX_INNATE_HEAT_CAPACITY]
private_temperature = milla[MILLA_INDEX_TEMPERATURE]
/proc/share_many_airs(list/mixtures)
var/total_volume = 0
var/total_thermal_energy = 0
var/total_heat_capacity = 0
var/total_oxygen = 0
var/total_nitrogen = 0
var/total_toxins = 0
var/total_carbon_dioxide = 0
var/total_sleeping_agent = 0
var/total_agent_b = 0
for(var/datum/gas_mixture/G as anything in mixtures)
total_volume += G.volume
var/heat_capacity = G.heat_capacity()
total_heat_capacity += heat_capacity
total_thermal_energy += G.private_temperature * heat_capacity
total_oxygen += G.private_oxygen
total_nitrogen += G.private_nitrogen
total_toxins += G.private_toxins
total_carbon_dioxide += G.private_carbon_dioxide
total_sleeping_agent += G.private_sleeping_agent
total_agent_b += G.private_agent_b
if(total_volume > 0)
//Calculate temperature
var/temperature = 0
if(total_heat_capacity > 0)
temperature = total_thermal_energy/total_heat_capacity
//Update individual gas_mixtures by volume ratio
for(var/datum/gas_mixture/G as anything in mixtures)
G.private_oxygen = total_oxygen * G.volume / total_volume
G.private_nitrogen = total_nitrogen * G.volume / total_volume
G.private_toxins = total_toxins * G.volume / total_volume
G.private_carbon_dioxide = total_carbon_dioxide * G.volume / total_volume
G.private_sleeping_agent = total_sleeping_agent * G.volume / total_volume
G.private_agent_b = total_agent_b * G.volume / total_volume
G.private_temperature = temperature
G.set_dirty()
///Mathematical proofs:
/**
get_breath_partial_pressure(gas_pp) --> gas_pp/total_moles()*breath_pp = pp
get_true_breath_pressure(pp) --> gas_pp = pp/breath_pp*total_moles()
10/20*5 = 2.5
10 = 2.5/5*20
**/
#undef SPECIFIC_HEAT_TOXIN
#undef SPECIFIC_HEAT_AIR
#undef SPECIFIC_HEAT_CDO
#undef SPECIFIC_HEAT_N2O
#undef SPECIFIC_HEAT_AGENT_B
#undef HEAT_CAPACITY_CALCULATION
#undef MINIMUM_HEAT_CAPACITY
#undef MINIMUM_MOLE_COUNT
#undef QUANTIZE
/datum/gas_mixture/bound_to_turf
var/dirty = FALSE
var/lastread = 0
var/turf/bound_turf = null
var/datum/gas_mixture/readonly/readonly = null
/datum/gas_mixture/bound_to_turf/Destroy()
bound_turf = null
return ..()
/datum/gas_mixture/bound_to_turf/set_dirty()
dirty = TRUE
if(!isnull(readonly))
readonly.private_oxygen = private_oxygen
readonly.private_carbon_dioxide = private_carbon_dioxide
readonly.private_nitrogen = private_nitrogen
readonly.private_toxins = private_toxins
readonly.private_sleeping_agent = private_sleeping_agent
readonly.private_agent_b = private_agent_b
readonly.private_temperature = private_temperature
if(istype(bound_turf, /turf/simulated))
var/turf/simulated/S = bound_turf
S.update_visuals()
ASSERT(SSair.is_in_milla_safe_code())
/datum/gas_mixture/bound_to_turf/set_oxygen(value)
private_oxygen = value
set_dirty()
/datum/gas_mixture/bound_to_turf/set_carbon_dioxide(value)
private_carbon_dioxide = value
set_dirty()
/datum/gas_mixture/bound_to_turf/set_nitrogen(value)
private_nitrogen = value
set_dirty()
/datum/gas_mixture/bound_to_turf/set_toxins(value)
private_toxins = value
set_dirty()
/datum/gas_mixture/bound_to_turf/set_sleeping_agent(value)
private_sleeping_agent = value
set_dirty()
/datum/gas_mixture/bound_to_turf/set_agent_b(value)
private_agent_b = value
set_dirty()
/datum/gas_mixture/bound_to_turf/set_temperature(value)
private_temperature = value
set_dirty()
/datum/gas_mixture/bound_to_turf/proc/private_unsafe_write()
set_tile_atmos(bound_turf, oxygen = private_oxygen, carbon_dioxide = private_carbon_dioxide, nitrogen = private_nitrogen, toxins = private_toxins, sleeping_agent = private_sleeping_agent, agent_b = private_agent_b, temperature = private_temperature)
/datum/gas_mixture/bound_to_turf/proc/get_readonly()
if(isnull(readonly))
readonly = new(src)
return readonly
/// A gas mixture that should not be modified after creation.
/datum/gas_mixture/readonly
/datum/gas_mixture/readonly/New(datum/gas_mixture/parent)
private_oxygen = parent.private_oxygen
private_carbon_dioxide = parent.private_carbon_dioxide
private_nitrogen = parent.private_nitrogen
private_toxins = parent.private_toxins
private_sleeping_agent = parent.private_sleeping_agent
private_agent_b = parent.private_agent_b
private_temperature = parent.private_temperature
/datum/gas_mixture/readonly/set_dirty()
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_oxygen(value)
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_carbon_dioxide(value)
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_nitrogen(value)
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_toxins(value)
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_sleeping_agent(value)
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_agent_b(value)
CRASH("Attempted to modify a readonly gas_mixture.")
/datum/gas_mixture/readonly/set_temperature(value)
CRASH("Attempted to modify a readonly gas_mixture.")