This commit is contained in:
Putnam
2021-06-11 05:23:24 -07:00
parent 3b25f3e199
commit eda6a4d2e2
12 changed files with 517 additions and 480 deletions
@@ -0,0 +1,37 @@
// Breathing classes are, yes, just a list of gases, associated with numbers.
// But they're very simple: pluoxium's status as O2 * 8 is represented here,
// with a single line of code, no hardcoding and special-casing across the codebase.
// Not only that, but they're very general: you could have a negative value
// to simulate asphyxiants, e.g. if I add krypton it could go into the oxygen
// breathing class at -7, simulating krypton narcosis.
/datum/breathing_class
var/list/gases = null
var/list/products = null
var/danger_reagent = null
var/low_alert_category = "not_enough_oxy"
var/low_alert_datum = /obj/screen/alert/not_enough_oxy
var/high_alert_category = "too_much_oxy"
var/high_alert_datum = /obj/screen/alert/too_much_oxy
/datum/breathing_class/oxygen
gases = list(
GAS_O2 = 1
GAS_PLUOXIUM = 8,
GAS_CO2 = -0.7, // CO2 isn't actually toxic, just an asphyxiant
)
products = list(
GAS_CO2 = 1
)
/datum/breathing_class/plasma
gases = list(
GAS_PLASMA = 1
)
products = list(
GAS_CO2 = 1
)
low_alert_category = "not_enough_tox"
low_alert_datum = /obj/screen/alert/not_enough_tox
high_alert_category = "too_much_tox"
high_alert_datum = /obj/screen/alert/too_much_tox
@@ -0,0 +1,170 @@
/datum/gas/oxygen
id = GAS_O2
specific_heat = 20
name = "Oxygen"
oxidation_temperature = T0C - 100 // it checks max of this and fire temperature, so rarely will things spontaneously combust
/datum/gas/nitrogen
id = GAS_N2
specific_heat = 20
breath_alert_info = list(
not_enough_alert = list(
alert_category = "not_enough_nitro",
alert_type = /obj/screen/alert/not_enough_nitro
)
too_much_alert = list(
alert_category = "too_much_nitro",
alert_type = /obj/screen/alert/too_much_nitro
)
)
name = "Nitrogen"
/datum/gas/carbon_dioxide //what the fuck is this?
id = GAS_CO2
specific_heat = 30
name = "Carbon Dioxide"
breath_results = GAS_O2
breath_alert_info = list(
not_enough_alert = list(
alert_category = "not_enough_co2",
alert_type = /obj/screen/alert/not_enough_co2
)
too_much_alert = list(
alert_category = "too_much_co2",
alert_type = /obj/screen/alert/too_much_co2
)
)
breath_reagent_dangerous = /datum/reagent/co2_toxic
fusion_power = 3
/datum/gas/plasma
id = GAS_PLASMA
specific_heat = 200
name = "Plasma"
gas_overlay = "plasma"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
// no fire info cause it has its own bespoke reaction for trit generation reasons
/datum/gas/water_vapor
id = GAS_H2O
specific_heat = 40
name = "Water Vapor"
gas_overlay = "water_vapor"
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 8
breath_reagent = /datum/reagent/water
/datum/gas/hypernoblium
id = GAS_HYPERNOB
specific_heat = 2000
name = "Hyper-noblium"
gas_overlay = "freon"
moles_visible = MOLES_GAS_VISIBLE
/datum/gas/nitrous_oxide
id = GAS_NITROUS
specific_heat = 40
name = "Nitrous Oxide"
gas_overlay = "nitrous_oxide"
moles_visible = MOLES_GAS_VISIBLE * 2
flags = GAS_FLAG_DANGEROUS
fire_products = list(GAS_N2 = 1)
oxidation_rate = 0.5
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
/datum/gas/nitryl
id = GAS_NITRYL
specific_heat = 20
name = "Nitryl"
gas_overlay = "nitryl"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
fusion_power = 15
fire_products = list(GAS_N2 = 0.5)
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
/datum/gas/tritium
id = GAS_TRITIUM
specific_heat = 10
name = "Tritium"
gas_overlay = "tritium"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
fusion_power = 1
/*
these are for when we add hydrogen, trit gets to keep its hardcoded fire for legacy reasons
fire_provides = list(GAS_H2O = 2)
fire_burn_rate = 2
fire_energy_released = FIRE_HYDROGEN_ENERGY_RELEASED
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
*/
/datum/gas/bz
id = GAS_BZ
specific_heat = 20
name = "BZ"
flags = GAS_FLAG_DANGEROUS
fusion_power = 8
/datum/gas/stimulum
id = GAS_STIMULUM
specific_heat = 5
name = "Stimulum"
fusion_power = 7
/datum/gas/pluoxium
id = GAS_PLUOXIUM
specific_heat = 80
name = "Pluoxium"
fusion_power = 10
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
oxidation_rate = 8
/datum/gas/miasma
id = GAS_MIASMA
specific_heat = 20
fusion_power = 50
name = "Miasma"
gas_overlay = "miasma"
moles_visible = MOLES_GAS_VISIBLE * 60
/datum/gas/methane
id = GAS_METHANE
specific_heat = 30
name = "Methane"
breath_results = GAS_METHYL_BROMIDE
fire_provides = list(GAS_CO2 = 1, GAS_H2O = 2)
fire_burn_rate = 0.5
breath_alert_info = list(
not_enough_alert = list(
alert_category = "not_enough_ch4",
alert_type = /obj/screen/alert/not_enough_ch4
)
too_much_alert = list(
alert_category = "too_much_ch4",
alert_type = /obj/screen/alert/too_much_ch4
)
)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
/datum/gas/methyl_bromide
id = GAS_METHYL_BROMIDE
specific_heat = 42
name = "Methyl Bromide"
flags = GAS_FLAG_DANGEROUS
breath_alert_info = list(
not_enough_alert = list(
alert_category = "not_enough_ch3br",
alert_type = /obj/screen/alert/not_enough_ch3br
)
too_much_alert = list(
alert_category = "too_much_ch3br",
alert_type = /obj/screen/alert/too_much_ch3br
)
)
fire_provides = list(GAS_CO2 = 1, GAS_H2O = 1.5, GAS_BZ = 0.5)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
fire_burn_rate = 0.5
fire_temperature = 808 // its autoignition, it apparently doesn't spark readily, so i don't put it lower
@@ -0,0 +1,124 @@
GLOBAL_LIST_INIT(hardcoded_gases, list(GAS_O2, GAS_N2, GAS_CO2, GAS_PLASMA)) //the main four gases, which were at one time hardcoded
GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GAS_PLUOXIUM, GAS_STIMULUM, GAS_NITRYL))) //unable to react amongst themselves
// Auxgm
// It's a send-up of XGM, like what baystation got.
// It's got the same architecture as XGM, but it's structured
// differently to make it more convenient for auxmos.
// Most important compared to TG is that it does away with hardcoded typepaths,
// which lead to problems on the auxmos end anyway. We cache the string value
// references on the Rust end, so no performance is lost here.
// Also allows you to add new gases at runtime
/proc/_auxtools_register_gas(datum/gas/gas) // makes sure auxtools knows stuff about this gas
/datum/auxgm
var/list/datums = list()
var/list/specific_heats = list()
var/list/names = list()
var/list/visibility = list()
var/list/overlays = list()
var/list/flags = list()
var/list/ids = list()
var/list/typepaths = list()
var/list/fusion_powers = list()
var/list/breathing_classes = list()
var/list/breath_results = list()
var/list/breath_reagents = list()
var/list/breath_reagents_dangerous = list()
var/list/breath_alert_info = list()
var/list/oxidation_temperatures = list()
var/list/oxidation_rates = list()
var/list/fire_temperatures = list()
var/list/fire_enthalpies = list()
var/list/fire_products = list()
var/list/fire_burn_rates = list()
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
var/gas_overlay = "" //icon_state in icons/effects/atmospherics.dmi
var/moles_visible = null
var/flags = NONE //currently used by canisters
var/fusion_power = 0 // How much the gas destabilizes a fusion reaction
var/breath_results = GAS_CO2 // what breathing this breathes out
var/breath_reagent = null // what breathing this adds to your reagents
var/breath_reagent_dangerous = null // what breathing this adds to your reagents IF it's above a danger threshold
var/list/breath_alert_info = null // list for alerts that pop up when you have too much/not enough of something
var/oxidation_temperature = null // temperature above which this gas is an oxidizer; null for none
var/oxidation_rate = 1 // how many moles of this can oxidize how many moles of material
var/fire_temperature = null // temperature above which gas may catch fire; null for none
var/list/fire_products = null // what results when this gas is burned (oxidizer or fuel); null for none
var/fire_energy_released = 0 // how much energy is released per mole of fuel burned
var/fire_burn_rate = 1 // how many moles are burned per product released
/datum/gas/proc/breath(partial_pressure, light_threshold, heavy_threshold, moles, mob/living/carbon/C, obj/item/organ/lungs/lungs)
// This is only called on gases with the GAS_FLAG_BREATH_PROC flag. When possible, do NOT use this--
// greatly prefer just adding a reagent. This is mostly around for legacy reasons.
return null
/datum/auxgm/add_gas(datum/gas/gas)
var/g = gas.id
if(g)
datums[g] = gas
specific_heats[g] = gas.specific_heat
names[g] = gas.name
if(gas.moles_visible)
visibility[g] = gas.moles_visible
overlays[g] = new /list(FACTOR_GAS_VISIBLE_MAX)
for(var/i in 1 to FACTOR_GAS_VISIBLE_MAX)
overlays[g][i] = new /obj/effect/overlay/gas(gas.gas_overlay, i * 255 / FACTOR_GAS_VISIBLE_MAX)
else
visibility[g] = 0
overlays[g] = 0
flags[g] = gas.flags
ids[g] = g
typepaths[g] = gas_path
fusion_powers[g] = gas.fusion_power
if(gas.breath_alert_info)
breath_alert_info[g] = gas.breath_alert_info
breath_results[g] = gas.breath_results
if(gas.breath_reagent)
breath_reagents[g] = gas.breath_reagent
if(gas.breath_reagent_dangerous)
breath_reagents_dangerous[g] = gas.breath_reagent_dangerous
if(gas.oxidation_temperature)
oxidation_temperatures[g] = gas.oxidation_temperature
oxidation_rates[g] = gas.oxidation_rate
if(gas.fire_products)
fire_products[g] = gas.fire_products
fire_enthalpies[g] = gas.fire_energy_released
else if(gas.fire_temperature)
fire_temperatures[g] = gas.fire_temperature
fire_burn_rates[g] = gas.fire_burn_rate
if(gas.fire_products)
fire_products[g] = gas.fire_products
fire_enthalpies[g] = gas.fire_energy_released
_auxtools_register_gas(gas)
/datum/auxgm/New()
for(var/gas_path in subtypesof(/datum/gas))
var/datum/gas/gas = new gas_path
add_gas(gas)
GLOBAL_DATUM_INIT(gas_data, /datum/auxgm, new)
/obj/effect/overlay/gas
icon = 'icons/effects/atmospherics.dmi'
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE // should only appear in vis_contents, but to be safe
layer = FLY_LAYER
appearance_flags = TILE_BOUND
vis_flags = NONE
/obj/effect/overlay/gas/New(state, alph)
. = ..()
icon_state = state
alpha = alph
@@ -314,17 +314,6 @@ GLOBAL_LIST_INIT(auxtools_atmos_initialized,FALSE)
if (. & STOP_REACTIONS)
break
*/
//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 * return_temperature()) / BREATH_VOLUME
//inverse
/datum/gas_mixture/proc/get_true_breath_pressure(partial_pressure)
return (partial_pressure * BREATH_VOLUME) / (R_IDEAL_GAS_EQUATION * return_temperature())
/datum/gas_mixture/proc/set_analyzer_results(instability)
if(!analyzer_results)
@@ -1,227 +0,0 @@
GLOBAL_LIST_INIT(hardcoded_gases, list(GAS_O2, GAS_N2, GAS_CO2, GAS_PLASMA)) //the main four gases, which were at one time hardcoded
GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GAS_PLUOXIUM, GAS_STIMULUM, GAS_NITRYL))) //unable to react amongst themselves
// Auxgm
// It's a send-up of XGM, like what baystation got.
// It's got the same architecture as XGM, but it's structured
// differently to make it more convenient for auxmos.
// Most important compared to TG is that it does away with hardcoded typepaths,
// which lead to problems on the auxmos end anyway. We cache the string value
// references on the Rust end, so no performance is lost here.
// Also allows you to add new gases at runtime
/proc/_auxtools_register_gas(datum/gas/gas) // makes sure auxtools knows stuff about this gas
/datum/auxgm
var/list/datums = list()
var/list/specific_heats = list()
var/list/names = list()
var/list/visibility = list()
var/list/overlays = list()
var/list/flags = list()
var/list/ids = list()
var/list/typepaths = list()
var/list/fusion_powers = list()
/datum/auxgm/add_gas(datum/gas/gas)
var/g = gas.id
if(g)
datums[g] = gas
specific_heats[g] = gas.specific_heat
names[g] = gas.name
if(gas.moles_visible)
visibility[g] = gas.moles_visible
overlays[g] = new /list(FACTOR_GAS_VISIBLE_MAX)
for(var/i in 1 to FACTOR_GAS_VISIBLE_MAX)
overlays[g][i] = new /obj/effect/overlay/gas(gas.gas_overlay, i * 255 / FACTOR_GAS_VISIBLE_MAX)
else
visibility[g] = 0
overlays[g] = 0
flags[g] = gas.flags
ids[g] = g
typepaths[g] = gas_path
fusion_powers[g] = gas.fusion_power
_auxtools_register_gas(gas)
/datum/auxgm/New()
for(var/gas_path in subtypesof(/datum/gas))
var/datum/gas/gas = new gas_path
add_gas(gas)
GLOBAL_DATUM_INIT(gas_data, /datum/auxgm, new)
/datum/breath_info
var/breathing_power = 1 // how much this gas counts for good-breath
var/breathing_class = null // what type of breath this is; lungs can also use gas IDs directly
var/breath_results = GAS_CO2 // what breathing a mole of this results in
/datum/oxidation_info
var/oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST // temperature above which this gas is an oxidizer
var/list/oxidation_provides = list() // a list of elements this gas provides in combustion
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
var/gas_overlay = "" //icon_state in icons/effects/atmospherics.dmi
var/moles_visible = null
var/flags = NONE //currently used by canisters
var/fusion_power = 0 // How much the gas destabilizes a fusion reaction
var/breathing_power = 1 // how much this gas counts for good-breath
var/breathing_class = null // what type of breath this is; lungs can also use gas IDs directly
var/breath_results = GAS_CO2 // what breathing a mole of this results in
var/oxidation_temperature = null // temperature above which this gas is an oxidizer; null for none
var/oxidation_rate = 1 // how many moles of this can oxidize how many moles of material
var/oxidation_energy_released = 0 // how many moles are released per mole burned
var/list/oxidation_products = null // extra results from oxidizing this (per mole); null for none
var/fire_temperature = null // temperature above which gas may catch fire; null for none
var/list/fire_provides = null // what elements this gas provides as fuel for combustion; null for none
var/fire_energy_released = 0 // how much energy is released per mole of fuel burned
var/fire_burn_rate = 1 // how many moles are burned per product released
/datum/gas/oxygen
id = GAS_O2
specific_heat = 20
name = "Oxygen"
breathing_class = BREATH_OXY
oxidation_temperature = T0C - 100 // it checks max of this and fire temperature, so rarely will things spontaneously combust
/datum/gas/nitrogen
id = GAS_N2
specific_heat = 20
name = "Nitrogen"
/datum/gas/carbon_dioxide //what the fuck is this?
id = GAS_CO2
specific_heat = 30
name = "Carbon Dioxide"
fusion_power = 3
/datum/gas/plasma
id = GAS_PLASMA
specific_heat = 200
name = "Plasma"
gas_overlay = "plasma"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
breathing_class = BREATH_PLASMA
// no fire info cause it has its own bespoke reaction for trit generation reasons
/datum/gas/water_vapor
id = GAS_H2O
specific_heat = 40
name = "Water Vapor"
gas_overlay = "water_vapor"
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 8
/datum/gas/hypernoblium
id = GAS_HYPERNOB
specific_heat = 2000
name = "Hyper-noblium"
gas_overlay = "freon"
moles_visible = MOLES_GAS_VISIBLE
/datum/gas/nitrous_oxide
id = GAS_NITROUS
specific_heat = 40
name = "Nitrous Oxide"
gas_overlay = "nitrous_oxide"
moles_visible = MOLES_GAS_VISIBLE * 2
flags = GAS_FLAG_DANGEROUS
oxidation_products = list(GAS_N2 = 1)
oxidation_rate = 0.5
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
/datum/gas/nitryl
id = GAS_NITRYL
specific_heat = 20
name = "Nitryl"
gas_overlay = "nitryl"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
fusion_power = 15
oxidation_products = list(GAS_N2 = 0.5)
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
/datum/gas/tritium
id = GAS_TRITIUM
specific_heat = 10
name = "Tritium"
gas_overlay = "tritium"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
fusion_power = 1
/*
these are for when we add hydrogen, trit gets to keep its hardcoded fire for legacy reasons
fire_provides = list(GAS_H2O = 2)
fire_burn_rate = 2
fire_energy_released = FIRE_HYDROGEN_ENERGY_RELEASED
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
*/
/datum/gas/bz
id = GAS_BZ
specific_heat = 20
name = "BZ"
flags = GAS_FLAG_DANGEROUS
fusion_power = 8
/datum/gas/stimulum
id = GAS_STIMULUM
specific_heat = 5
name = "Stimulum"
fusion_power = 7
/datum/gas/pluoxium
id = GAS_PLUOXIUM
specific_heat = 80
name = "Pluoxium"
fusion_power = 10
breathing_power = 8
breathing_class = CLASS_OXY
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
oxidation_rate = 8
/datum/gas/miasma
id = GAS_MIASMA
specific_heat = 20
fusion_power = 50
name = "Miasma"
gas_overlay = "miasma"
moles_visible = MOLES_GAS_VISIBLE * 60
/datum/gas/methane
id = GAS_METHANE
specific_heat = 30
name = "Methane"
fire_provides = list(GAS_CO2 = 1, GAS_H2O = 2)
fire_burn_rate = 0.5
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
/datum/gas/methyl_bromide
id = GAS_METHYL_BROMIDE
specific_heat = 42
name = "Methyl Bromide"
flags = GAS_FLAG_DANGEROUS
fire_provides = list(GAS_CO2 = 1, GAS_H2O = 1.5, GAS_BZ = 0.5)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
fire_burn_rate = 0.5
fire_temperature = 808 // its autoignition, it apparently doesn't spark readily, so i don't put it lower
/obj/effect/overlay/gas
icon = 'icons/effects/atmospherics.dmi'
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE // should only appear in vis_contents, but to be safe
layer = FLY_LAYER
appearance_flags = TILE_BOUND
vis_flags = NONE
/obj/effect/overlay/gas/New(state, alph)
. = ..()
icon_state = state
alpha = alph
@@ -253,8 +253,56 @@
/datum/gas_reaction/genericfire/react(datum/gas_mixture/air, datum/holder)
var/temperature = air.return_temperature()
var/list/oxidation_temps = GLOB.gas_data.oxidation_temps
for(var/datum/gas/G in air.get_gases())
var/list/oxidation_rates = GLOB.gas_data.oxidation_rates
var/oxidation_power = 0
var/list/burn_results = list()
var/list/fuels = list()
var/list/oxidizers = list()
var/list/fuel_rates = GLOB.gas_data.fuel_rates
var/total_fuel = 0
var/energy_released = 0
for(var/G in air.get_gases())
var/oxidation_temp = oxidation_temps[G]
if(oxidation_temp && oxidation_temp > temperature)
var/temperature_scale = max(0, 1-(temperature / oxidation_temp))
var/amt = air.get_moles(G) * temperature_scale
oxidizers[G] = amt
oxidation_power += amt * oxidation_rates[G]
else
var/fuel_temp = fuel_temps[G]
if(fuel_temp && fuel_temp > temperature)
var/amt = (air.get_moles(G) / fuel_rates[G]) * max(0, 1-(temperature / fuel_temp))
fuels[G] = amt // we have to calculate the actual amount we're using after we get all oxidation together
total_fuel += amt
if(oxidation_power <= 0 || total_fuel <= 0)
return NO_REACTION
var/oxidation_ratio = oxidation_power / total_fuel
if(oxidation_ratio > 1)
for(var/oxidizer in oxidizers)
oxidizers[oxidizer] /= oxidation_ratio
else if oxidation_ratio < 1
for(var/fuel in fuels)
fuels[fuel] *= oxidation_ratio
fuels += oxidizers
var/list/fire_products = GLOB.gas_data.fire_products
var/list/fire_enthalpies = GLOB.gas_data.fire_enthalpies
for(var/fuel in fuels + oxidizers)
var/amt = fuels[fuel]
if(!burn_results[fuel])
burn_results[fuel] = 0
burn_results[fuel] -= amt
energy_released += amt * fire_enthalpies[fuel]
for(var/product in fire_products[fuel])
if(!burn_results[product])
burn_results[product] = 0
burn_results[o] += amt
var/final_energy = air.thermal_energy() + energy_released
for(var/result in burn_results)
air.adjust_moles(result, burn_results[result])
air.set_temperature(final_energy / air.heat_capacity())
var/list/cached_results = air.reaction_results
cached_results["fire"] = min(total_fuel, oxidation_power) * 2
return cached_results["fire"] ? REACTING : NO_REACTION
//fusion: a terrible idea that was fun but broken. Now reworked to be less broken and more interesting. Again (and again, and again). Again!