diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm
index de4123eaff0c..a50c637263b2 100644
--- a/code/__DEFINES/antagonists.dm
+++ b/code/__DEFINES/antagonists.dm
@@ -212,6 +212,8 @@ GLOBAL_LIST_INIT(ai_employers, list(
/// Checks if the given mob is a blood cultist
#define IS_CULTIST(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/cult) || mob?.mind?.has_antag_datum(/datum/antagonist/advanced_cult)) // NON-MODULE CHANGE
+/// Gets the antagonist datum for a blood cultist, or null if they are not one.
+#define GET_CULTIST(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/cult)) // NON-MODULE CHANGE
/// Checks if the given mob is a changeling
#define IS_CHANGELING(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/changeling))
diff --git a/code/__DEFINES/atmospherics/atmos_core.dm b/code/__DEFINES/atmospherics/atmos_core.dm
index 0f441bdf2089..da63ab6b1edd 100644
--- a/code/__DEFINES/atmospherics/atmos_core.dm
+++ b/code/__DEFINES/atmospherics/atmos_core.dm
@@ -1,11 +1,4 @@
//LISTMOS
-//indices of values in gas lists.
-///Amount of total moles in said gas mixture
-#define MOLES 1
-///Archived version of MOLES
-#define ARCHIVE 2
-///All gas related variables
-#define GAS_META 3
///Gas specific heat per mole
#define META_GAS_SPECIFIC_HEAT 1
///Name of the gas
@@ -22,6 +15,8 @@
#define META_GAS_FUSION_POWER 7
///Short description of the gas.
#define META_GAS_DESC 8
+///Length of gas meta array
+#define META_GAS_LENGTH 8
//ATMOS
//stuff you should probably leave well alone!
/// kPa*L/(K*mol)
@@ -56,7 +51,7 @@
/// Molar accuracy to round to
#define MOLAR_ACCURACY 1E-4
/// Types of gases (based on gaslist_cache)
-#define GAS_TYPE_COUNT GLOB.gaslist_cache.len
+#define GAS_TYPE_COUNT 20
/// Maximum error caused by QUANTIZE when removing gas (roughly, in reality around 2 * MOLAR_ACCURACY less)
#define MAXIMUM_ERROR_GAS_REMOVAL (MOLAR_ACCURACY * GAS_TYPE_COUNT)
@@ -182,3 +177,13 @@
#define ATMOS_PRESSURE_APPROXIMATION_ITERATIONS 20
/// We deal with big numbers and a lot of math, things are bound to get imprecise. Take this traveller.
#define ATMOS_PRESSURE_ERROR_TOLERANCE 0.01
+/// Helper function for retrieving gas meta info for use in performace critical places
+#define GAS_META /datum/gas_mixture::gas_meta
+
+// Flags for gas-cargo handling
+/// Gas can be sold in canisters
+#define GAS_EXPORTABLE (1 << 0)
+/// Gas can be purchased in canisters
+#define GAS_PURCHASABLE (1 << 1)
+/// Gas is dangerous and may need additional access
+#define GAS_DANGEROUS (1 << 2)
diff --git a/code/__DEFINES/atmospherics/atmos_helpers.dm b/code/__DEFINES/atmospherics/atmos_helpers.dm
index b004a6e5e301..d91115102a9d 100644
--- a/code/__DEFINES/atmospherics/atmos_helpers.dm
+++ b/code/__DEFINES/atmospherics/atmos_helpers.dm
@@ -53,41 +53,24 @@
///Calculate the thermal energy of the selected gas (J)
#define THERMAL_ENERGY(gas) (gas.temperature * gas.heat_capacity())
-///Directly adds a gas to a gas mixture without checking for its presence beforehand, use only if is certain the absence of said gas
-#define ADD_GAS(gas_id, out_list)\
- var/list/tmp_gaslist = GLOB.gaslist_cache[gas_id]; out_list[gas_id] = tmp_gaslist.Copy();
-
-///Adds a gas to a gas mixture but checks if is already present, faster than the same proc
-#define ASSERT_GAS(gas_id, gas_mixture) ASSERT_GAS_IN_LIST(gas_id, gas_mixture.gases)
-
-///Adds a gas to a gas LIST but checks if is already present, accepts a list instead of a datum, so faster if the list is locally cached
-#define ASSERT_GAS_IN_LIST(gas_id, gases) if (!gases[gas_id]) { ADD_GAS(gas_id, gases) };
-
-//prefer this to gas_mixture/total_moles in performance critical areas
-///Calculate the total moles of the gas mixture, faster than the proc, good for performance critical areas
-#define TOTAL_MOLES(cached_gases, out_var)\
- out_var = 0;\
- for(var/total_moles_id in cached_gases){\
- out_var += cached_gases[total_moles_id][MOLES];\
- }
-
GLOBAL_LIST_INIT(nonoverlaying_gases, typecache_of_gases_with_no_overlays())
///Returns a list of overlays of every gas in the mixture
-#define GAS_OVERLAYS(gases, out_var, z_layer_turf)\
+#define GAS_OVERLAYS(moles, out_var, z_layer_turf)\
do { \
out_var = list();\
var/offset = GET_TURF_PLANE_OFFSET(z_layer_turf) + 1;\
- for(var/_ID in gases){\
- if(GLOB.nonoverlaying_gases[_ID]) continue;\
- var/_GAS = gases[_ID];\
- var/_GAS_META = _GAS[GAS_META];\
- if(_GAS[MOLES] <= _GAS_META[META_GAS_MOLES_VISIBLE]) continue;\
- var/_GAS_OVERLAY = _GAS_META[META_GAS_OVERLAY][offset];\
- out_var += _GAS_OVERLAY[min(TOTAL_VISIBLE_STATES, CEILING(_GAS[MOLES] / MOLES_GAS_VISIBLE_STEP, 1))];\
- } \
+ var/list/_META_MOLES_VISIBLE = GAS_META[META_GAS_MOLES_VISIBLE];\
+ var/list/_META_GAS_OVERLAY = GAS_META[META_GAS_OVERLAY];\
+ for(var/gas_id, amount in moles){\
+ if(GLOB.nonoverlaying_gases[gas_id]) continue;\
+ if(amount <= _META_MOLES_VISIBLE[gas_id]) continue;\
+ var/_GAS_OVERLAY = _META_GAS_OVERLAY[gas_id][offset];\
+ out_var += _GAS_OVERLAY[min(TOTAL_VISIBLE_STATES, CEILING(amount / MOLES_GAS_VISIBLE_STEP, 1))];\
+ }\
}\
while (FALSE)
+
#ifdef TESTING
GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0))
#define CALCULATE_ADJACENT_TURFS(T, state) if (SSair.adjacent_rebuild[T]) { GLOB.atmos_adjacent_savings[1] += 1 } else { GLOB.atmos_adjacent_savings[2] += 1; SSair.adjacent_rebuild[T] = state}
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
index c197eb8c8fc3..6928ed198d1e 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -190,3 +190,10 @@
//from base of [/obj/effect/particle_effect/fluid/smoke/proc/smoke_mob]: (seconds_per_tick)
#define COMSIG_CARBON_EXPOSED_TO_SMOKE "carbon_exposed_to_smoke"
+
+/// Before a mob starts dreaming - you can add dream datums to the dream pool to override the selection: (list/dream_pool)
+#define COMSIG_PRE_DREAMING "pre_dreaming"
+/// A mob has started dreaming: (datum/dream/current_dream)
+#define COMSIG_START_DREAMING "start_dreaming"
+/// A mob has finished dreaming: (datum/dream/finished_dream)
+#define COMSIG_END_DREAMING "end_dreaming"
diff --git a/code/__DEFINES/economy.dm b/code/__DEFINES/economy.dm
index 344b575aa41d..1415dd34d697 100644
--- a/code/__DEFINES/economy.dm
+++ b/code/__DEFINES/economy.dm
@@ -75,6 +75,21 @@
#define MARKET_PROFIT_MODIFIER 0.8 //We don't make every sale a 1-1 of the actual buy price value, like with real life taxes and to encourage more smart trades
+// Fair warning that these defines at present are not used in all tgui, static descriptions, or any varible names or comments
+/// The symbol for the default type of money used in the code.
+#define MONEY_SYMBOL "cr"
+/// The name for the default type of money used in the code.
+#define MONEY_NAME "credits"
+#define MONEY_NAME_SINGULAR "credit"
+#define MONEY_NAME_CAPITALIZED "Credits"
+// Due to the ways macros work, I cant just directly use credit\s.
+// You will need to verify there is no loose use cases of credit\s.
+// As of present there is none left floating around.
+#define MONEY_NAME_AUTOPURAL(amount) "credit[##amount == 1 ? "" : "s"]"
+
+#define MONEY_MINING_SYMBOL "mp"
+#define MONEY_BITRUNNING_SYMBOL "np"
+
/// Create quantity subtypes for stock market datums.
#define MARKET_QUANTITY_HELPERS(path) ##path/one {\
amount = 1; \
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index aca45241f1cc..19598ad72178 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -55,6 +55,9 @@
/// Increments a value and wraps it if it exceeds some value. Can be used to circularly iterate through a list through `idx = WRAP_UP(idx, length_of_list)`.
#define WRAP_UP(val, max) (((val) % (max)) + 1)
+/// Helper that increments and wraps the passed in number when it hits the integer limit
+#define WRAP_UID(val) WRAP_UP(val, SHORT_REAL_LIMIT - 1)
+
// Real modulus that handles decimals
#define MODULUS(x, y) ( (x) - FLOOR(x, y))
diff --git a/code/__DEFINES/reactions.dm b/code/__DEFINES/reactions.dm
index f51271d0e22b..80f70a5001be 100644
--- a/code/__DEFINES/reactions.dm
+++ b/code/__DEFINES/reactions.dm
@@ -174,17 +174,23 @@
/// The number of moles of hyper-noblium required to prevent reactions.
#define REACTION_OPPRESSION_THRESHOLD 5
+/// Minimum temperature required for hypernoblium to prevent reactions.
+#define REACTION_OPPRESSION_MIN_TEMP 20
// Halon:
-/// The minimum temperature required for halon to form from tritium and BZ.
-#define HALON_FORMATION_MIN_TEMPERATURE 30
-/// The maximum temperature required for halon to form from tritium and BZ.
-#define HALON_FORMATION_MAX_TEMPERATURE 55
-/// The amount of energy 4.25 moles of halon forming from tritium and BZ releases.
-#define HALON_FORMATION_ENERGY 300
+/// Energy released per mole of BZ consumed during halon formation.
+#define HALON_FORMATION_ENERGY 91232.1
/// How much energy a mole of halon combusting consumes.
#define HALON_COMBUSTION_ENERGY 2500
+/// The minimum temperature required for halon to combust.
+#define HALON_COMBUSTION_MIN_TEMPERATURE (T0C + 70)
+/// The temperature scale for halon combustion reaction rate.
+#define HALON_COMBUSTION_TEMPERATURE_SCALE (FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 10)
+/// Amount of halon required to be consumed in order to release resin. This is always possible as long as there's enough gas.
+#define HALON_COMBUSTION_MINIMUM_RESIN_MOLES (0.99 * HALON_COMBUSTION_MIN_TEMPERATURE / HALON_COMBUSTION_TEMPERATURE_SCALE)
+/// The volume of the resin foam fluid when halon combusts, in turfs.
+#define HALON_COMBUSTION_RESIN_VOLUME 1
// Healium:
/// The minimum temperature healium can form from BZ and freon at.
diff --git a/code/__DEFINES/religion.dm b/code/__DEFINES/religion.dm
index 3074dae29879..d3fb3d36e410 100644
--- a/code/__DEFINES/religion.dm
+++ b/code/__DEFINES/religion.dm
@@ -54,3 +54,17 @@
#define PUNISHMENT_LIGHTNING "lightningbolt"
///brands the sinner
#define PUNISHMENT_BRAND "brand"
+
+/// Failed to bless the target, beat them over the head
+#define BLESSING_FAILED "failed"
+/// Blessed unsuccessfully, no limbs to heal, robotic limbs, etc
+#define BLESSING_IGNORED "ignored"
+/// Blessed successfully by healing or whatever
+#define BLESSING_SUCCESS "success"
+
+///The rite will automatically delete itself by the religious tool calling it after it's invoked.
+#define RITE_AUTO_DELETE (1<<0)
+///The rite can be performed multiple times with a religious tool, so don't delete/null it.
+#define RITE_ALLOW_MULTIPLE_PERFORMS (1<<1)
+///The rite can only be fully performed once, so we'll completely remove it from the rite list afterwards.
+#define RITE_ONE_TIME_USE (1<<2)
diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index e4bbc1a6188d..f6c36434fe3b 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -43,6 +43,7 @@
#define span_cultboldtalic(str) ("" + str + "")
#define span_cultitalic(str) ("" + str + "")
#define span_cultlarge(str) ("" + str + "")
+#define span_cyan(str) ("" + str + "")
#define span_danger(str) ("" + str + "")
#define span_deadsay(str) ("" + str + "")
#define span_deconversion_message(str) ("" + str + "")
diff --git a/code/__HELPERS/atmospherics.dm b/code/__HELPERS/atmospherics.dm
index 2a59cf60b403..7ca179f75459 100644
--- a/code/__HELPERS/atmospherics.dm
+++ b/code/__HELPERS/atmospherics.dm
@@ -41,13 +41,15 @@
)
if(!gasmix)
return
- for(var/gas_path in gasmix.gases)
+ var/list/cached_gas_id = GAS_META[META_GAS_ID]
+ var/list/cached_gas_name = GAS_META[META_GAS_NAME]
+ for(var/gas_path, amount in gasmix.moles)
.["gases"] += list(list(
- gasmix.gases[gas_path][GAS_META][META_GAS_ID],
- gasmix.gases[gas_path][GAS_META][META_GAS_NAME],
- gasmix.gases[gas_path][MOLES],
+ cached_gas_id[gas_path],
+ cached_gas_name[gas_path],
+ amount,
))
- for(var/datum/gas_reaction/reaction_result as anything in gasmix.reaction_results)
+ for(var/datum/gas_reaction/standard/reaction_result as anything in gasmix.reaction_results)
.["reactions"] += list(list(
initial(reaction_result.id),
initial(reaction_result.name),
@@ -75,17 +77,18 @@ GLOBAL_LIST_EMPTY(gas_handbook)
for (var/datum/gas/gas_path as anything in subtypesof(/datum/gas))
var/list/gas_info = list()
- var/list/meta_information = GLOB.meta_gas_info[gas_path]
- if(!meta_information)
+ var/list/meta_information = GLOB.meta_gas_info
+ if(!meta_information[META_GAS_ID])
continue
- gas_info["id"] = meta_information[META_GAS_ID]
- gas_info["name"] = meta_information[META_GAS_NAME]
- gas_info["description"] = meta_information[META_GAS_DESC]
- gas_info["specific_heat"] = meta_information[META_GAS_SPECIFIC_HEAT]
+ gas_info["id"] = meta_information[META_GAS_ID][gas_path]
+ gas_info["name"] = meta_information[META_GAS_NAME][gas_path]
+ gas_info["description"] = meta_information[META_GAS_DESC][gas_path]
+ gas_info["specific_heat"] = meta_information[META_GAS_SPECIFIC_HEAT][gas_path]
+ gas_info["export_value"] = (gas_path::cargo_flags & GAS_EXPORTABLE) ? gas_path::base_value : 0
gas_info["reactions"] = list()
momentary_gas_list[gas_path] = gas_info
- for (var/datum/gas_reaction/reaction_path as anything in subtypesof(/datum/gas_reaction))
+ for (var/datum/gas_reaction/standard/reaction_path as anything in valid_subtypesof(/datum/gas_reaction))
var/datum/gas_reaction/reaction = new reaction_path
var/list/reaction_info = list()
reaction_info["id"] = reaction.id
@@ -107,9 +110,9 @@ GLOBAL_LIST_EMPTY(gas_handbook)
if(factor == "Temperature" || factor == "Pressure")
factor_info["tooltip"] = "Reaction is influenced by the [LOWER_TEXT(factor)] of the place where the reaction is occuring."
else if(factor == "Energy")
- factor_info["tooltip"] = "Energy released by the reaction, may or may not result in linear temperature change depending on a slew of other factors."
+ factor_info["tooltip"] = "Energy released by the reaction. May or may not result in linear temperature change depending on a slew of other factors."
else if(factor == "Radiation")
- factor_info["tooltip"] = "This reaction emits dangerous radiation! Take precautions."
+ factor_info["tooltip"] = "This reaction emits hazardous radiation - take precautions."
else if (factor == "Location")
factor_info["tooltip"] = "This reaction has special behaviour when occuring in specific locations."
else if(factor == "Hot Ice")
@@ -118,44 +121,13 @@ GLOBAL_LIST_EMPTY(gas_handbook)
GLOB.reaction_handbook += list(reaction_info)
qdel(reaction)
- for (var/datum/electrolyzer_reaction/reaction_path as anything in subtypesof(/datum/electrolyzer_reaction))
- var/datum/electrolyzer_reaction/reaction = new reaction_path
- var/list/reaction_info = list()
- reaction_info["id"] = reaction.id
- reaction_info["name"] = reaction.name
- reaction_info["description"] = reaction.desc
- reaction_info["factors"] = list()
- for (var/factor in reaction.factor)
- var/list/factor_info = list()
- factor_info["desc"] = reaction.factor[factor]
-
- if(factor in momentary_gas_list)
- momentary_gas_list[factor]["reactions"] += list(reaction.id = reaction.name)
- factor_info["factor_id"] = momentary_gas_list[factor]["id"] //Gas id
- factor_info["factor_type"] = "gas"
- factor_info["factor_name"] = momentary_gas_list[factor]["name"] //Common name
- else
- factor_info["factor_name"] = factor
- factor_info["factor_type"] = "misc"
- if(factor == "Temperature" || factor == "Pressure")
- factor_info["tooltip"] = "Reaction is influenced by the [LOWER_TEXT(factor)] of the place where the reaction is occuring."
- else if(factor == "Energy")
- factor_info["tooltip"] = "Energy released by the reaction, may or may not result in linear temperature change depending on a slew of other factors."
- else if(factor == "Radiation")
- factor_info["tooltip"] = "This reaction emits dangerous radiation! Take precautions."
- else if (factor == "Location")
- factor_info["tooltip"] = "This reaction has special behaviour when occuring in specific locations."
- reaction_info["factors"] += list(factor_info)
- GLOB.reaction_handbook += list(reaction_info)
- qdel(reaction)
-
for (var/gas_info_index in momentary_gas_list)
GLOB.gas_handbook += list(momentary_gas_list[gas_info_index])
/// Returns an assoc list of the gas handbook and the reaction handbook.
/// For UIs, simply do data += return_atmos_handbooks() to use.
/proc/return_atmos_handbooks()
- return list("gasInfo" = GLOB.gas_handbook, "reactionInfo" = GLOB.reaction_handbook)
+ return list("gasInfo" = GLOB.gas_handbook, "reactionInfo" = GLOB.reaction_handbook, "moneySymbol" = MONEY_SYMBOL, "moneyName" = MONEY_NAME)
/proc/extract_id_tags(list/objects)
var/list/tags = list()
@@ -172,37 +144,9 @@ GLOBAL_LIST_EMPTY(gas_handbook)
return null
-/**
- * A simple helped proc that checks if the contents of a list of gases are within acceptable terms.
- *
- * Arguments:
- * * gases: The list of gases which contents are being checked
- * * gases to check: An associated list of gas types and acceptable boundaries in moles. e.g. /datum/gas/oxygen = list(16, 30)
- * * * if the assoc list is null, then it'll be considered a safe gas and won't return FALSE.
- * * extraneous_gas_limit: If a gas not in gases is found, this is the limit above which the proc will return FALSE.
- */
-/proc/check_gases(list/gases, list/gases_to_check, extraneous_gas_limit = 0.1)
- gases_to_check = gases_to_check.Copy()
- for(var/id in gases)
- var/gas_moles = gases[id][MOLES]
- if(!(id in gases_to_check))
- if(gas_moles > extraneous_gas_limit)
- return FALSE
- continue
- var/list/boundaries = gases_to_check[id]
- if(boundaries && !ISINRANGE(gas_moles, boundaries[1], boundaries[2]))
- return FALSE
- gases_to_check -= id
- ///Check that gases absent from the turf have a lower boundary of zero or none at all, otherwise return FALSE
- for(var/id in gases_to_check)
- var/list/boundaries = gases_to_check[id]
- if(boundaries && boundaries[1] > 0)
- return FALSE
- return TRUE
-
/proc/print_gas_mixture(datum/gas_mixture/gas_mixture)
var/message = "TEMPERATURE: [gas_mixture.temperature]K, QUANTITY: [gas_mixture.total_moles()] mols, VOLUME: [gas_mixture.volume]L; "
- for(var/key in gas_mixture.gases)
- var/list/gaslist = gas_mixture.gases[key]
- message += "[gaslist[GAS_META][META_GAS_ID]]=[gaslist[MOLES]] mols;"
+ var/list/cached_gas_id = GAS_META[META_GAS_ID]
+ for(var/gas_id, amount in gas_mixture.moles)
+ message += "[cached_gas_id[gas_id]]=[amount] mols;"
return message
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 3f2cacaf1045..cccad01091eb 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -164,7 +164,6 @@ GLOBAL_LIST_INIT(WALLITEMS_INTERIOR, typecacheof(list(
/obj/item/radio/intercom,
/obj/structure/secure_safe,
/obj/machinery/airalarm,
- /obj/machinery/bluespace_vendor,
/obj/machinery/button,
/obj/machinery/computer/security/telescreen,
/obj/machinery/computer/security/telescreen/entertainment,
diff --git a/code/__HELPERS/logging/atmos.dm b/code/__HELPERS/logging/atmos.dm
index 644c9e656257..9e2a76f648a7 100644
--- a/code/__HELPERS/logging/atmos.dm
+++ b/code/__HELPERS/logging/atmos.dm
@@ -2,26 +2,27 @@
/proc/log_atmos(text, datum/gas_mixture/gas_mixture)
var/message = "[text]\"[print_gas_mixture(gas_mixture)]\""
//Cache commonly accessed information.
- var/list/gases = gas_mixture.gases //List of gas datum paths that are associated with a list of information related to the gases.
+ var/list/cached_moles = gas_mixture.moles
var/heat_capacity = gas_mixture.heat_capacity()
var/temperature = gas_mixture.return_temperature()
var/thermal_energy = temperature * heat_capacity
var/volume = gas_mixture.return_volume()
var/pressure = gas_mixture.return_pressure()
var/total_moles = gas_mixture.total_moles()
+ var/list/cached_specific_heat = GAS_META[META_GAS_SPECIFIC_HEAT]
+ var/list/cached_gas_name = GAS_META[META_GAS_NAME]
///The total value of the gas mixture in credits.
var/total_value = 0
var/list/specific_gas_data = list()
//Gas specific information assigned to each gas.
- for(var/datum/gas/gas_path as anything in gases)
- var/list/gas = gases[gas_path]
- var/moles = gas[MOLES]
+ for(var/datum/gas/gas_path as anything in cached_moles)
+ var/moles = cached_moles[gas_path]
var/composition = moles / total_moles
- var/energy = temperature * moles * gas[GAS_META][META_GAS_SPECIFIC_HEAT]
+ var/energy = temperature * moles * cached_specific_heat[gas_path]
var/value = initial(gas_path.base_value) * moles
total_value += value
- specific_gas_data[gas[GAS_META][META_GAS_NAME]] = list(
+ specific_gas_data[cached_gas_name[gas_path]] = list(
"moles" = moles,
"composition" = composition,
"molar concentration" = moles / volume,
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index 2dc59f0c71ea..57b8737aeb82 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -563,9 +563,8 @@ SUBSYSTEM_DEF(air)
// If it's already been processed, then it's already talked to us
if(enemy_tile.current_cycle == -INFINITE)
continue
- // .air instead of .return_air() because we can guarentee that the proc won't do anything
- if(potential_diff.air.compare(enemy_tile.air))
- //testing("Active turf found. Return value of compare(): [T.air.compare(enemy_tile.air)]")
+ // .air instead of .return_air() because we can guarantee that the proc won't do anything
+ if(potential_diff.air.compare(enemy_tile.air, FALSE))
if(!potential_diff.excited)
potential_diff.excited = TRUE
SSair.active_turfs += potential_diff
@@ -785,7 +784,6 @@ GLOBAL_LIST_EMPTY(colored_images)
strings_to_mix["[gas_string]-[gastype]"] = canonical_mix
gas_string = preprocess_gas_string(gas_string)
- var/list/gases = canonical_mix.gases
var/list/gas = params2list(gas_string)
if(gas["TEMP"])
canonical_mix.temperature = text2num(gas["TEMP"])
@@ -793,12 +791,12 @@ GLOBAL_LIST_EMPTY(colored_images)
gas -= "TEMP"
else // if we do not have a temp in the new gas mix lets assume room temp.
canonical_mix.temperature = T20C
+ var/list/cached_moles = canonical_mix.moles
for(var/id in gas)
var/path = id
if(!ispath(path))
path = gas_id2path(path) //a lot of these strings can't have embedded expressions (especially for mappers), so support for IDs needs to stick around
- ADD_GAS(path, gases)
- gases[path][MOLES] = text2num(gas[id])
+ cached_moles[path] = text2num(gas[id])
if(istype(canonical_mix, /datum/gas_mixture/immutable))
return canonical_mix
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 66954fad4cc1..249faead6987 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -894,10 +894,9 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away
GLOB.starlight_objects += starlight_object(offset)
GLOB.starlight_overlays += starlight_overlay(offset)
- for(var/datum/gas/gas_type as anything in GLOB.meta_gas_info)
- var/list/gas_info = GLOB.meta_gas_info[gas_type]
+ for(var/datum/gas/gas_type as anything in GLOB.meta_gas_info[META_GAS_ID])
if(initial(gas_type.moles_visible) != null)
- gas_info[META_GAS_OVERLAY] += generate_gas_overlays(gen_from, new_offset, gas_type)
+ GLOB.meta_gas_info[META_GAS_OVERLAY][gas_type] += generate_gas_overlays(gen_from, new_offset, gas_type)
/datum/controller/subsystem/mapping/proc/create_plane_offsets(gen_from, new_offset)
for(var/plane_offset in gen_from to new_offset)
diff --git a/code/datums/atmosphere/_atmosphere.dm b/code/datums/atmosphere/_atmosphere.dm
index 702a42e7ab07..37bdb954fa13 100644
--- a/code/datums/atmosphere/_atmosphere.dm
+++ b/code/datums/atmosphere/_atmosphere.dm
@@ -27,11 +27,10 @@
// First let's set up the gasmix and base gases for this template
// We make the string from a gasmix in this proc because gases need to calculate their pressure
var/datum/gas_mixture/gasmix = new
- var/list/gaslist = gasmix.gases
gasmix.temperature = rand(minimum_temp, maximum_temp)
+ var/list/cached_moles = gasmix.moles
for(var/i in base_gases)
- ADD_GAS(i, gaslist)
- gaslist[i][MOLES] = base_gases[i]
+ cached_moles[i] = base_gases[i]
// Now let the random choices begin
var/datum/gas/gastype
@@ -49,22 +48,21 @@
amount *= pressure_scalar // If we pick a really small target pressure we want roughly the same mix but less of it all
amount = CEILING(amount, 0.1)
- ASSERT_GAS_IN_LIST(gastype, gaslist)
- gaslist[gastype][MOLES] += amount
+ cached_moles[gastype] += amount
// Ensure that minimum_pressure is actually a hard lower limit
- target_pressure = clamp(target_pressure, minimum_pressure + (gaslist[gastype][MOLES] * 0.1), maximum_pressure)
+ target_pressure = clamp(target_pressure, minimum_pressure + (cached_moles[gastype] * 0.1), maximum_pressure)
// That last one put us over the limit, remove some of it
while(gasmix.return_pressure() > target_pressure)
- gaslist[gastype][MOLES] -= gaslist[gastype][MOLES] * 0.1
- gaslist[gastype][MOLES] = FLOOR(gaslist[gastype][MOLES], 0.1)
+ cached_moles[gastype] -= cached_moles[gastype] * 0.1
+ cached_moles[gastype] = FLOOR(cached_moles[gastype], 0.1)
gasmix.garbage_collect()
// Now finally lets make that string
var/list/gas_string_builder = list()
- for(var/i in gaslist)
- var/list/gas = gaslist[i]
- gas_string_builder += "[gas[GAS_META][META_GAS_ID]]=[gas[MOLES]]"
+ var/list/cached_gas_id = GAS_META[META_GAS_ID]
+ for(var/gas_id, gas_amount in cached_moles)
+ gas_string_builder += "[cached_gas_id[gas_id]]=[gas_amount]"
gas_string_builder += "TEMP=[gasmix.temperature]"
gas_string = gas_string_builder.Join(";")
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index ea07db402fc1..1841673b07ac 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -76,7 +76,7 @@
see_invisible = SEE_INVISIBLE_LIVING
invisibility = INVISIBILITY_MAXIMUM
has_emotes = TRUE
- var/icon/human_image
+ var/icon/human_icon
var/image/current_image
var/hidden = FALSE
var/move_delay = 0
@@ -129,7 +129,7 @@
gender = pick(MALE, FEMALE)
real_name = generate_random_name_species_based(gender, FALSE, /datum/species/human)
name = real_name
- human_image = get_flat_human_icon(null, pick(SSjob.joinable_occupations))
+ human_icon = get_flat_human_icon(null, pick(SSjob.joinable_occupations))
Show()
/**
@@ -160,11 +160,11 @@
appearance_job = SSjob.get_job(JOB_ASSISTANT)
if(istype(appearance_job, /datum/job/ai))
- human_image = icon('icons/mob/silicon/ai.dmi', icon_state = resolve_ai_icon(appearance_from_prefs.read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH)
+ human_icon = icon('icons/mob/silicon/ai.dmi', icon_state = resolve_ai_icon(appearance_from_prefs.read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH)
else if(istype(appearance_job, /datum/job/cyborg))
- human_image = icon('icons/mob/silicon/robots.dmi', icon_state = "robot")
+ human_icon = icon('icons/mob/silicon/robots.dmi', icon_state = "robot")
else
- human_image = get_flat_human_icon(null, appearance_job, appearance_from_prefs)
+ human_icon = get_flat_human_icon(null, appearance_job, appearance_from_prefs)
Show()
/// Returns all member clients of the imaginary_group
@@ -184,7 +184,7 @@
remove_image_from_clients(current_image, friend_clients)
//Generate image from the static icon and the current dir
- current_image = image(human_image, src, , MOB_LAYER, dir=src.dir)
+ current_image = image(human_icon, src, , MOB_LAYER, dir=src.dir)
current_image.override = TRUE
current_image.name = name
if(hidden)
@@ -198,9 +198,9 @@
/mob/camera/imaginary_friend/Destroy()
if(owner?.client)
- owner.client.images.Remove(human_image)
+ owner.client.images.Remove(human_icon)
if(client)
- client.images.Remove(human_image)
+ client.images.Remove(human_icon)
owner.imaginary_group -= src
return ..()
@@ -529,4 +529,4 @@
/mob/camera/imaginary_friend/trapped/setup_friend()
real_name = "[owner.real_name]?"
name = real_name
- human_image = icon('icons/mob/simple/lavaland/lavaland_monsters.dmi', icon_state = "curseblob")
+ human_icon = icon('icons/mob/simple/lavaland/lavaland_monsters.dmi', icon_state = "curseblob")
diff --git a/code/datums/communications.dm b/code/datums/communications.dm
index 91ccc8369374..816aa05dbbfe 100644
--- a/code/datums/communications.dm
+++ b/code/datums/communications.dm
@@ -27,6 +27,9 @@ GLOBAL_DATUM_INIT(communications_controller, /datum/communciations_controller, n
/// What is the higher bound of when the roundstart announcement is sent out?
var/waittime_h = 180 SECONDS
+ /// Tracks if we have announced greenshift at the start of the round or not
+ var/announced_greenshift = FALSE
+
/datum/communciations_controller/proc/can_announce(mob/living/user, is_silicon)
if(is_silicon && COOLDOWN_FINISHED(src, silicon_message_cooldown))
return TRUE
@@ -96,6 +99,7 @@ GLOBAL_DATUM_INIT(communications_controller, /datum/communciations_controller, n
if(greenshift)
station_goal_strings += "All special orders have been authorized for the shift. \
Feel free to pick one your crew wishes to specialize in - you are not expected to complete them all."
+ announced_greenshift = TRUE
else
for(var/datum/station_goal/station_goal as anything in SSstation.get_station_goals())
diff --git a/code/datums/components/crafting/atmospheric.dm b/code/datums/components/crafting/atmospheric.dm
index 1d1e82e11c2e..12bdd0e663ee 100644
--- a/code/datums/components/crafting/atmospheric.dm
+++ b/code/datums/components/crafting/atmospheric.dm
@@ -1,14 +1,3 @@
-/datum/crafting_recipe/bluespace_vendor_mount
- name = "Bluespace Vendor Wall Mount"
- result = /obj/item/wallframe/bluespace_vendor_mount
- time = 6 SECONDS
- reqs = list(
- /obj/item/stack/sheet/iron = 15,
- /obj/item/stack/sheet/glass = 10,
- /obj/item/stack/cable_coil = 10,
- )
- category = CAT_ATMOSPHERIC
-
/datum/crafting_recipe/pipe
name = "Smart pipe fitting"
tool_behaviors = list(TOOL_WRENCH)
diff --git a/code/datums/components/food/germ_sensitive.dm b/code/datums/components/food/germ_sensitive.dm
index 36992b9e1169..cd8eadc01d43 100644
--- a/code/datums/components/food/germ_sensitive.dm
+++ b/code/datums/components/food/germ_sensitive.dm
@@ -5,9 +5,9 @@
/// Possible diseases
GLOBAL_LIST_INIT(floor_diseases, list(
- /datum/disease/advance/nebula_nausea = 2,
- /datum/disease/advance/gastritium = 2,
/datum/disease/advance/carpellosis = 1,
+ /datum/disease/advance/nebula_nausea = 2,
+ /datum/disease/gastritium = 2,
))
/// Makes items infective if left on floor, also sending corresponding signals to parent
diff --git a/code/datums/components/religious_tool.dm b/code/datums/components/religious_tool.dm
index 3c421a04aa30..3c5e226b1a54 100644
--- a/code/datums/components/religious_tool.dm
+++ b/code/datums/components/religious_tool.dm
@@ -25,13 +25,13 @@
var/list/rite_types_allowlist
/datum/component/religious_tool/Initialize(
- operation_flags = ALL,
- force_catalyst_afterattack = FALSE,
- after_sect_select_cb = null,
- catalyst_type = /obj/item/book/bible,
- charges = -1,
- rite_types_allowlist = null,
- )
+ operation_flags = ALL,
+ force_catalyst_afterattack = FALSE,
+ after_sect_select_cb = null,
+ catalyst_type = /obj/item/book/bible,
+ charges = -1,
+ rite_types_allowlist = null,
+)
. = ..()
SetGlobalToLocal() //attempt to connect on start in case one already exists!
src.operation_flags = operation_flags
@@ -44,8 +44,8 @@
RegisterSignal(SSdcs, COMSIG_RELIGIOUS_SECT_RESET, PROC_REF(on_sect_reset))
/datum/component/religious_tool/Destroy(force)
+ QDEL_NULL(performing_rite)
easy_access_sect = null
- performing_rite = null
catalyst_type = null
after_sect_select_cb = null
return ..()
@@ -166,22 +166,30 @@
if(rite_types_allowlist && !is_path_in_list(path, rite_types_allowlist))
to_chat(user, span_warning("This cannot perform that kind of rite."))
return
- if(performing_rite)
- to_chat(user, "There is a rite currently being performed here already.")
- return
if(!user.can_perform_action(parent, FORBID_TELEKINESIS_REACH))
to_chat(user,span_warning("You are not close enough to perform the rite."))
return
- performing_rite = new path(parent)
- if(!performing_rite.perform_rite(user, parent))
+ //we have a rite already, but we want to do a new one.
+ if(performing_rite && !ispath(performing_rite.type, path))
QDEL_NULL(performing_rite)
+ if(!performing_rite)
+ performing_rite = new path(parent)
+
+ if(!performing_rite.perform_rite(user, parent))
+ if(!(performing_rite.rite_flags & RITE_ALLOW_MULTIPLE_PERFORMS))
+ QDEL_NULL(performing_rite)
return
- performing_rite.invoke_effect(user, parent)
- easy_access_sect.adjust_favor(-performing_rite.favor_cost)
- if(performing_rite.auto_delete)
- QDEL_NULL(performing_rite)
- else
- performing_rite = null
+
+ if(performing_rite.invoke_effect(user, parent))
+ performing_rite.post_invoke_effects(user, parent)
+ easy_access_sect.adjust_favor(-performing_rite.favor_cost)
+
+ if(!(performing_rite.rite_flags & RITE_ALLOW_MULTIPLE_PERFORMS))
+ if(performing_rite.rite_flags & RITE_AUTO_DELETE)
+ QDEL_NULL(performing_rite)
+ else
+ performing_rite = null
+
if(charges)
charges--
if(!charges)
diff --git a/code/datums/components/self_ignition.dm b/code/datums/components/self_ignition.dm
index eda5f69f2f27..92ac74920bef 100644
--- a/code/datums/components/self_ignition.dm
+++ b/code/datums/components/self_ignition.dm
@@ -45,7 +45,7 @@
if (!environment?.total_moles())
return
- if(environment.gases[/datum/gas/hypernoblium] && environment.gases[/datum/gas/hypernoblium][MOLES] >= 5)
+ if(environment.moles[/datum/gas/hypernoblium] >= 5)
if(owner.on_fire && owner.fire_stacks > 0)
owner.adjust_fire_stacks(-fire_stacks_loss * seconds_per_tick)
return
@@ -55,7 +55,7 @@
active_burning = TRUE
- if(!environment.gases[/datum/gas/oxygen] || environment.gases[/datum/gas/oxygen][MOLES] < 1) //Same threshhold that extinguishes fire
+ if(environment.moles[/datum/gas/oxygen] < 1) //Same threshhold that extinguishes fire
return
owner.adjust_fire_stacks(fire_stacks_per_second * seconds_per_tick)
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index cd69e05d64db..016fbfd78f15 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -609,6 +609,9 @@
if(!kevved)
return
+ if(prob(2 * owner.get_skill_modifier(/datum/skill/athletics, SKILL_RANDS_MODIFIER)))
+ return
+
var/list/messes = list()
// we split the mess-making into two parts (check what we're gonna send flying, intermission for dealing with the tackler, then actually send stuff flying) for the benefit of making sure the face-slam text
diff --git a/code/datums/diseases/advance/floor_diseases/gastritium.dm b/code/datums/diseases/advance/floor_diseases/gastritium.dm
index f012f4515f2c..be7573e9c87e 100644
--- a/code/datums/diseases/advance/floor_diseases/gastritium.dm
+++ b/code/datums/diseases/advance/floor_diseases/gastritium.dm
@@ -1,10 +1,12 @@
/// Caused by dirty food. Makes you burp out Tritium, sometimes burning hot!
-/datum/disease/advance/gastritium
+/datum/disease/gastritium
name = "Gastritium"
desc = "If left untreated, may manifest in severe Tritium heartburn."
- form = "Infection"
+ form = "Bacteria"
agent = "Atmobacter Polyri"
- cures = list(/datum/reagent/firefighting_foam)
+ cure_text = /datum/reagent/consumable/milk::name
+ spread_text = "None"
+ cures = list(/datum/reagent/consumable/milk)
viable_mobtypes = list(/mob/living/carbon/human)
spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS
severity = DISEASE_SEVERITY_HARMFUL
@@ -13,19 +15,7 @@
/// The chance of burped out tritium to be hot during max stage
var/tritium_burp_hot_chance = 10
-/datum/disease/advance/gastritium/New()
- symptoms = list(new/datum/symptom/fever)
- ..()
-
-/datum/disease/advance/gastritium/generate_cure()
- cures = list(pick(cures))
- var/datum/reagent/cure = GLOB.chemical_reagents_list[cures[1]]
- cure_text = cure.name
-
-/datum/disease/advance/gastritium/GetDiseaseID()
- return "[type]"
-
-/datum/disease/advance/gastritium/stage_act(seconds_per_tick, times_fired)
+/datum/disease/gastritium/stage_act(seconds_per_tick)
. = ..()
if(!.)
return
@@ -50,13 +40,18 @@
else if(SPT_PROB(1, seconds_per_tick))
tritium_burp(hot_chance = TRUE)
-/datum/disease/advance/gastritium/proc/tritium_burp(hot_chance = FALSE)
+ affected_mob.add_homeostasis_level(type, affected_mob.bodytemp_heat_damage_limit - 5 KELVIN, 0.5)
+
+/datum/disease/gastritium/remove_disease()
+ affected_mob.remove_homeostasis_level(type)
+ return ..()
+
+/datum/disease/gastritium/proc/tritium_burp(hot_chance = FALSE)
var/datum/gas_mixture/burp = new
- ADD_GAS(/datum/gas/tritium, burp.gases)
- burp.gases[/datum/gas/tritium][MOLES] = MOLES_GAS_VISIBLE
+ burp.set_gas(/datum/gas/tritium, MOLES_GAS_VISIBLE)
burp.temperature = affected_mob.body_temperature
if(hot_chance && prob(tritium_burp_hot_chance))
- burp.temperature = TRITIUM_MINIMUM_BURN_TEMPERATURE
+ burp.set_temperature(TRITIUM_MINIMUM_BURN_TEMPERATURE)
if(affected_mob.stat == CONSCIOUS)
to_chat(affected_mob, span_warning("Your throat feels hot!"))
affected_mob.visible_message("burps out green gas.", visible_message_flags = EMOTE_MESSAGE)
diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm
index 9f273353461d..62908afd6ce8 100644
--- a/code/datums/diseases/advance/symptoms/heal.dm
+++ b/code/datums/diseases/advance/symptoms/heal.dm
@@ -542,8 +542,6 @@
/datum/symptom/heal/plasma/CanHeal(datum/disease/advance/advanced_disease)
var/mob/living/carbon/infected_mob = advanced_disease.affected_mob
var/datum/gas_mixture/environment
- var/list/gases
-
. = 0
// Check internals
@@ -554,16 +552,14 @@
if(internals_tank)
var/datum/gas_mixture/tank_contents = internals_tank.return_air()
if(tank_contents && round(tank_contents.return_pressure())) // make sure the tank is not empty or 0 pressure
- if(tank_contents.gases[/datum/gas/plasma])
+ if(tank_contents.moles[/datum/gas/plasma])
// higher tank distribution pressure leads to more healing, but once you get to about 15kpa you reach the max
. += power * min(MAX_HEAL_COEFFICIENT_INTERNALS, internals_tank.distribute_pressure * HEALING_PER_BREATH_PRESSURE)
else // Check environment
if(infected_mob.loc)
environment = infected_mob.loc.return_air()
- if(environment)
- gases = environment.gases
- if(gases[/datum/gas/plasma])
- . += power * min(MAX_HEAL_COEFFICIENT_INTERNALS, gases[/datum/gas/plasma][MOLES] * HEALING_PER_MOL)
+ if(environment && environment.moles[/datum/gas/plasma])
+ . += power * min(MAX_HEAL_COEFFICIENT_INTERNALS, environment.moles[/datum/gas/plasma] * HEALING_PER_MOL)
// Check for reagents in bloodstream
if(infected_mob.reagents.has_reagent(/datum/reagent/toxin/plasma, needs_metabolizing = TRUE))
diff --git a/code/datums/elements/atmos_requirements.dm b/code/datums/elements/atmos_requirements.dm
index 1ac10f020274..378d12567678 100644
--- a/code/datums/elements/atmos_requirements.dm
+++ b/code/datums/elements/atmos_requirements.dm
@@ -45,13 +45,13 @@
if(!open_turf.air && (atmos_requirements["min_oxy"] || atmos_requirements["min_tox"] || atmos_requirements["min_n2"] || atmos_requirements["min_co2"]))
return FALSE
- var/open_turf_gases = open_turf.air.gases
+ var/open_turf_moles = open_turf.air.moles
open_turf.air.assert_gases(/datum/gas/oxygen, /datum/gas/pluoxium, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, /datum/gas/plasma)
- var/plas = open_turf_gases[/datum/gas/plasma][MOLES]
- var/oxy = open_turf_gases[/datum/gas/oxygen][MOLES] + (open_turf_gases[/datum/gas/pluoxium][MOLES] * PLUOXIUM_PROPORTION)
- var/n2 = open_turf_gases[/datum/gas/nitrogen][MOLES]
- var/co2 = open_turf_gases[/datum/gas/carbon_dioxide][MOLES]
+ var/plas = open_turf_moles[/datum/gas/plasma]
+ var/oxy = open_turf_moles[/datum/gas/oxygen] + (open_turf_moles[/datum/gas/pluoxium] * PLUOXIUM_PROPORTION)
+ var/n2 = open_turf_moles[/datum/gas/nitrogen]
+ var/co2 = open_turf_moles[/datum/gas/carbon_dioxide]
open_turf.air.garbage_collect()
diff --git a/code/datums/elements/death_gases.dm b/code/datums/elements/death_gases.dm
index b93c870f817f..7589e7194158 100644
--- a/code/datums/elements/death_gases.dm
+++ b/code/datums/elements/death_gases.dm
@@ -30,7 +30,7 @@
SIGNAL_HANDLER
var/datum/gas_mixture/mix_to_spawn = new()
mix_to_spawn.add_gas(gas_type)
- mix_to_spawn.gases[gas_type][MOLES] = amount_of_gas
- mix_to_spawn.temperature = T20C
+ mix_to_spawn.set_gas(gas_type, amount_of_gas)
+ mix_to_spawn.set_temperature(T20C)
var/turf/open/our_turf = get_turf(target)
our_turf.assume_air(mix_to_spawn)
diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm
index bee48a2bb493..b2fe45e94e3e 100644
--- a/code/datums/helper_datums/teleport.dm
+++ b/code/datums/helper_datums/teleport.dm
@@ -132,13 +132,12 @@
if(!floor_gas_mixture)
return
- var/list/floor_gases = floor_gas_mixture.gases
var/static/list/gases_to_check = list(
/datum/gas/oxygen = list(16, 100),
/datum/gas/nitrogen,
/datum/gas/carbon_dioxide = list(0, 10)
)
- if(!check_gases(floor_gases, gases_to_check))
+ if(!floor_gas_mixture.check_gases(gases_to_check))
return FALSE
// Aim for goldilocks temperatures and pressure
diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm
index b34b79bc5bf0..2ddf78439cd5 100644
--- a/code/datums/materials/basemats.dm
+++ b/code/datums/materials/basemats.dm
@@ -336,7 +336,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
///RPG Magic.
/datum/material/mythril
name = "mythril"
- desc = "How this even exists is byond me"
+ desc = "How this even exists is byond me."
color = "#f2d5d7"
greyscale_colors = "#f2d5d7"
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
@@ -365,7 +365,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
//formed when freon react with o2, emits a lot of plasma when heated
/datum/material/hot_ice
name = "hot ice"
- desc = "A weird kind of ice, feels warm to the touch"
+ desc = "A crystalline solid formed when Freon reacts with Oxygen. Extremely flammable, and will easily combust when exposed to heat."
color = "#88cdf1"
greyscale_colors = "#88cdf196"
alpha = 150
@@ -389,8 +389,8 @@ Unless you know what you're doing, only use the first three numbers. They're in
return TRUE
/datum/material/metalhydrogen
- name = "Metal Hydrogen"
- desc = "Solid metallic hydrogen. Some say it should be impossible"
+ name = "metal hydrogen"
+ desc = "Hydrogen in a metallic state, formed under extreme pressure. Some say achieving this state is impossible."
color = "#f2d5d7"
greyscale_colors = "#f2d5d796"
alpha = 150
@@ -561,7 +561,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
/datum/material/zaukerite
name = "zaukerite"
- desc = "A light absorbing crystal"
+ desc = "A light absorbing crystal formed out of Zauker. Vaguely toxic, like the gas itself."
color = COLOR_ALMOST_BLACK
greyscale_colors = COLOR_ALMOST_BLACK
categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE, MAT_CATEGORY_ITEM_MATERIAL=TRUE)
diff --git a/code/datums/mutations/olfaction.dm b/code/datums/mutations/olfaction.dm
index fb7c9a92e900..2fd8658d52c9 100644
--- a/code/datums/mutations/olfaction.dm
+++ b/code/datums/mutations/olfaction.dm
@@ -50,9 +50,8 @@
. = ..()
// Can we sniff? is there miasma in the air?
var/datum/gas_mixture/air = cast_on.loc.return_air()
- var/list/cached_gases = air.gases
- if(cached_gases[/datum/gas/miasma])
+ if(air.moles[/datum/gas/miasma])
cast_on.adjust_disgust(sensitivity * 45)
to_chat(cast_on, span_warning("With your overly sensitive nose, \
you get a whiff of stench and feel sick! Try moving to a cleaner area!"))
diff --git a/code/datums/quirks/negative_quirks/chronic_illness.dm b/code/datums/quirks/negative_quirks/chronic_illness.dm
index 573e5babde5d..d8c3bc375374 100644
--- a/code/datums/quirks/negative_quirks/chronic_illness.dm
+++ b/code/datums/quirks/negative_quirks/chronic_illness.dm
@@ -9,9 +9,9 @@
hardcore_value = 12
mail_goodies = list(/obj/item/storage/pill_bottle/sansufentanyl)
-/datum/quirk/item_quirk/chronic_illness/add_unique(client/client_source)
- var/datum/disease/chronic_illness/hms = new /datum/disease/chronic_illness()
- quirk_holder.ForceContractDisease(hms)
+/datum/quirk/item_quirk/chronic_illness/add(client/client_source)
+ var/datum/disease/chronic_illness/hms = new()
+ quirk_holder.ForceContractDisease(hms, make_copy = FALSE, del_on_fail = TRUE)
/datum/quirk/item_quirk/chronic_illness/add_unique(client/client_source)
give_item_to_holder(/obj/item/storage/pill_bottle/sansufentanyl, list(LOCATION_BACKPACK = ITEM_SLOT_BACKPACK), flavour_text = "You've been provided with medication to help manage your condition. Take it regularly to avoid complications.", notify_player = TRUE)
diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm
index eb0d48faacfd..45df61c0fd1e 100644
--- a/code/datums/status_effects/debuffs/fire_stacks.dm
+++ b/code/datums/status_effects/debuffs/fire_stacks.dm
@@ -155,7 +155,7 @@
return TRUE
var/datum/gas_mixture/air = owner.loc.return_air()
- if(!air.gases[/datum/gas/oxygen] || air.gases[/datum/gas/oxygen][MOLES] < 1)
+ if(air.moles[/datum/gas/oxygen] < 1)
qdel(src)
return TRUE
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index af5fefed3880..e969e2ed5763 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -168,35 +168,72 @@
/datum/wound/blunt/bone/proc/carbon_step(datum/source)
SIGNAL_HANDLER
- if(limb.body_zone != BODY_ZONE_L_LEG && limb.body_zone != BODY_ZONE_R_LEG)
- return
- if(victim.body_position == LYING_DOWN || victim.buckled) // wheelchair = fine, being pulled = not fine
- return
- if(victim.has_status_effect(/datum/status_effect/determined))
- return
footstep_counter += 1
if(footstep_counter >= 8)
footstep_counter = 1
- if(limb.get_splint_factor() <= 0.75 || !CAN_FEEL_PAIN(victim))
+ // crawling
+ if(victim.body_position == LYING_DOWN)
+ // crawling, and we're not dealing with broken arms or ribs - skip
+ if(limb.body_zone == BODY_ZONE_CHEST)
+ pass()
+ else if(limb.body_zone != SELECT_LEFT_OR_RIGHT(footstep_counter, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+ return
+
+ // walking
+ else
+ // walking, and we're not dealing with broken legs or ribs - skip
+ if(limb.body_zone == BODY_ZONE_CHEST)
+ pass()
+ else if(limb.body_zone != SELECT_LEFT_OR_RIGHT(footstep_counter, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
+ return
+
+ // secured via something like a wheelchair or gurney
+ if(isobj(victim.buckled))
return
- if(limb.body_zone == SELECT_LEFT_OR_RIGHT(footstep_counter, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
+
+ // adrenaline rush
+ if(victim.has_status_effect(/datum/status_effect/determined))
return
- var/mod = 1
+
+ if(!CAN_FEEL_PAIN(victim))
+ return
+
+ var/splint_factor = limb.get_splint_factor()
+ var/mod = 1 - splint_factor
switch(victim.move_intent)
if(MOVE_INTENT_RUN)
- mod = 1.5
+ mod += 0.5
if(MOVE_INTENT_WALK)
- mod = 1
+ pass()
if(MOVE_INTENT_SNEAK)
- mod = 0.5
+ mod -= 0.5
+ // since chest can trigger on both left and right steps, halve the chance to be fair
+ if(limb.body_zone == BODY_ZONE_CHEST)
+ mod *= 0.5
+
if(!prob(severity * mod * 20))
return
if(SEND_SIGNAL(victim, COMSIG_CARBON_PAINED_STEP, limb, footstep_counter) & STOP_PAIN)
return
- to_chat(victim, span_danger("Your [limb.plaintext_zone] [pick("aches", "pangs", "stings")] as you take a step!"))
- victim.sharp_pain(limb.body_zone, severity * 6, BRUTE, 10 SECONDS)
+ // we can be in one of t
+ var/situation = ""
+ // crawling or being dragged
+ if(victim.body_position == LYING_DOWN)
+ situation = isnull(victim.pulledby) ? "as you crawl" : "as you are dragged"
+ // being pulled/grabbed
+ else if(!isnull(victim.pulledby))
+ situation = "as you are pulled along"
+ // being fireman carried
+ else if(ismob(victim.buckled))
+ situation = "as you are carried limply"
+ // walking as normal
+ else
+ situation = "as you take a step"
+
+ to_chat(victim, span_danger("Your [limb.plaintext_zone] [pick("aches", "pangs", "stings")] [situation]!"))
+ victim.sharp_pain(limb.body_zone, severity * 6 * splint_factor, BRUTE, 10 SECONDS)
/datum/wound/blunt/bone/proc/breath(...)
SIGNAL_HANDLER
diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm
index 176ffecef0df..97f515c0559b 100644
--- a/code/game/area/areas/mining.dm
+++ b/code/game/area/areas/mining.dm
@@ -206,13 +206,18 @@
name = "Icemoon Wastes"
outdoors = TRUE
-/area/icemoon/surface/outdoors/nospawn // this is the area you use for stuff to not spawn, but if you still want weather.
+/// this is the area you use for stuff to not spawn, but if you still want weather.
+/area/icemoon/surface/outdoors/nospawn
-/area/icemoon/surface/outdoors/nospawn/New() // unless you roll forested trait lol
+// unless you roll forested trait lol (fuck you time green)
+/area/icemoon/surface/outdoors/nospawn/New()
. = ..()
+ // this area SOMETIMES does map generation. Often it doesn't at all
+ // so it SHOULD NOT be used with the genturf turf type, as it is not always replaced
if(HAS_TRAIT(SSstation, STATION_TRAIT_FORESTED))
map_generator = /datum/map_generator/cave_generator/icemoon/surface/forested
- area_flags = MOB_SPAWN_ALLOWED | FLORA_ALLOWED//flip this on, the generator has already disabled dangerous fauna
+ // flip this on, the generator has already disabled dangerous fauna
+ area_flags = MOB_SPAWN_ALLOWED | FLORA_ALLOWED
/area/icemoon/surface/outdoors/noteleport // for places like the cursed spring water
area_flags_mapping = parent_type::area_flags_mapping | NOTELEPORT
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index b8faa06c2c55..3ea1862706aa 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -379,11 +379,8 @@
return null
///Return the current air environment in this atom
-/atom/proc/return_air()
- if(loc)
- return loc.return_air()
- else
- return null
+/atom/proc/return_air() as /datum/gas_mixture
+ return loc?.return_air()
///Return the air if we can analyze it
/atom/proc/return_analyzable_air()
diff --git a/code/game/machinery/computer/atmos_computers/_atmos_control.dm b/code/game/machinery/computer/atmos_computers/_atmos_control.dm
index 094f12e36e36..49cdb7462a65 100644
--- a/code/game/machinery/computer/atmos_computers/_atmos_control.dm
+++ b/code/game/machinery/computer/atmos_computers/_atmos_control.dm
@@ -116,6 +116,7 @@
data["maxOutput"] = MAX_OUTPUT_PRESSURE
data["control"] = control
data["reconnecting"] = reconnecting
+ data["defaultGas"] = get_default_gas()
data += return_atmos_handbooks()
return data
@@ -229,6 +230,13 @@
return TRUE
+/obj/machinery/computer/atmos_control/proc/get_default_gas()
+ for(var/gas_path, gas_id in GLOB.meta_gas_info[META_GAS_ID])
+ if(gas_id == atmos_chambers[1])
+ return gas_id
+
+ return null
+
/obj/machinery/computer/atmos_control/nocontrol
control = FALSE
circuit = /obj/item/circuitboard/computer/atmos_control/nocontrol
diff --git a/code/game/machinery/computer/atmos_computers/air_sensors.dm b/code/game/machinery/computer/atmos_computers/air_sensors.dm
index 723ffacb2eb7..374459906a4a 100644
--- a/code/game/machinery/computer/atmos_computers/air_sensors.dm
+++ b/code/game/machinery/computer/atmos_computers/air_sensors.dm
@@ -47,7 +47,7 @@
chamber_id = ATMOS_GAS_MONITOR_H2
/obj/machinery/air_sensor/hypernoblium_tank
- name = "hypernoblium tank gas sensor"
+ name = "hyper-noblium tank gas sensor"
chamber_id = ATMOS_GAS_MONITOR_HYPERNOBLIUM
/obj/machinery/air_sensor/miasma_tank
@@ -83,7 +83,7 @@
chamber_id = ATMOS_GAS_MONITOR_HELIUM
/obj/machinery/air_sensor/antinoblium_tank
- name = "antinoblium tank gas sensor"
+ name = "anti-noblium tank gas sensor"
chamber_id = ATMOS_GAS_MONITOR_ANTINOBLIUM
/obj/machinery/air_sensor/incinerator_tank
diff --git a/code/game/machinery/computer/atmos_computers/atmos_controls.dm b/code/game/machinery/computer/atmos_computers/atmos_controls.dm
index bf386c83a1c3..76ccc228dfa6 100644
--- a/code/game/machinery/computer/atmos_computers/atmos_controls.dm
+++ b/code/game/machinery/computer/atmos_computers/atmos_controls.dm
@@ -4,122 +4,122 @@
atmos_chambers = list(ATMOS_GAS_MONITOR_DISTRO = "Distribution Loop", ATMOS_GAS_MONITOR_WASTE = "Waste Loop")
/obj/machinery/computer/atmos_control/oxygen_tank
- name = "Oxygen Supply Control"
+ name = "\improper Oxygen supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/oxygen_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_O2 = "Oxygen Supply")
/obj/machinery/computer/atmos_control/plasma_tank
- name = "Plasma Supply Control"
+ name = "\improper Plasma supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/plasma_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_PLAS = "Plasma Supply")
/obj/machinery/computer/atmos_control/air_tank
- name = "Mixed Air Supply Control"
+ name = "\improper mixed air supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/air_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_AIR = "Mixed Air Supply")
/obj/machinery/computer/atmos_control/nitrous_tank
- name = "Nitrous Oxide Supply Control"
+ name = "\improper Nitrous Oxide supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/nitrous_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_N2O = "Nitrous Oxide Supply")
/obj/machinery/computer/atmos_control/nitrogen_tank
- name = "Nitrogen Supply Control"
+ name = "\improper Nitrogen supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/nitrogen_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_N2 = "Nitrogen Supply")
/obj/machinery/computer/atmos_control/carbon_tank
- name = "Carbon Dioxide Supply Control"
+ name = "\improper Carbon Dioxide supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/carbon_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_CO2 = "Carbon Dioxide Supply")
/obj/machinery/computer/atmos_control/bz_tank
- name = "BZ Supply Control"
+ name = "\improper BZ supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/bz_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_BZ = "BZ Supply")
/obj/machinery/computer/atmos_control/freon_tank
- name = "Freon Supply Control"
+ name = "\improper Freon supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/freon_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_FREON = "Freon Supply")
/obj/machinery/computer/atmos_control/halon_tank
- name = "Halon Supply Control"
+ name = "\improper Halon supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/halon_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_HALON = "Halon Supply")
/obj/machinery/computer/atmos_control/healium_tank
- name = "Healium Supply Control"
+ name = "\improper Healium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/healium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_HEALIUM = "Healium Supply")
/obj/machinery/computer/atmos_control/hydrogen_tank
- name = "Hydrogen Supply Control"
+ name = "\improper Hydrogen supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/hydrogen_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_H2 = "Hydrogen Supply")
/obj/machinery/computer/atmos_control/hypernoblium_tank
- name = "Hypernoblium Supply Control"
+ name = "\improper Hyper-Noblium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/hypernoblium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_HYPERNOBLIUM = "Hypernoblium Supply")
/obj/machinery/computer/atmos_control/miasma_tank
- name = "Miasma Supply Control"
+ name = "\improper Miasma supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/miasma_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_MIASMA = "Miasma Supply")
/obj/machinery/computer/atmos_control/nitrium_tank
- name = "Nitrium Supply Control"
+ name = "\improper Nitrium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/nitrium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_NITRIUM = "Nitrium Supply")
/obj/machinery/computer/atmos_control/pluoxium_tank
- name = "Pluoxium Supply Control"
+ name = "\improper Pluoxium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/pluoxium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_PLUOXIUM = "Pluoxium Supply")
/obj/machinery/computer/atmos_control/proto_nitrate_tank
- name = "Proto-Nitrate Supply Control"
+ name = "\improper Proto-Nitrate supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/proto_nitrate_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_PROTO_NITRATE = "Proto-Nitrate Supply")
/obj/machinery/computer/atmos_control/tritium_tank
- name = "Tritium Supply Control"
+ name = "\improper Tritium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/tritium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_TRITIUM = "Tritium Supply")
/obj/machinery/computer/atmos_control/water_vapor
- name = "Water Vapor Supply Control"
+ name = "\improper Water Vapor supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/water_vapor
atmos_chambers = list(ATMOS_GAS_MONITOR_H2O = "Water Vapor Supply")
/obj/machinery/computer/atmos_control/zauker_tank
- name = "Zauker Supply Control"
+ name = "\improper Zauker supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/zauker_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_ZAUKER = "Zauker Supply")
/obj/machinery/computer/atmos_control/helium_tank
- name = "Helium Supply Control"
+ name = "\improper Helium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/helium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_HELIUM = "Helium Supply")
/obj/machinery/computer/atmos_control/antinoblium_tank
- name = "Antinoblium Supply Control"
+ name = "\improper Anti-Noblium supply control"
circuit = /obj/item/circuitboard/computer/atmos_control/antinoblium_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_ANTINOBLIUM = "Antinoblium Supply")
/obj/machinery/computer/atmos_control/mix_tank
- name = "Mix Chamber Control"
+ name = "mix chamber control"
circuit = /obj/item/circuitboard/computer/atmos_control/mix_tank
atmos_chambers = list(ATMOS_GAS_MONITOR_MIX = "Mix Chamber")
/obj/machinery/computer/atmos_control/nocontrol/incinerator
- name = "Incinerator Chamber Monitor"
+ name = "incinerator chamber monitor"
circuit = /obj/item/circuitboard/computer/atmos_control/nocontrol/incinerator
atmos_chambers = list(ATMOS_GAS_MONITOR_INCINERATOR = "Incinerator Chamber")
/obj/machinery/computer/atmos_control/ordnancemix
- name = "Ordnance Chamber Control"
+ name = "ordnance chamber control"
circuit = /obj/item/circuitboard/computer/atmos_control/ordnancemix
atmos_chambers = list(
ATMOS_GAS_MONITOR_ORDNANCE_BURN = "Ordnance Burn Chamber",
diff --git a/code/game/machinery/computer/atmos_computers/outlets.dm b/code/game/machinery/computer/atmos_computers/outlets.dm
index 529a83d72192..8a66516100d9 100644
--- a/code/game/machinery/computer/atmos_computers/outlets.dm
+++ b/code/game/machinery/computer/atmos_computers/outlets.dm
@@ -23,6 +23,7 @@
/obj/machinery/atmospherics/components/unary/vent_pump/siphon/monitored/mix_output
name = "mix tank output inlet"
chamber_id = ATMOS_GAS_MONITOR_MIX
+ on = FALSE
/obj/machinery/atmospherics/components/unary/vent_pump/siphon/monitored/nitrous_output
name = "nitrous oxide tank output inlet"
diff --git a/code/game/machinery/embedded_controller/airlock_controller.dm b/code/game/machinery/embedded_controller/airlock_controller.dm
index e82c102db219..6e30fad1a328 100644
--- a/code/game/machinery/embedded_controller/airlock_controller.dm
+++ b/code/game/machinery/embedded_controller/airlock_controller.dm
@@ -348,7 +348,7 @@
return ..()
/obj/machinery/airlock_controller/incinerator_ordmix
- name = "Incinerator Access Console"
+ name = "incinerator access console"
airpump_tag = INCINERATOR_ORDMIX_DP_VENTPUMP
exterior_door_tag = INCINERATOR_ORDMIX_AIRLOCK_EXTERIOR
id_tag = INCINERATOR_ORDMIX_AIRLOCK_CONTROLLER
@@ -359,7 +359,7 @@
exterior_target_pressure = 0
/obj/machinery/airlock_controller/incinerator_atmos
- name = "Incinerator Access Console"
+ name = "incinerator access console"
airpump_tag = INCINERATOR_ATMOS_DP_VENTPUMP
exterior_door_tag = INCINERATOR_ATMOS_AIRLOCK_EXTERIOR
id_tag = INCINERATOR_ATMOS_AIRLOCK_CONTROLLER
@@ -370,7 +370,7 @@
exterior_target_pressure = 0
/obj/machinery/airlock_controller/incinerator_syndicatelava
- name = "Incinerator Access Console"
+ name = "incinerator access console"
airpump_tag = INCINERATOR_SYNDICATELAVA_DP_VENTPUMP
exterior_door_tag = INCINERATOR_SYNDICATELAVA_AIRLOCK_EXTERIOR
id_tag = INCINERATOR_SYNDICATELAVA_AIRLOCK_CONTROLLER
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index 800b4b8c80fa..32eedf69cf69 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -167,7 +167,7 @@
leave_smell()
/obj/effect/decal/cleanable/vomit/proc/leave_smell()
- add_smell(smell = "vomit", intensity = SMELL_INTENSITY_STRONG, radius = 1)
+ add_smell(smell = /datum/smell/vomit, intensity = SMELL_INTENSITY_STRONG, radius = 1)
/obj/effect/decal/cleanable/vomit/attack_hand(mob/user, list/modifiers)
. = ..()
diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
index b0546ac3081d..913c4e96cf5a 100644
--- a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
@@ -250,10 +250,9 @@
QDEL_NULL(hotspot)
var/datum/gas_mixture/air = location.air
- var/list/gases = air.gases
- if (gases[/datum/gas/plasma])
- var/scrub_amt = min(30, gases[/datum/gas/plasma][MOLES]) //Absorb some plasma
- gases[/datum/gas/plasma][MOLES] -= scrub_amt
+ if (air.moles[/datum/gas/plasma])
+ var/scrub_amt = min(30, air.moles[/datum/gas/plasma]) //Absorb some plasma
+ air.adjust_gas(/datum/gas/plasma, -scrub_amt)
absorbed_plasma += scrub_amt
if (air.temperature > T20C)
air.temperature = max(air.temperature / 2, T20C)
@@ -416,6 +415,12 @@
alpha = 120
max_integrity = 10
pass_flags_self = PASSGLASS
+ var/static/list/ignored_gases = typecacheof(list(
+ /datum/gas/nitrogen,
+ /datum/gas/oxygen,
+ /datum/gas/pluoxium,
+ /datum/gas/halon,
+ ))
/obj/structure/foamedmetal/resin/Initialize(mapload)
. = ..()
@@ -430,13 +435,10 @@
for(var/obj/effect/hotspot/fire in location)
qdel(fire)
- var/list/gases = air.gases
- for(var/gas_type in gases)
- switch(gas_type)
- if(/datum/gas/oxygen, /datum/gas/nitrogen)
- continue
- else
- gases[gas_type][MOLES] = 0
+ var/list/cached_moles = air.moles
+ for(var/gas_id in cached_moles)
+ if(!(ignored_gases[gas_id]))
+ cached_moles[gas_id] = 0
air.garbage_collect()
for(var/obj/machinery/atmospherics/components/unary/comp in location)
diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm
index 95169137c08d..a8d891609d22 100644
--- a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm
+++ b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm
@@ -295,11 +295,10 @@
if(!distcheck || get_dist(location, chilly) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air
air.temperature = temperature
- var/list/gases = air.gases
- if(gases[/datum/gas/plasma])
- air.assert_gas(/datum/gas/nitrogen)
- gases[/datum/gas/nitrogen][MOLES] += gases[/datum/gas/plasma][MOLES]
- gases[/datum/gas/plasma][MOLES] = 0
+ if(air.moles[/datum/gas/plasma])
+ var/mole_count = air.moles[/datum/gas/plasma]
+ air.adjust_gas(/datum/gas/nitrogen, mole_count)
+ air.adjust_gas(/datum/gas/plasma, -mole_count)
air.garbage_collect()
for(var/obj/effect/hotspot/fire in chilly)
diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm
index 9d1da48cd290..c5c82b485e0e 100644
--- a/code/game/objects/effects/spawners/bombspawner.dm
+++ b/code/game/objects/effects/spawners/bombspawner.dm
@@ -44,14 +44,11 @@
if(!first_gasmix || !second_gasmix)
return
- first_gasmix.temperature = 1413
- second_gasmix.temperature = 141.3
+ first_gasmix.set_temperature(1413)
+ second_gasmix.set_temperature(141.3)
- first_gasmix.assert_gas(/datum/gas/plasma)
- second_gasmix.assert_gas(/datum/gas/oxygen)
-
- first_gasmix.gases[/datum/gas/plasma][MOLES] = calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1)
- second_gasmix.gases[/datum/gas/oxygen][MOLES] = calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1)
+ first_gasmix.set_gas(/datum/gas/plasma, calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1))
+ second_gasmix.set_gas(/datum/gas/oxygen, calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1))
/obj/effect/spawner/newbomb/tritium
@@ -60,16 +57,13 @@
if(!first_gasmix || !second_gasmix)
return
- first_gasmix.temperature = 8000
- second_gasmix.temperature = 43
+ first_gasmix.set_temperature(8000)
+ second_gasmix.set_temperature(43)
- first_gasmix.assert_gas(/datum/gas/plasma)
- second_gasmix.assert_gas(/datum/gas/oxygen)
- second_gasmix.assert_gas(/datum/gas/tritium)
+ first_gasmix.set_gas(/datum/gas/plasma, calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1))
- first_gasmix.gases[/datum/gas/plasma][MOLES] = calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1)
- second_gasmix.gases[/datum/gas/oxygen][MOLES] = 0.67 * calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1)
- second_gasmix.gases[/datum/gas/tritium][MOLES] = 0.33 * calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1)
+ second_gasmix.set_gas(/datum/gas/oxygen, 0.67 * calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1))
+ second_gasmix.set_gas(/datum/gas/tritium, 0.33 * calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1))
/obj/effect/spawner/newbomb/isolated_tritium
@@ -78,16 +72,13 @@
if(!first_gasmix || !second_gasmix)
return
- first_gasmix.temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 1
- second_gasmix.temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 1
+ first_gasmix.set_temperature(FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 1)
+ second_gasmix.set_temperature(FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 1)
- first_gasmix.assert_gas(/datum/gas/hypernoblium)
- first_gasmix.assert_gas(/datum/gas/tritium)
- second_gasmix.assert_gas(/datum/gas/oxygen)
+ first_gasmix.set_gas(/datum/gas/hypernoblium, REACTION_OPPRESSION_THRESHOLD - 0.01,)
+ first_gasmix.set_gas( /datum/gas/tritium, 0.5 * calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1))
- first_gasmix.gases[/datum/gas/hypernoblium][MOLES] = REACTION_OPPRESSION_THRESHOLD - 0.01
- first_gasmix.gases[/datum/gas/tritium][MOLES] = 0.5 * calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1)
- second_gasmix.gases[/datum/gas/oxygen][MOLES] = calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE-1)
+ second_gasmix.set_gas(/datum/gas/oxygen, calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE-1))
/obj/effect/spawner/newbomb/noblium
@@ -96,14 +87,11 @@
if(!first_gasmix || !second_gasmix)
return
- first_gasmix.temperature = 2.7
- second_gasmix.temperature = 2.7
-
- first_gasmix.assert_gas(/datum/gas/nitrogen)
- second_gasmix.assert_gas(/datum/gas/tritium)
+ first_gasmix.set_temperature(2.7)
+ second_gasmix.set_temperature(2.7)
- first_gasmix.gases[/datum/gas/nitrogen][MOLES] = calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1)
- second_gasmix.gases[/datum/gas/tritium][MOLES] = calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1)
+ first_gasmix.set_gas(/datum/gas/nitrogen, calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1))
+ second_gasmix.set_gas(/datum/gas/tritium, calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1))
/obj/effect/spawner/newbomb/pressure
@@ -112,11 +100,8 @@
if(!first_gasmix || !second_gasmix)
return
- first_gasmix.temperature = 20000
- second_gasmix.temperature = 2.7
-
- first_gasmix.assert_gas(/datum/gas/hypernoblium)
- second_gasmix.assert_gas(/datum/gas/tritium)
+ first_gasmix.set_temperature(20000)
+ second_gasmix.set_temperature(2.7)
- first_gasmix.gases[/datum/gas/hypernoblium][MOLES] = calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1)
- second_gasmix.gases[/datum/gas/tritium][MOLES] = calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1)
+ first_gasmix.set_gas(/datum/gas/hypernoblium, calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1))
+ second_gasmix.set_gas(/datum/gas/tritium, calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE - 1))
diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm
index b82c51a9a0a3..47988bb0843d 100644
--- a/code/game/objects/items/bodybag.dm
+++ b/code/game/objects/items/bodybag.dm
@@ -61,6 +61,7 @@
unfoldedbag_path = /obj/structure/closet/body_bag/bluespace
w_class = WEIGHT_CLASS_SMALL
item_flags = NO_MAT_REDEMPTION
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/plasma = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT)
/// Tracks the air from the bodybag
var/datum/gas_mixture/internal_air
@@ -175,6 +176,7 @@
icon = 'maplestation_modules/icons/obj/bodybag.dmi'
icon_state = "stasis_bag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/environmental/stasis
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 10, /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT)
/obj/item/bodybag/stasis/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/environmental/stasis/bag = ..()
diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
index 1d6e75eb6097..de24082a31ba 100644
--- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
@@ -447,15 +447,6 @@
/obj/item/stack/sheet/glass = 10,
/obj/item/stack/sheet/plasteel = 5)
-/obj/item/circuitboard/machine/bluespace_sender
- name = "Bluespace Sender"
- greyscale_colors = CIRCUIT_COLOR_ENGINEERING
- build_path = /obj/machinery/atmospherics/components/unary/bluespace_sender
- req_components = list(
- /obj/item/stack/cable_coil = 10,
- /obj/item/stack/sheet/glass = 10,
- /obj/item/stack/sheet/plasteel = 5)
-
//Generic
/obj/item/circuitboard/machine/component_printer
name = "\improper Component Printer (Machine Board)"
diff --git a/code/game/objects/items/devices/scanners/gas_analyzer.dm b/code/game/objects/items/devices/scanners/gas_analyzer.dm
index a7fd51ce1b28..d7d53b87153a 100644
--- a/code/game/objects/items/devices/scanners/gas_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/gas_analyzer.dm
@@ -136,39 +136,57 @@
/obj/item/analyzer/ui_data(mob/user)
var/obj/item/last_scanned_real = last_scanned?.resolve()
if(!QDELETED(last_scanned_real) && can_see(user, last_scanned_real, ranged_scan_distance))
- on_analyze(src, last_scanned_real) // updates last_gasmix_data as long as we're in range
+ collect_scan_info(last_scanned_real) // updates last_gasmix_data as long as we're in range
return list(
- "gasmixes" = last_gasmix_data || list(),
+ "gasmixes" = last_gasmix_data,
)
+/// Checks if we can use the analyzer at all
+/obj/item/analyzer/proc/can_use(mob/user)
+ if(!user.can_read(src))
+ return FALSE
+ // Logical, but it contains "tutorial information", so we should allow it.
+ // if(user.is_blind())
+ // return FALSE
+ return TRUE
+
+/obj/item/analyzer/ui_status(mob/user)
+ return can_use(user) ? ..() : UI_CLOSE
+
/obj/item/analyzer/attack_self(mob/user, modifiers)
- if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind())
- return
- var/lowest_obj = ismob(loc) ? loc.loc : loc
- atmos_scan(user = user, target = lowest_obj, silent = FALSE)
- on_analyze(src, lowest_obj)
+ scan_atom(get_turf(src), user)
+ return TRUE
/obj/item/analyzer/attack_self_secondary(mob/user, modifiers)
- if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind())
- return
-
ui_interact(user)
+ return TRUE
/obj/item/analyzer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ // if(istype(interacting_with, /obj/effect/anomaly) && can_see(user, interacting_with, ranged_scan_distance))
+ // var/obj/effect/anomaly/ranged_anomaly = interacting_with
+ // ranged_anomaly.analyzer_act(user, src)
+ // return ITEM_INTERACT_SUCCESS
+
return interact_with_atom(interacting_with, user, modifiers)
/obj/item/analyzer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION) && can_see(user, interacting_with, ranged_scan_distance))
- atmos_scan(user, (interacting_with.return_analyzable_air() ? interacting_with : get_turf(interacting_with)))
+ scan_atom(interacting_with.return_analyzable_air() ? interacting_with : get_turf(interacting_with), user)
return NONE // Non-blocking
-/// Called when our analyzer is used on something
-/obj/item/analyzer/proc/on_analyze(datum/source, atom/target)
- SIGNAL_HANDLER
+/obj/item/analyzer/proc/scan_atom(atom/target, mob/living/user)
+ if(!can_use(user))
+ return
+
+ atmos_scan(user, target, silent = FALSE)
+ collect_scan_info(target)
+
+/obj/item/analyzer/proc/collect_scan_info(atom/target)
var/mixture = target.return_analyzable_air()
if(!mixture)
- return FALSE
+ return
+
var/list/airs = islist(mixture) ? mixture : list(mixture)
var/list/new_gasmix_data = list()
for(var/datum/gas_mixture/air as anything in airs)
@@ -179,6 +197,11 @@
last_gasmix_data = new_gasmix_data
last_scanned = WEAKREF(target)
+/// Called when our analyzer is used on something
+/obj/item/analyzer/proc/on_analyze(datum/source, atom/target)
+ SIGNAL_HANDLER
+ collect_scan_info(target)
+
/**
* Outputs a message to the user describing the target's gasmixes.
*
@@ -215,10 +238,10 @@
if(total_moles > 0)
message += span_notice("Moles: [round(total_moles, 0.01)] mol")
- var/list/cached_gases = air.gases
- for(var/id in cached_gases)
- var/gas_concentration = cached_gases[id][MOLES]/total_moles
- message += span_notice("[cached_gases[id][GAS_META][META_GAS_NAME]]: [round(cached_gases[id][MOLES], 0.01)] mol ([round(gas_concentration*100, 0.01)] %)")
+ var/list/cached_gas_name = GAS_META[META_GAS_NAME]
+ for(var/id, amount in air.moles)
+ var/gas_concentration = amount / total_moles
+ message += span_notice("[cached_gas_name[id]]: [round(amount, 0.01)] mol ([round(gas_concentration*100, 0.01)] %)")
message += span_notice("Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)")
message += span_notice("Volume: [volume] L")
message += span_notice("Pressure: [round(pressure, 0.01)] kPa")
diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm
index bf9ae18a611a..e97f61c9771e 100644
--- a/code/game/objects/items/flamethrower.dm
+++ b/code/game/objects/items/flamethrower.dm
@@ -230,8 +230,9 @@
var/datum/gas_mixture/tank_mix = ptank.return_air()
var/datum/gas_mixture/air_transfer = tank_mix.remove_ratio(release_amount)
- if(air_transfer.gases[/datum/gas/plasma])
- air_transfer.gases[/datum/gas/plasma][MOLES] *= 5 //Suffering
+ if(air_transfer.moles[/datum/gas/plasma])
+ var/moles = air_transfer.moles[/datum/gas/plasma] * 5 //Suffering
+ air_transfer.set_gas(/datum/gas/plasma, moles)
target.assume_air(air_transfer)
//Burn it based on transferred gas
target.hotspot_expose((tank_mix.temperature*2) + 380,500)
@@ -278,8 +279,7 @@
SIGNAL_HANDLER
if(ptank)
var/datum/gas_mixture/tank_mix = ptank.return_air()
- tank_mix.assert_gas(/datum/gas/plasma)
- tank_mix.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*ptank.volume/(R_IDEAL_GAS_EQUATION*T20C)
+ tank_mix.set_gas(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*ptank.volume/(R_IDEAL_GAS_EQUATION*T20C))
else
ptank = new /obj/item/tank/internals/plasma/full(src)
update_appearance()
diff --git a/code/game/objects/items/grenades/atmos_grenades.dm b/code/game/objects/items/grenades/atmos_grenades.dm
index 9f35ac6824c0..3c6556f227f8 100644
--- a/code/game/objects/items/grenades/atmos_grenades.dm
+++ b/code/game/objects/items/grenades/atmos_grenades.dm
@@ -1,6 +1,7 @@
/obj/item/grenade/gas_crystal
- desc = "Some kind of crystal, this shouldn't spawn"
- name = "Gas Crystal"
+ name = "gas crystal"
+ desc = "Some kind of crystal."
+ abstract_type = /obj/item/grenade/gas_crystal
icon = 'icons/obj/weapons/grenade.dmi'
icon_state = "bluefrag"
inhand_icon_state = "flashbang"
@@ -24,8 +25,8 @@
addtimer(CALLBACK(src, PROC_REF(detonate)), isnull(delayoverride)? det_time : delayoverride)
/obj/item/grenade/gas_crystal/healium_crystal
- name = "Healium crystal"
- desc = "A crystal made from the Healium gas, it's cold to the touch."
+ name = "\improper Healium crystal"
+ desc = "A crystal made from the Healium gas. It's cold to the touch."
icon_state = "healium_crystal"
///Range of the grenade that will cool down and affect mobs
var/fix_range = 7
@@ -46,8 +47,8 @@
qdel(src)
/obj/item/grenade/gas_crystal/proto_nitrate_crystal
- name = "Proto Nitrate crystal"
- desc = "A crystal made from the Proto Nitrate gas, you can see the liquid gases inside."
+ name = "\improper Proto-Nitrate crystal"
+ desc = "A crystal made from the Proto-Nitrate gas. It feels lighter than you'd expect."
icon_state = "proto_nitrate_crystal"
///Range of the grenade air refilling
var/refill_range = 5
@@ -72,8 +73,8 @@
qdel(src)
/obj/item/grenade/gas_crystal/nitrous_oxide_crystal
- name = "N2O crystal"
- desc = "A crystal made from the N2O gas, you can see the liquid gases inside."
+ name = "\improper N2O crystal"
+ desc = "A crystal made from Nitrous Oxide gas. Looking at it makes you feel sleepy."
icon_state = "n2o_crystal"
///Range of the grenade air refilling
var/fill_range = 1
@@ -97,7 +98,7 @@
/obj/item/grenade/gas_crystal/crystal_foam
name = "crystal foam"
- desc = "A crystal with a foggy inside"
+ desc = "A crystal with a foggy inside."
icon_state = "crystal_foam"
var/breach_range = 7
diff --git a/code/game/objects/items/stacks/ammonia_crystals.dm b/code/game/objects/items/stacks/ammonia_crystals.dm
index 367864269e9a..446204d18cc4 100644
--- a/code/game/objects/items/stacks/ammonia_crystals.dm
+++ b/code/game/objects/items/stacks/ammonia_crystals.dm
@@ -1,5 +1,6 @@
/obj/item/stack/ammonia_crystals
name = "ammonia crystals"
+ desc = "Crystallized ammonia. Valuable for someone, but probably not you."
singular_name = "ammonia crystal"
icon = 'icons/obj/stack_objects.dmi'
icon_state = "ammonia_crystal"
diff --git a/code/game/objects/items/tanks/jetpack.dm b/code/game/objects/items/tanks/jetpack.dm
index 04a82fd9029e..6a7abe0b555c 100644
--- a/code/game/objects/items/tanks/jetpack.dm
+++ b/code/game/objects/items/tanks/jetpack.dm
@@ -63,8 +63,7 @@
/obj/item/tank/jetpack/populate_gas()
if(gas_type)
var/datum/gas_mixture/our_mix = return_air()
- our_mix.assert_gas(gas_type)
- our_mix.gases[gas_type][MOLES] = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))
+ our_mix.set_gas(gas_type, ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C)))
/obj/item/tank/jetpack/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_jetpack))
diff --git a/code/game/objects/items/tanks/tank_types.dm b/code/game/objects/items/tanks/tank_types.dm
index 8f0cc857ab1c..daf38882159d 100644
--- a/code/game/objects/items/tanks/tank_types.dm
+++ b/code/game/objects/items/tanks/tank_types.dm
@@ -35,8 +35,7 @@
/obj/item/tank/internals/oxygen/populate_gas()
- air_contents.assert_gas(/datum/gas/oxygen)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/oxygen, (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/oxygen/yellow
@@ -68,9 +67,9 @@
force = 10
/obj/item/tank/internals/anesthetic/populate_gas()
- air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrous_oxide)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
- air_contents.gases[/datum/gas/nitrous_oxide][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
+ air_contents.set_gas(/datum/gas/oxygen, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD)
+ air_contents.set_gas(/datum/gas/nitrous_oxide, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD)
+
/obj/item/tank/internals/anesthetic/examine(mob/user)
. = ..()
@@ -82,8 +81,7 @@
icon_state = "anesthetic_warning"
/obj/item/tank/internals/anesthetic/pure/populate_gas()
- air_contents.assert_gases(/datum/gas/nitrous_oxide)
- air_contents.gases[/datum/gas/nitrous_oxide][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.adjust_gas(/datum/gas/nitrous_oxide, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/*
* Plasma
@@ -101,8 +99,7 @@
/obj/item/tank/internals/plasma/populate_gas()
- air_contents.assert_gas(/datum/gas/plasma)
- air_contents.gases[/datum/gas/plasma][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/plasma, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/plasma/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
if(istype(W, /obj/item/flamethrower))
@@ -118,8 +115,7 @@
return ..()
/obj/item/tank/internals/plasma/full/populate_gas()
- air_contents.assert_gas(/datum/gas/plasma)
- air_contents.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/plasma/empty/populate_gas()
return
@@ -138,12 +134,10 @@
distribute_pressure = TANK_PLASMAMAN_RELEASE_PRESSURE
/obj/item/tank/internals/plasmaman/populate_gas()
- air_contents.assert_gas(/datum/gas/plasma)
- air_contents.gases[/datum/gas/plasma][MOLES] = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/plasma, (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/plasmaman/full/populate_gas()
- air_contents.assert_gas(/datum/gas/plasma)
- air_contents.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/plasmaman/belt
@@ -158,8 +152,7 @@
w_class = WEIGHT_CLASS_SMALL //thanks i forgot this
/obj/item/tank/internals/plasmaman/belt/full/populate_gas()
- air_contents.assert_gas(/datum/gas/plasma)
- air_contents.gases[/datum/gas/plasma][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/plasma, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/plasmaman/belt/empty/populate_gas()
return
@@ -186,8 +179,7 @@
/obj/item/tank/internals/emergency_oxygen/populate_gas()
- air_contents.assert_gas(/datum/gas/oxygen)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.set_gas(/datum/gas/oxygen, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
/obj/item/tank/internals/emergency_oxygen/empty/populate_gas()
@@ -246,21 +238,18 @@
/obj/item/tank/internals/emergency_oxygen/engi/clown/n2o
/obj/item/tank/internals/emergency_oxygen/engi/clown/n2o/populate_gas()
- air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/nitrous_oxide)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.95
- air_contents.gases[/datum/gas/nitrous_oxide][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.05
+ air_contents.set_gas(/datum/gas/oxygen, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.95)
+ air_contents.set_gas(/datum/gas/nitrous_oxide, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.05)
/obj/item/tank/internals/emergency_oxygen/engi/clown/bz
/obj/item/tank/internals/emergency_oxygen/engi/clown/bz/populate_gas()
- air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/bz)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.9
- air_contents.gases[/datum/gas/bz][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.1
+ air_contents.set_gas(/datum/gas/oxygen,(10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.9)
+ air_contents.set_gas(/datum/gas/bz,(10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.1)
/obj/item/tank/internals/emergency_oxygen/engi/clown/helium
distribute_pressure = TANK_CLOWN_RELEASE_PRESSURE + 2
/obj/item/tank/internals/emergency_oxygen/engi/clown/helium/populate_gas()
- air_contents.assert_gases(/datum/gas/oxygen, /datum/gas/helium)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.75
- air_contents.gases[/datum/gas/helium][MOLES] = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.25
+ air_contents.set_gas(/datum/gas/oxygen, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.75)
+ air_contents.set_gas(/datum/gas/helium, (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * 0.25)
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index 89240e52e743..ba3ce2ec1c04 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -526,7 +526,7 @@
var/datum/gas_mixture/our_mix = return_air()
our_mix.assert_gases(/datum/gas/plasma, /datum/gas/oxygen)
- var/fuel_moles = our_mix.gases[/datum/gas/plasma][MOLES] + our_mix.gases[/datum/gas/oxygen][MOLES]/6
+ var/fuel_moles = our_mix.moles[/datum/gas/plasma] + our_mix.moles[/datum/gas/oxygen]/6
our_mix.garbage_collect()
var/datum/gas_mixture/bomb_mixture = our_mix.copy()
var/strength = 1
diff --git a/code/game/objects/structures/bonfire.dm b/code/game/objects/structures/bonfire.dm
index facb250e6ad0..7e9a68d28e24 100644
--- a/code/game/objects/structures/bonfire.dm
+++ b/code/game/objects/structures/bonfire.dm
@@ -97,10 +97,8 @@
/obj/structure/bonfire/proc/check_oxygen()
if(isopenturf(loc))
var/turf/open/bonfire_turf = loc
- if(bonfire_turf.air)
- var/loc_gases = bonfire_turf.air.gases
- if(loc_gases[/datum/gas/oxygen] && loc_gases[/datum/gas/oxygen][MOLES] >= 5)
- return TRUE
+ if(bonfire_turf.air?.moles[/datum/gas/oxygen] >= 5)
+ return TRUE
return FALSE
/obj/structure/bonfire/proc/start_burning()
diff --git a/code/game/objects/structures/crates_lockers/closets/bodybag.dm b/code/game/objects/structures/crates_lockers/closets/bodybag.dm
index 2a64da6d0bb4..b2ebc605de8d 100644
--- a/code/game/objects/structures/crates_lockers/closets/bodybag.dm
+++ b/code/game/objects/structures/crates_lockers/closets/bodybag.dm
@@ -32,6 +32,10 @@
/// Paper pinned to this bag
var/obj/item/paper/pinned
+/obj/structure/closet/body_bag/Initialize(mapload)
+ . = ..()
+ RegisterSignal(src, COMSIG_ATOM_EXAMINE_POST_DESCRIPTOR, PROC_REF(make_examine_cloth))
+
/obj/structure/closet/body_bag/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(isnull(held_item))
@@ -53,6 +57,14 @@
QDEL_NULL(foldedbag_instance)
return ..()
+/obj/structure/closet/body_bag/proc/make_examine_cloth(mob/user, mob/user, list/examine_text, list/mat_list)
+ SIGNAL_HANDLER
+
+ // melbert todo : weeeee need a cloth material
+ mat_list -= /datum/material/iron::name
+ mat_list.Insert(1, "cloth")
+ mat_list["cloth"] = "It is made out of cloth."
+
///Handles renaming of the bodybag's examine tag.
/obj/structure/closet/body_bag/proc/handle_tag(new_name)
playsound(src, SFX_WRITING_PEN, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, SOUND_FALLOFF_EXPONENT + 3, ignore_walls = FALSE)
@@ -154,6 +166,7 @@
max_mob_size = MOB_SIZE_LARGE
sealed = TRUE
air_volume = TANK_STANDARD_VOLUME * 2
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/plasma = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = HALF_SHEET_MATERIAL_AMOUNT, /datum/material/bluespace = HALF_SHEET_MATERIAL_AMOUNT)
/obj/structure/closet/body_bag/bluespace/attempt_fold(mob/living/carbon/human/the_folder)
. = FALSE
@@ -421,15 +434,14 @@
if(opened)
// lose a majority of all n2o when we start leaking gas, to stop this being a free (obnoxious) way to make n2o
internal_air.assert_gases(/datum/gas/nitrous_oxide)
- internal_air.gases[/datum/gas/nitrous_oxide][MOLES] *= 0.15
+ internal_air.adjust_gas(/datum/gas/nitrous_oxide, internal_air.moles[/datum/gas/nitrous_oxide] * 0.15)
return ..()
internal_air.assert_gases(/datum/gas/nitrogen, /datum/gas/nitrous_oxide)
- var/conversion_amount = min(internal_air.gases[/datum/gas/nitrogen][MOLES], 0.2 * internal_air.total_moles() * seconds_per_tick)
+ var/conversion_amount = min(internal_air.moles[/datum/gas/nitrogen], 0.2 * internal_air.total_moles() * seconds_per_tick)
if(conversion_amount > 0)
// 20% of the nitrogen in the bag is converted to nitrous oxide every second while closed
- internal_air.gases[/datum/gas/nitrogen][MOLES] = max(0, internal_air.gases[/datum/gas/nitrogen][MOLES] - conversion_amount)
- internal_air.gases[/datum/gas/nitrous_oxide][MOLES] += conversion_amount
+ internal_air.convert_gas(/datum/gas/nitrogen, /datum/gas/nitrous_oxide, conversion_amount)
return ..()
/obj/structure/closet/body_bag/environmental/hardlight
@@ -440,6 +452,7 @@
foldedbag_path = null
weather_protection = list(TRAIT_VOIDSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE)
can_scan_through = TRUE
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 10, /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT)
/obj/structure/closet/body_bag/environmental/hardlight/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
if(damage_type in list(BRUTE, BURN))
@@ -526,8 +539,9 @@
if(internal_air.temperature <= BODY_PRESERVATION_TEMP && !HAS_TRAIT(freezing, TRAIT_STASIS))
apply_stasis(freezing)
- // Bout two minutes of time
- take_damage(max_integrity * 0.004 * seconds_per_tick, sound_effect = FALSE)
+ if(loc?.return_air()?.return_temperature() > T0C)
+ // Bout two minutes of time
+ take_damage(max_integrity * 0.004 * seconds_per_tick, sound_effect = FALSE)
/obj/structure/closet/body_bag/environmental/stasis/examine_status(mob/user)
switch(100 * get_integrity_percentage())
diff --git a/code/game/objects/structures/toiletbong.dm b/code/game/objects/structures/toiletbong.dm
index 038e8fa292a3..8091da40b203 100644
--- a/code/game/objects/structures/toiletbong.dm
+++ b/code/game/objects/structures/toiletbong.dm
@@ -85,7 +85,8 @@
new /obj/item/flamethrower(get_turf(src))
new /obj/item/stack/sheet/iron(get_turf(src))
var/obj/item/tank/internals/plasma/ptank = new /obj/item/tank/internals/plasma(get_turf(src))
- ptank.air_contents.gases[/datum/gas/plasma][MOLES] = (0)
+ ptank.air_contents.set_gas(/datum/gas/plasma, 0)
+ // drop_custom_materials()
qdel(src)
return TRUE
diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
index 3877f75d1bf8..7e3fcdf80fc5 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
@@ -14,9 +14,8 @@
/obj/structure/transit_tube_pod/Initialize(mapload)
. = ..()
- air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
- air_contents.gases[/datum/gas/oxygen][MOLES] = MOLES_O2STANDARD
- air_contents.gases[/datum/gas/nitrogen][MOLES] = MOLES_N2STANDARD
+ var/list/new_gases = list(/datum/gas/oxygen = MOLES_O2STANDARD, /datum/gas/nitrogen = MOLES_N2STANDARD)
+ air_contents.adjust_multiple_gases(new_gases)
air_contents.temperature = T20C
/obj/structure/transit_tube_pod/Destroy()
diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm
index e7e2974d2f18..1cee2f794872 100644
--- a/code/game/turfs/change_turf.dm
+++ b/code/game/turfs/change_turf.dm
@@ -240,7 +240,6 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
return
var/datum/gas_mixture/total = new//Holders to assimilate air from nearby turfs
- var/list/total_gases = total.gases
//Stolen blatently from self_breakdown
var/list/turf_list = atmos_adjacent_turfs + src
var/turflen = turf_list.len
@@ -256,14 +255,13 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
energy += mix.temperature * capacity
heat_cap += capacity
- var/list/giver_gases = mix.gases
- for(var/giver_id in giver_gases)
- ASSERT_GAS_IN_LIST(giver_id, total_gases)
- total_gases[giver_id][MOLES] += giver_gases[giver_id][MOLES]
+ for(var/giver_id, amount in mix.moles)
+ total.adjust_gas(giver_id, amount)
total.temperature = energy / heat_cap
- for(var/id in total_gases)
- total_gases[id][MOLES] /= turflen
+ var/list/cached_total_moles = total.moles
+ for(var/id in cached_total_moles)
+ cached_total_moles[id] /= turflen
for(var/turf/open/turf in turf_list)
turf.air.copy_from(total)
diff --git a/code/game/turfs/open/floor/reinforced_floor.dm b/code/game/turfs/open/floor/reinforced_floor.dm
index 3fe427f1aced..e8c3a5420b97 100644
--- a/code/game/turfs/open/floor/reinforced_floor.dm
+++ b/code/game/turfs/open/floor/reinforced_floor.dm
@@ -113,7 +113,7 @@
initial_gas_mix = ATMOS_TANK_CO2
/turf/open/floor/engine/plasma
- name = "plasma floor"
+ name = "\improper Plasma floor"
initial_gas_mix = ATMOS_TANK_PLASMA
/turf/open/floor/engine/o2
@@ -147,7 +147,7 @@
initial_gas_mix = ATMOS_TANK_H2
/turf/open/floor/engine/hypernoblium
- name = "\improper Hypernoblium floor"
+ name = "\improper Hyper-Noblium floor"
initial_gas_mix = ATMOS_TANK_HYPERNOBLIUM
/turf/open/floor/engine/miasma
@@ -155,7 +155,7 @@
initial_gas_mix = ATMOS_TANK_MIASMA
/turf/open/floor/engine/nitrium
- name = "\improper nitrium floor"
+ name = "\improper Nitrium floor"
initial_gas_mix = ATMOS_TANK_NITRIUM
/turf/open/floor/engine/pluoxium
@@ -184,7 +184,7 @@
initial_gas_mix = ATMOS_TANK_HELIUM
/turf/open/floor/engine/antinoblium
- name = "\improper Antinoblium floor"
+ name = "\improper Anti-Noblium floor"
initial_gas_mix = ATMOS_TANK_ANTINOBLIUM
/turf/open/floor/engine/air
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index a9accccd8b20..04243f6320d1 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -301,6 +301,7 @@
var/mob/living/our_mob = mob_override || owner.current
handle_clown_mutation(our_mob, "Ancient knowledge described to you has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
our_mob.faction |= FACTION_HERETIC
+ our_mob.apply_status_effect(/datum/status_effect/grouped/heretic_dreams, type)
if (!issilicon(our_mob))
GLOB.reality_smash_track.add_tracked_mind(owner)
@@ -314,6 +315,7 @@
var/mob/living/our_mob = mob_override || owner.current
handle_clown_mutation(our_mob, removing = FALSE)
our_mob.faction -= FACTION_HERETIC
+ our_mob.remove_status_effect(/datum/status_effect/grouped/heretic_dreams, type)
if (owner in GLOB.reality_smash_track.tracked_heretics)
GLOB.reality_smash_track.remove_tracked_mind(owner)
diff --git a/code/modules/antagonists/heretic/status_effects/dreams.dm b/code/modules/antagonists/heretic/status_effects/dreams.dm
new file mode 100644
index 000000000000..9904b2f190f5
--- /dev/null
+++ b/code/modules/antagonists/heretic/status_effects/dreams.dm
@@ -0,0 +1,118 @@
+/datum/status_effect/grouped/heretic_dreams
+ id = "heretic_dreams"
+ duration = -1 //STATUS_EFFECT_PERMANENT
+ tick_interval = -1 //STATUS_EFFECT_NO_TICK
+ alert_type = null
+ /// Cooldown between allowed dreams
+ COOLDOWN_DECLARE(dreaming_cooldown)
+
+/datum/status_effect/grouped/heretic_dreams/on_apply()
+ . = ..()
+ RegisterSignal(owner, COMSIG_PRE_DREAMING, PROC_REF(add_heretic_dream))
+ RegisterSignal(owner, COMSIG_START_DREAMING, PROC_REF(start_heretic_dream))
+
+/datum/status_effect/grouped/heretic_dreams/on_remove()
+ . = ..()
+ UnregisterSignal(owner, COMSIG_PRE_DREAMING)
+ UnregisterSignal(owner, COMSIG_START_DREAMING)
+
+/datum/status_effect/grouped/heretic_dreams/proc/add_heretic_dream(mob/living/dreamer, list/dream_pool)
+ SIGNAL_HANDLER
+
+ if(!COOLDOWN_FINISHED(src, dreaming_cooldown))
+ return
+
+ var/atom/dream_center = get_dream_center(dreamer)
+ if(isnull(dream_center))
+ return
+
+ dream_pool[new /datum/dream/heretic(dream_center)] = 200
+
+/datum/status_effect/grouped/heretic_dreams/proc/start_heretic_dream(mob/living/dreamer, datum/dream/current_dream)
+ SIGNAL_HANDLER
+
+ if(!istype(current_dream, /datum/dream/heretic))
+ return
+ COOLDOWN_START(src, dreaming_cooldown, /datum/mood_event/mansus_dream_fatigue::timeout)
+ dreamer.add_mood_event("mansus_dream_fatigue", /datum/mood_event/mansus_dream_fatigue)
+
+/datum/status_effect/grouped/heretic_dreams/proc/get_dream_center(mob/living/dreamer)
+ // Select a random influence as the center of the dream
+ if(length(GLOB.reality_smash_track.smashes))
+ return pick(GLOB.reality_smash_track.smashes)
+
+ // If there are no influences, either don't trigger the dream (if we are a heretic) or pick a completely random locale (if we aren't)
+ if(IS_HERETIC(dreamer))
+ return null
+
+ return get_safe_random_station_turf()
+
+/// Heretics can see dreams about random machinery from the perspective of a random unused influence
+/datum/dream/heretic
+ sleep_until_finished = TRUE
+ /// The location of the influence (or lack thereof in the case of a fake dream) we will be dreaming about
+ var/atom/dream_center
+ /// The distance to the objects visible from the influence during the dream
+ var/dream_view_range = 5
+ var/list/what_you_can_see = list(
+ /obj/item,
+ /obj/structure,
+ /obj/machinery,
+ )
+ var/static/list/what_you_cant_see = typecacheof(list(
+ // Underfloor stuff and default wallmounts
+ /obj/item/radio/intercom,
+ /obj/structure/cable,
+ /obj/structure/disposalpipe/segment,
+ /obj/machinery/atmospherics/pipe/smart/manifold4w,
+ /obj/machinery/atmospherics/components/unary/vent_scrubber,
+ /obj/machinery/atmospherics/components/unary/vent_pump,
+ /obj/machinery/duct,
+ /obj/machinery/navbeacon,
+ /obj/machinery/power/terminal,
+ /obj/machinery/power/apc,
+ /obj/machinery/light_switch,
+ /obj/machinery/light,
+ /obj/machinery/camera,
+ /obj/machinery/door/firedoor,
+ /obj/machinery/firealarm,
+ /obj/machinery/airalarm,
+ /obj/structure/window/fulltile,
+ /obj/structure/window/reinforced/fulltile,
+ ))
+ /// Cached list of allowed typecaches for each type in what_you_can_see
+ var/static/list/allowed_typecaches_by_root_type = null
+
+/datum/dream/heretic/New(atom/dream_center)
+ src.dream_center = dream_center
+
+/datum/dream/heretic/GenerateDream(mob/living/carbon/dreamer)
+ . = list()
+ . += "you wander through the forest of Mansus"
+ . += "there is a " + pick("pond", "well", "lake", "puddle", "stream", "spring", "brook", "marsh")
+
+ if(isnull(allowed_typecaches_by_root_type))
+ allowed_typecaches_by_root_type = list()
+ for(var/type in what_you_can_see)
+ allowed_typecaches_by_root_type[type] = typecacheof(type) - what_you_cant_see
+
+ var/list/all_objects = oview(dream_view_range, dream_center)
+ var/something_found = FALSE
+ for(var/object_type in allowed_typecaches_by_root_type)
+ var/list/filtered_objects = typecache_filter_list(all_objects, allowed_typecaches_by_root_type[object_type])
+ if(filtered_objects.len)
+ if (!something_found)
+ . += "its waters reflect"
+ something_found = TRUE
+ var/obj/found_object = pick(filtered_objects)
+ . += initial(found_object.name)
+ if(!something_found)
+ . += pick("it's pitch black", "ihe reflections are vague", "you stroll aimlessly")
+ else
+ . += "the images fade in the ripples"
+ . += "you feel exhausted"
+
+/datum/mood_event/mansus_dream_fatigue
+ description = "I must recover before I can dream of Mansus again."
+ mood_change = -2
+ timeout = 5 MINUTES
diff --git a/code/modules/atmospherics/Atmospherics.md b/code/modules/atmospherics/Atmospherics.md
index 6d61a8cf16bc..972ef62302a2 100644
--- a/code/modules/atmospherics/Atmospherics.md
+++ b/code/modules/atmospherics/Atmospherics.md
@@ -38,7 +38,7 @@ Now then, into the breach.
The air controller is, at its core, quite simple, yet it is absolutely fundamental to the atmospheric system. The air controller is the clock which triggers all continuous actions within the atmos system, such as vents distributing air or gas moving between tiles. The actions taken by the air controller are quite simple, and will be enumerated here. Much of the substance of the air ticker is due to the game's master controller, whose intricacies I will not delve into for this document. I will however go into more detail about how SSAir in particular works in Chapter 6. In any case, this is a simplified list of the air controller's actions in a single tick:
1. Rebuild Pipenets
- Runs each time SSAir processes, sometimes out of order. It ensures that no pipeline sit unresolved or unbuilt
- - Processes the `rebuild_queue` list into the `expansion_queue` list, and then builds a full pipeline piecemeal. We do a ton of fenagling here to reduce overrun
+ - Processes the `rebuild_queue` list into the `expansion_queue` list, and then builds a full pipeline piecemeal. We do a ton of fenagling here to reduce overrun
2. Pipenets
- Updates the internal gasmixes of attached pipe machinery, and reacts the gases in a pipeline
- Calls `process()` on each `/datum/pipenet` in the `networks` list
@@ -109,20 +109,27 @@ Each gas mixture has an associative list, gases, which maps according to a key t
Each type of gas is defined by defining a new subtype of /datum/gas. These datums do not get instantiated; they merely serve as a convenient and familiar means for a coder unfamiliar with the inner workings of listmos to define a new gas. Additionally, the type paths serve a second use as the keys used to access a particular gas within the gases list. It is easiest to demonstrate the manipulation of gas, including these list accesses, with an example.
+### Vectorized xgm-like system
+Current system uses two associative arrays: `moles` and `moles_archived` with the key being `/datum/gas`. The gas metadata array is stored in a static variable `gas_mixture::gas_meta`. The layout of keys in the array is `gas_meta[META_INDEX][gas_path]`. This allows us to use vector functions like `values_sum` and `values_dot` (introduced in BYOND v516). This approach showed improvement of ~20% on process_cell and memory footprint stayed the same.
+
+While these vector functions are extremely fast, they can only work with associative arrays of number. To calculate total moles you would write `values_sum(moles)` and to calculate heat capacity, it would be `values_dot(moles, gas_meta[META_GAS_SPECIFIC_HEAT])`. This does not seem like a lot, but when a subsytem is doing 1000 updates each simulation tick every microsecond matters.
+
+Another benefit of using one-dimensional associative arrays is that for any arithmetic or logic operation a missing key acts like a 0. For example you could write `moles[/datum/gas/oxygen] += 10` even if `/datum/gas/oxygen` is not in the list.
+
### Interfacing with a Gas Mixture
```DM
var/datum/gas_mixture/air = new
air.assert_gas(/datum/gas/oxygen)
-air.gases[/datum/gas/oxygen][MOLES] = 100
-world << air.gases[/datum/gas/oxygen][GAS_META][META_GAS_NAME] //outputs "Oxygen"
-world << air.gases.heat_capacity() //outputs 2000 (100 mol * 20 J/K/mol)
-air.gases[/datum/gas/oxygen][MOLES] -= 110
+air.moles[/datum/gas/oxygen] = 100
+world << air.gas_meta[META_GAS_NAME][/datum/gas/oxygen] //outputs "Oxygen"
+world << air.heat_capacity() //outputs 2000 (100 mol * 20 J/K/mol)
+air.moles[/datum/gas/oxygen] -= 110
air.garbage_collect() //oxygen is now removed from the gases list, since it was empty
```
*Snippet 4.2: gas mixture usage examples*
-Of particular note in this snippet are the two procs assert_gas() and garbage_collect(). These procs are very important while interfacing with gas mixtures. If you are uncertain about whether a given mixture has a particular gas, you must use assert_gas() before any reads or writes from the gas. If you fail to use assert_gas() then there will be runtime errors when you try to access the inner lists. When you remove any number of moles from a given gas, be sure to call garbage_collect(). This proc removes all gases which have mole counts less than or equal to 0. This is a memory and performance enhancement for list accesses achieved by reducing the size of the list, and also saves us from having to do sanity checks for negative moles whenever gas is removed. As a quick reference, here is a list of common procs/vars/list indices which the average coder may wish to use when interfacing with a gas mixture.
+Of particular note in this snippet are the two procs assert_gas() and garbage_collect(). These procs are very important while interfacing with gas mixtures. If you are uncertain about whether a given mixture has a particular gas, you must use assert_gas() before any reads or writes from the gas. When you remove any number of moles from a given gas, be sure to call garbage_collect(). This proc removes all gases which have mole counts less than or equal to 0. This is a memory and performance enhancement for list accesses achieved by reducing the size of the list, and also saves us from having to do sanity checks for negative moles whenever gas is removed. As a quick reference, here is a list of common procs/vars/list indices which the average coder may wish to use when interfacing with a gas mixture.
##### Gas Mixture Datum
* *`/datum/gas_mixture/proc/assert_gas()`* - Used before accessing a particular type of gas.
@@ -141,10 +148,13 @@ It's used by `/datum/gas_mixture/immutable/space`, which implements some particu
It's also implemented by `/datum/gas_mixture/immutable/planetary`, which is used for planetary turfs, and has some code that makes actually having a gasmix possible.
-##### Gas List
-* *`gases[path][MOLES]`* - Quantity of a particular gas within a mixture.
-* *`gases[path][GAS_META][META_GAS_NAME]`* - The long name of a gas, ex. "Oxygen" or "Hyper-noblium"
-* *`gases[path][GAS_META][META_GAS_ID]`* - The internal ID of a given gas, ex. "o2" or "nob"
+- _`moles[path]`_ - Quantity of a particular gas within a mixture.
+- _`gas_meta[META_GAS_NAME][path]`_ - The long name of a gas, ex. "Oxygen" or "Hyper-noblium"
+- _`gas_meta[META_GAS_ID][path]`_ - The internal ID of a given gas, ex. "o2" or "nob"
+
+##### Gas Meta
+As was said previously gas metadata is stored in a static variable `gas_mixture.gas_meta`. This is done so you can easily access this variable from within gas_mixture, but if you are outside of gas_mixture code and you need gas metadata you can use convenience macro `GAS_META`. There is also third name for that list and it is `GLOB.meta_gas_info`. All of those variables point to the same list and you can use either. As the rule of thumb, in the performance critical code use `GAS_META` or `gas_meta`, otherwise `GLOB.meta_gas_info`.
+
### Reactions
While defining a new gas on its own is very simple, there is no gas-specific behavior defined within /datum/gas. This behavior gets defined in a few places, notably breath code (to be discussed later) and in reactions. The most important and well known reaction in SS13 is fire - the combustion of plasma. Reactions are used for several things - in particular, it is conventional (though by no means enforced) that to form a gas, a reaction must occur. Creating a new reaction is fairly simple, this is the area of atmos that has received the most attention over the last few years, and the best place to start. Don't be scared of the size of reactions.dm, it's not that complex.
@@ -180,7 +190,7 @@ You may notice something like this in `process_cell()`. It's not quite the same
Back in the old FEA days, neighbor count was hardcoded to 4 (Likely because this is what cell sharing on an infinite grid would look like). This means that turf A -> turf B is the same as turf B -> turf A, because they're each portioning up the gas in the same way.
-But when we moved to LINDA, we started using the length of our atmos_adjacent_turfs list (or an analog).
+But when we moved to LINDA, we started using the length of our atmos_adjacent_turfs list (or an analog).
We need this so things like multiz can work, and so tiles in a corner share in a way that makes sense.
Because of this, turf A -> turf B was no longer the same as turf B -> turf A, assuming one of those turfs had a different neighbor count, from I DON'T KNOW WALLS?
diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm
index 268b48907459..ae001a646179 100644
--- a/code/modules/atmospherics/environmental/LINDA_fire.dm
+++ b/code/modules/atmospherics/environmental/LINDA_fire.dm
@@ -1,7 +1,7 @@
/// Returns reactions which will contribute to a hotspot's size.
/proc/init_hotspot_reactions()
var/list/fire_reactions = list()
- for (var/datum/gas_reaction/reaction as anything in subtypesof(/datum/gas_reaction))
+ for (var/datum/gas_reaction/standard/reaction as anything in subtypesof(/datum/gas_reaction/standard))
if(initial(reaction.expands_hotspot))
fire_reactions += reaction
@@ -22,38 +22,34 @@
*/
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
//If the air doesn't exist we just return false
- var/list/air_gases = air?.gases
- if(!air_gases)
+ var/cached_moles = air?.moles
+ if(!cached_moles)
return
- . = air_gases[/datum/gas/oxygen]
- var/oxy = . ? .[MOLES] : 0
- if (oxy < 0.5)
+ if (cached_moles[/datum/gas/oxygen] < 0.5)
return
- . = air_gases[/datum/gas/plasma]
- var/plas = . ? .[MOLES] : 0
- . = air_gases[/datum/gas/tritium]
- var/trit = . ? .[MOLES] : 0
- . = air_gases[/datum/gas/hydrogen]
- var/h2 = . ? .[MOLES] : 0
- . = air_gases[/datum/gas/freon]
- var/freon = . ? .[MOLES] : 0
+
+ var/plas_trit_h2_threshold = (\
+ cached_moles[/datum/gas/plasma] > 0.5\
+ || cached_moles[/datum/gas/tritium] > 0.5\
+ || cached_moles[/datum/gas/hydrogen] > 0.5)
+ var/freon_threshold = (cached_moles[/datum/gas/freon] > 0.5)
if(active_hotspot)
if(soh)
- if(plas > 0.5 || trit > 0.5 || h2 > 0.5)
+ if(plas_trit_h2_threshold)
if(active_hotspot.temperature < exposed_temperature)
active_hotspot.temperature = exposed_temperature
if(active_hotspot.volume < exposed_volume)
active_hotspot.volume = exposed_volume
- else if(freon > 0.5)
+ else if(freon_threshold)
if(active_hotspot.temperature > exposed_temperature)
active_hotspot.temperature = exposed_temperature
if(active_hotspot.volume < exposed_volume)
active_hotspot.volume = exposed_volume
return
- if(((exposed_temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) && (plas > 0.5 || trit > 0.5 || h2 > 0.5)) || \
- ((exposed_temperature < FREON_MAXIMUM_BURN_TEMPERATURE) && (freon > 0.5)))
+ if (((exposed_temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) && plas_trit_h2_threshold) || \
+ ((exposed_temperature < FREON_MAXIMUM_BURN_TEMPERATURE) && freon_threshold))
new /obj/effect/hotspot(src, exposed_volume * 25, exposed_temperature)
SSair.add_to_active(src)
@@ -273,7 +269,7 @@
color = list(LERP(0.3, 1, 1-greyscale_fire) * heat_r,0.3 * heat_g * greyscale_fire,0.3 * heat_b * greyscale_fire, 0.59 * heat_r * greyscale_fire,LERP(0.59, 1, 1-greyscale_fire) * heat_g,0.59 * heat_b * greyscale_fire, 0.11 * heat_r * greyscale_fire,0.11 * heat_g * greyscale_fire,LERP(0.11, 1, 1-greyscale_fire) * heat_b, 0,0,0)
alpha = heat_a
-#define INSUFFICIENT(path) (!location.air.gases[path] || location.air.gases[path][MOLES] < 0.5)
+#define INSUFFICIENT(path) (!location.air.moles[path] || location.air.moles[path] < 0.5)
/**
* Regular process proc for hotspots governed by the controller.
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index 4bc54df2a2aa..988c228f0d8b 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -173,10 +173,9 @@
src.atmos_overlay_types = null
return
- var/list/gases = air.gases
-
+ var/list/moles = air.moles
var/list/new_overlay_types
- GAS_OVERLAYS(gases, new_overlay_types, src)
+ GAS_OVERLAYS(moles, new_overlay_types, src)
if (atmos_overlay_types)
for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
@@ -300,7 +299,7 @@
our_excited_group = excited_group //update our cache
if(our_excited_group && enemy_excited_group && enemy_tile.excited) //If you're both excited, no need to compare right?
should_share_air = TRUE
- else if(our_air.compare(enemy_air)) //Lets see if you're up for it
+ else if(our_air.compare(enemy_air, /*cmp_archive = */ TRUE)) //Lets see if you're up for it
SSair.add_to_active(enemy_tile) //Add yourself young man
var/datum/excited_group/existing_group = our_excited_group || enemy_excited_group || new
if(!our_excited_group)
@@ -328,7 +327,7 @@
var/datum/gas_mixture/planetary_mix = SSair.planetary[initial_gas_mix]
// archive ourself again so we don't accidentally share more gas than we currently have
LINDA_CYCLE_ARCHIVE(src)
- if(our_air.compare(planetary_mix))
+ if(our_air.compare(planetary_mix, /*cmp_archive = */ TRUE))
if(!our_excited_group)
var/datum/excited_group/new_group = new
new_group.add_turf(src)
@@ -462,7 +461,7 @@
var/datum/gas_mixture/shared_mix = new
//make local for sanic speed
- var/list/shared_gases = shared_mix.gases
+ var/list/shared_cached_moles = shared_mix.moles
var/list/turf_list = src.turf_list
var/turflen = turf_list.len
var/imumutable_in_group = FALSE
@@ -472,26 +471,32 @@
for(var/turf/open/group_member as anything in turf_list)
//Cache?
var/datum/gas_mixture/turf/mix = group_member.air
- if (roundstart && istype(group_member.air, /datum/gas_mixture/immutable))
- imumutable_in_group = TRUE
- shared_mix.copy_from(group_member.air) //This had better be immutable young man
- shared_gases = shared_mix.gases //update the cache
- break
+ if (roundstart)
+ if(istype(group_member.air, /datum/gas_mixture/immutable))
+ imumutable_in_group = TRUE
+ shared_mix.copy_from(group_member.air) //This had better be immutable young man
+ shared_cached_moles = shared_mix.moles //update the cache
+ break
+ // If we're planetary use THAT mix, and stop here
+ if(group_member.planetary_atmos)
+ imumutable_in_group = TRUE
+ var/datum/gas_mixture/planetary_mix = SSair.planetary[group_member.initial_gas_mix]
+ shared_mix.copy_from(planetary_mix)
+ shared_cached_moles = shared_mix.moles // Cache update
+ break
//"borrowing" this code from merge(), I need to play with the temp portion. Lets expand it out
//temperature = (giver.temperature * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity
var/capacity = mix.heat_capacity()
energy += mix.temperature * capacity
heat_cap += capacity
- var/list/giver_gases = mix.gases
- for(var/giver_id in giver_gases)
- ASSERT_GAS_IN_LIST(giver_id, shared_gases)
- shared_gases[giver_id][MOLES] += giver_gases[giver_id][MOLES]
+ for(var/gas_id, amount in mix.moles)
+ shared_cached_moles[gas_id] += amount
if(!imumutable_in_group)
shared_mix.temperature = energy / heat_cap
- for(var/id in shared_gases)
- shared_gases[id][MOLES] /= turflen
+ for(var/gas_id in shared_cached_moles)
+ shared_cached_moles[gas_id] /= turflen
shared_mix.garbage_collect()
for(var/turf/open/group_member as anything in turf_list)
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index cfb87ce7cb3f..9f02b092aab2 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -5,22 +5,14 @@ This prevents race conditions that arise based on the order of tile processing.
*/
GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm
-GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
-
-/proc/init_gaslist_cache()
- var/list/gases = list()
- for(var/id in GLOB.meta_gas_info)
- var/list/cached_gas = new(3)
-
- gases[id] = cached_gas
-
- cached_gas[MOLES] = 0
- cached_gas[ARCHIVE] = 0
- cached_gas[GAS_META] = GLOB.meta_gas_info[id]
- return gases
/datum/gas_mixture
- var/list/gases
+ /// Associative list of moles for each gas. List key is /datum/gas/, value is amount in moles
+ var/list/moles
+ /// Archived version of moles
+ var/list/moles_archive
+ /// Static list of gas meta data like heat capacity (initialized globally)
+ var/static/list/gas_meta
/// The temperature of the gas mix in kelvin. Should never be lower then TCMB
var/temperature = TCMB
/// Used, like all archived variables, to ensure turf sharing is consistent inside a tick, no matter
@@ -39,7 +31,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
var/pipeline_cycle = -1
/datum/gas_mixture/New(volume)
- gases = new
+ moles = list()
+ moles_archive = list()
if(!isnull(volume))
src.volume = volume
if(src.volume <= 0)
@@ -52,75 +45,74 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
///assert_gas(gas_id) - used to guarantee that the gas list for this id exists in gas_mixture.gases.
///Must be used before adding to a gas. May be used before reading from a gas.
/datum/gas_mixture/proc/assert_gas(gas_id)
- ASSERT_GAS(gas_id, src)
+ moles[gas_id] += 0
+ moles_archive[gas_id] += 0
-///assert_gases(args) - shorthand for calling ASSERT_GAS() once for each gas type.
+///assert_gases(args) - shorthand for calling assert_gas(gas_id) once for each gas type.
/datum/gas_mixture/proc/assert_gases(...)
- for(var/id in args)
- ASSERT_GAS(id, src)
+ var/cached_moles = moles
+ var/cached_moles_archive = moles_archive
+ for(var/gas_id in args)
+ cached_moles[gas_id] += 0
+ cached_moles_archive[gas_id] += 0
///add_gas(gas_id) - similar to assert_gas(), but does not check for an existing gas list for this id. This can clobber existing gases.
///Used instead of assert_gas() when you know the gas does not exist. Faster than assert_gas().
/datum/gas_mixture/proc/add_gas(gas_id)
- ADD_GAS(gas_id, gases)
+ moles[gas_id] = 0
+ moles_archive[gas_id] = 0
///add_gases(args) - shorthand for calling add_gas() once for each gas_type.
/datum/gas_mixture/proc/add_gases(...)
- var/cached_gases = gases
- for(var/id in args)
- ADD_GAS(id, cached_gases)
+ var/cached_moles = moles
+ var/cached_moles_archive = moles_archive
+ for(var/gas_id in args)
+ cached_moles[gas_id] = 0
+ cached_moles_archive[gas_id] = 0
///garbage_collect() - removes any gas list which is empty.
///If called with a list as an argument, only removes gas lists with IDs from that list.
///Must be used after subtracting from a gas. Must be used after assert_gas()
///if assert_gas() was called only to read from the gas.
///By removing empty gases, processing speed is increased.
-/datum/gas_mixture/proc/garbage_collect(list/tocheck)
- var/list/cached_gases = gases
- for(var/id in (tocheck || cached_gases))
- if(QUANTIZE(cached_gases[id][MOLES]) <= 0)
- cached_gases -= id
+/datum/gas_mixture/proc/garbage_collect()
+ values_cut_under(moles, MOLAR_ACCURACY, TRUE)
+ values_cut_under(moles_archive, MOLAR_ACCURACY, TRUE)
//PV = nRT
///joules per kelvin
-/datum/gas_mixture/proc/heat_capacity(data = MOLES)
- var/list/cached_gases = gases
- . = 0
- for(var/id in cached_gases)
- var/gas_data = cached_gases[id]
- . += gas_data[data] * gas_data[GAS_META][META_GAS_SPECIFIC_HEAT]
+/datum/gas_mixture/proc/heat_capacity()
+ return values_dot(moles, GAS_META[META_GAS_SPECIFIC_HEAT])
+
+///joules per kelvin. Same as heat_capacity() for moles_archive.
+// Separate function to reduce branches in a hot function
+/datum/gas_mixture/proc/heat_capacity_archive()
+ return values_dot(moles_archive, GAS_META[META_GAS_SPECIFIC_HEAT])
+
+/// Same as above except vacuums return HEAT_CAPACITY_VACUUM
+/datum/gas_mixture/turf/heat_capacity()
+ return values_dot(moles, GAS_META[META_GAS_SPECIFIC_HEAT]) || HEAT_CAPACITY_VACUUM
/// Same as above except vacuums return HEAT_CAPACITY_VACUUM
-/datum/gas_mixture/turf/heat_capacity(data = MOLES)
- var/list/cached_gases = gases
- . = 0
- for(var/id in cached_gases)
- var/gas_data = cached_gases[id]
- . += gas_data[data] * gas_data[GAS_META][META_GAS_SPECIFIC_HEAT]
- if(!.)
- . += HEAT_CAPACITY_VACUUM //we want vacuums in turfs to have the same heat capacity as space
+// Separate function to reduce branches in a hot function
+/datum/gas_mixture/turf/heat_capacity_archive()
+ return values_dot(moles_archive, GAS_META[META_GAS_SPECIFIC_HEAT]) || HEAT_CAPACITY_VACUUM
/// Calculate moles
/datum/gas_mixture/proc/total_moles()
- var/cached_gases = gases
- TOTAL_MOLES(cached_gases, .)
+ return values_sum(moles)
/// Checks to see if gas amount exists in mixture.
/// Do NOT use this in code where performance matters!
/// It's better to batch calls to garbage_collect(), especially in places where you're checking many gastypes
/datum/gas_mixture/proc/has_gas(gas_id, amount=0)
- ASSERT_GAS(gas_id, src)
- var/is_there_gas = amount < gases[gas_id][MOLES]
- garbage_collect()
- return is_there_gas
+ return amount < moles[gas_id]
/// Calculate pressure in kilopascals
/datum/gas_mixture/proc/return_pressure()
if(volume) // to prevent division by zero
- var/cached_gases = gases
- TOTAL_MOLES(cached_gases, .)
- return . * R_IDEAL_GAS_EQUATION * temperature / volume
+ return values_sum(moles) * R_IDEAL_GAS_EQUATION * temperature / volume
return 0
/// Calculate temperature in kelvins
@@ -134,7 +126,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
/// Gets the gas visuals for everything in this mixture
/datum/gas_mixture/proc/return_visuals(turf/z_context)
var/list/output
- GAS_OVERLAYS(gases, output, z_context)
+ GAS_OVERLAYS(moles, output, z_context)
return output
/// Calculate thermal energy in joules
@@ -143,11 +135,12 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
///Update archived versions of variables. Returns: 1 in all cases
/datum/gas_mixture/proc/archive()
- var/list/cached_gases = gases
+ var/list/cached_moles = moles
+ var/list/cached_moles_archive = moles_archive
temperature_archived = temperature
- for(var/id in cached_gases)
- cached_gases[id][ARCHIVE] = cached_gases[id][MOLES]
+ for(var/gas_id in cached_moles)
+ cached_moles_archive[gas_id] = cached_moles[gas_id]
return TRUE
@@ -164,34 +157,66 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
if(combined_heat_capacity)
temperature = (giver.temperature * giver_heat_capacity + temperature * self_heat_capacity) / combined_heat_capacity
- var/list/cached_gases = gases //accessing datum vars is slower than proc vars
- var/list/giver_gases = giver.gases
+ var/list/cached_moles = moles //accessing datum vars is slower than proc vars
+ var/list/cached_giver_moles = giver.moles
//gas transfer
- for(var/giver_id in giver_gases)
- ASSERT_GAS_IN_LIST(giver_id, cached_gases)
- cached_gases[giver_id][MOLES] += giver_gases[giver_id][MOLES]
+ for(var/gas_id in cached_giver_moles)
+ cached_moles[gas_id] += cached_giver_moles[gas_id]
SEND_SIGNAL(src, COMSIG_GASMIX_MERGED)
return TRUE
+// Set the gas specie within the gas mix to a set amount, if there is none it will be created at the target temp
+/datum/gas_mixture/proc/set_gas(gas_specie, amount)
+ moles[gas_specie] = amount
+ garbage_collect()
+
+/datum/gas_mixture/proc/set_temperature(target_temp)
+ temperature = target_temp
+
+/// Add a specific amount of moles to specified gas or add a new gas to the mix
+/// amount is added so make it negative to remove
+/datum/gas_mixture/proc/adjust_gas(gas, amount)
+ moles[gas] += QUANTIZE(amount)
+ garbage_collect()
+
+/// Add a specific amount of moles to all the gasses present or add a new gas to the mix
+///gases_moles is an associative list of gas species to their amount to be added
+/datum/gas_mixture/proc/adjust_multiple_gases(list/gases_moles)
+ var/cached_moles = moles
+ for(var/gas_id in gases_moles)
+ cached_moles[gas_id] += gases_moles[gas_id]
+ garbage_collect()
+
+
+/// Modify the gas list as to convert moles of gas species A to gas species B
+/// reactant and product are the gas species to convert and conversion_amount is the amount to be converted
+/datum/gas_mixture/proc/convert_gas(datum/gas/reactant, datum/gas/product, conversion_amount)
+ var/list/cached_moles = moles
+ assert_gases(reactant, product)
+ cached_moles[reactant] -= QUANTIZE(conversion_amount)
+ cached_moles[product] += QUANTIZE(conversion_amount)
+ garbage_collect()
+
///Proportionally removes amount of gas from the gas_mixture.
///Returns: gas_mixture with the gases removed
/datum/gas_mixture/proc/remove(amount)
- var/sum
- var/list/cached_gases = gases
- TOTAL_MOLES(cached_gases, sum)
- amount = min(amount, sum) //Can not take more air than tile has!
+
+ var/list/cached_moles = moles
+ var/total_moles = values_sum(cached_moles)
+ amount = min(amount, total_moles) //Can not take more air than tile has!
if(amount <= 0)
return null
- var/ratio = amount / sum
+ var/ratio = amount / total_moles
+
var/datum/gas_mixture/removed = new type(volume)
- var/list/removed_gases = removed.gases //accessing datum vars is slower than proc vars
+ var/list/cached_removed_moles = removed.moles //accessing datum vars is slower than proc vars
removed.temperature = temperature
- for(var/id in cached_gases)
- ADD_GAS(id, removed.gases)
- removed_gases[id][MOLES] = QUANTIZE(cached_gases[id][MOLES] * ratio)
- cached_gases[id][MOLES] -= removed_gases[id][MOLES]
+ for(var/id in cached_moles)
+ cached_removed_moles[id] = QUANTIZE(cached_moles[id] * ratio)
+ cached_moles[id] -= cached_removed_moles[id]
+
garbage_collect()
SEND_SIGNAL(src, COMSIG_GASMIX_REMOVED)
@@ -205,15 +230,14 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
return removed
ratio = min(ratio, 1)
- var/list/cached_gases = gases
+ var/list/cached_moles = moles
var/datum/gas_mixture/removed = new type(volume)
- var/list/removed_gases = removed.gases //accessing datum vars is slower than proc vars
+ var/list/cached_removed_moles = removed.moles //accessing datum vars is slower than proc vars
removed.temperature = temperature
- for(var/id in cached_gases)
- ADD_GAS(id, removed.gases)
- removed_gases[id][MOLES] = QUANTIZE(cached_gases[id][MOLES] * ratio)
- cached_gases[id][MOLES] -= removed_gases[id][MOLES]
+ for(var/id in cached_moles)
+ cached_removed_moles[id] = QUANTIZE(cached_moles[id] * ratio)
+ cached_moles[id] -= cached_removed_moles[id]
garbage_collect()
@@ -223,18 +247,17 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
///Removes an amount of a specific gas from the gas_mixture.
///Returns: gas_mixture with the gas removed
/datum/gas_mixture/proc/remove_specific(gas_id, amount)
- var/list/cached_gases = gases
- amount = min(amount, cached_gases[gas_id][MOLES])
+ var/list/cached_moles = moles
+ amount = min(amount, cached_moles[gas_id])
if(amount <= 0)
return null
var/datum/gas_mixture/removed = new type
- var/list/removed_gases = removed.gases
removed.temperature = temperature
- ADD_GAS(gas_id, removed.gases)
- removed_gases[gas_id][MOLES] = amount
- cached_gases[gas_id][MOLES] -= amount
+ removed.moles[gas_id] = amount
+ cached_moles[gas_id] -= amount
- garbage_collect(list(gas_id))
+ // TODO(antropod): maybe use if (cached_moles[gas_id] < MOLAR_ACCURACY) cached_moles -= gas_id?
+ garbage_collect()
return removed
/datum/gas_mixture/proc/remove_specific_ratio(gas_id, ratio)
@@ -242,16 +265,15 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
return null
ratio = min(ratio, 1)
- var/list/cached_gases = gases
+ var/list/cached_moles = moles
var/datum/gas_mixture/removed = new type
- var/list/removed_gases = removed.gases //accessing datum vars is slower than proc vars
+ var/list/cached_removed_moles = removed.moles //accessing datum vars is slower than proc vars
removed.temperature = temperature
- ADD_GAS(gas_id, removed.gases)
- removed_gases[gas_id][MOLES] = QUANTIZE(cached_gases[gas_id][MOLES] * ratio)
- cached_gases[gas_id][MOLES] -= removed_gases[gas_id][MOLES]
+ cached_removed_moles[gas_id] = QUANTIZE(cached_moles[gas_id] * ratio)
+ cached_moles[gas_id] -= cached_removed_moles[gas_id]
- garbage_collect(list(gas_id))
+ garbage_collect()
return removed
@@ -269,33 +291,33 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
var/min_p_delta = 0.1
var/total_volume = volume + other.volume
- var/list/gas_list = gases | other.gases
+ var/list/cached_moles = moles
+ var/list/cached_other_moles = other.moles
+ var/list/gas_list = cached_moles | cached_other_moles
for(var/gas_id in gas_list)
assert_gas(gas_id)
other.assert_gas(gas_id)
//math is under the assumption temperatures are equal
- if(abs(gases[gas_id][MOLES] / volume - other.gases[gas_id][MOLES] / other.volume) > min_p_delta / (R_IDEAL_GAS_EQUATION * temperature))
+ if(abs(cached_moles[gas_id] / volume - cached_other_moles[gas_id] / other.volume) > min_p_delta / (R_IDEAL_GAS_EQUATION * temperature))
. = TRUE
- var/total_moles = gases[gas_id][MOLES] + other.gases[gas_id][MOLES]
- gases[gas_id][MOLES] = total_moles * (volume/total_volume)
- other.gases[gas_id][MOLES] = total_moles * (other.volume/total_volume)
+ var/total_moles = cached_moles[gas_id] + cached_other_moles[gas_id]
+ cached_moles[gas_id] = total_moles * (volume/total_volume)
+ cached_other_moles[gas_id] = total_moles * (other.volume/total_volume)
garbage_collect()
other.garbage_collect()
///Creates new, identical gas mixture
///Returns: duplicate gas mixture
/datum/gas_mixture/proc/copy()
- // Type as /list/list to make spacemandmm happy with the inlined access we do down there
- var/list/list/cached_gases = gases
+ var/list/cached_moles = moles
var/datum/gas_mixture/copy = new type
- var/list/copy_gases = copy.gases
+ var/list/copy_cached_moles = copy.moles
+ var/list/copy_cached_moles_archive = copy.moles_archive
copy.temperature = temperature
- for(var/id in cached_gases)
- // Sort of a sideways way of doing ADD_GAS()
- // Faster tho, gotta save those cpu cycles
- copy_gases[id] = cached_gases[id].Copy()
- copy_gases[id][ARCHIVE] = 0
+ for(var/gas_id in cached_moles)
+ copy_cached_moles[gas_id] = cached_moles[gas_id]
+ copy_cached_moles_archive[gas_id] = 0
return copy
@@ -303,33 +325,33 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
///Copies variables from sample
///Returns: TRUE if we are mutable, FALSE otherwise
/datum/gas_mixture/proc/copy_from(datum/gas_mixture/sample)
- var/list/cached_gases = gases //accessing datum vars is slower than proc vars
- // Type as /list/list to make spacemandmm happy with the inlined access we do down there
- var/list/list/sample_gases = sample.gases
+ var/list/cached_moles = moles //accessing datum vars is slower than proc vars
+ var/list/cached_moles_archive = moles_archive
+ var/list/sample_cached_moles = sample.moles
//remove all gases
- cached_gases.Cut()
+ cached_moles.Cut()
+ cached_moles_archive.Cut()
temperature = sample.temperature
- for(var/id in sample_gases)
- cached_gases[id] = sample_gases[id].Copy()
- cached_gases[id][ARCHIVE] = 0
+ for(var/gas_id in sample_cached_moles)
+ cached_moles[gas_id] = sample_cached_moles[gas_id]
+ cached_moles_archive[gas_id] = 0
return TRUE
///Copies variables from sample, moles multiplicated by partial
///Returns: TRUE if we are mutable, FALSE otherwise
/datum/gas_mixture/proc/copy_from_ratio(datum/gas_mixture/sample, partial = 1)
- var/list/cached_gases = gases //accessing datum vars is slower than proc vars
- var/list/sample_gases = sample.gases
+ var/list/cached_moles = moles //accessing datum vars is slower than proc vars
+ var/list/sample_cached_moles = sample.moles
//remove all gases not in the sample
- cached_gases &= sample_gases
+ cached_moles &= sample_cached_moles
temperature = sample.temperature
- for(var/id in sample_gases)
- ASSERT_GAS_IN_LIST(id, cached_gases)
- cached_gases[id][MOLES] = sample_gases[id][MOLES] * partial
+ for(var/gas_id in sample_cached_moles)
+ cached_moles[gas_id] = sample_cached_moles[gas_id] * partial
return TRUE
@@ -338,18 +360,20 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
/// If we don't retain this, we will get negative moles. Don't do it
/// Returns: amount of gas exchanged (+ if sharer received)
/datum/gas_mixture/proc/share(datum/gas_mixture/sharer, our_coeff, sharer_coeff)
- var/list/cached_gases = gases
- var/list/sharer_gases = sharer.gases
+ var/list/cached_moles = moles
+ var/list/cached_moles_archive = moles_archive
+ var/list/sharer_cached_moles = sharer.moles
+ var/list/sharer_cached_moles_archive = sharer.moles_archive
- var/list/only_in_sharer = sharer_gases - cached_gases
- var/list/only_in_cached = cached_gases - sharer_gases
+ var/list/only_in_sharer = sharer_cached_moles - cached_moles
+ var/list/only_in_cached = cached_moles - sharer_cached_moles
var/temperature_delta = temperature_archived - sharer.temperature_archived
- var/abs_temperature_delta = abs(temperature_delta)
+ var/temp_delta_threshold = abs(temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER
var/old_self_heat_capacity = 0
var/old_sharer_heat_capacity = 0
- if(abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ if(temp_delta_threshold)
old_self_heat_capacity = heat_capacity()
old_sharer_heat_capacity = sharer.heat_capacity()
@@ -362,15 +386,16 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
//GAS TRANSFER
//Prep
- for(var/id in only_in_sharer) //create gases not in our cache
- ADD_GAS(id, cached_gases)
- for(var/id in only_in_cached) //create gases not in the sharing mix
- ADD_GAS(id, sharer_gases)
+ for(var/gas_id in only_in_sharer) //create gases not in our cache
+ cached_moles[gas_id] = 0
+ cached_moles_archive[gas_id] = 0
+ for(var/gas_id in only_in_cached) //create gases not in the sharing mix
+ sharer_cached_moles[gas_id] = 0
+ sharer_cached_moles_archive[gas_id] = 0
- for(var/id in cached_gases) //transfer gases
- var/gas = cached_gases[id]
- var/sharergas = sharer_gases[id]
- var/delta = QUANTIZE(gas[ARCHIVE] - sharergas[ARCHIVE]) //the amount of gas that gets moved between the mixtures
+ var/list/cached_specific_heat = GAS_META[META_GAS_SPECIFIC_HEAT]
+ for(var/gas_id in cached_moles) //transfer gases
+ var/delta = QUANTIZE(cached_moles_archive[gas_id] - sharer_cached_moles_archive[gas_id]) //the amount of gas that gets moved between the mixtures
if(!delta)
continue
@@ -382,22 +407,22 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
else
delta = delta * sharer_coeff
- if(abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
- var/gas_heat_capacity = delta * gas[GAS_META][META_GAS_SPECIFIC_HEAT]
+ if(temp_delta_threshold)
+ var/gas_heat_capacity = delta * cached_specific_heat[gas_id]
if(delta > 0)
heat_capacity_self_to_sharer += gas_heat_capacity
else
heat_capacity_sharer_to_self -= gas_heat_capacity //subtract here instead of adding the absolute value because we know that delta is negative.
- gas[MOLES] -= delta
- sharergas[MOLES] += delta
+ cached_moles[gas_id] -= delta
+ sharer_cached_moles[gas_id] += delta
moved_moles += delta
abs_moved_moles += abs(delta)
last_share = abs_moved_moles
//THERMAL ENERGY TRANSFER
- if(abs_temperature_delta > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
+ if(temp_delta_threshold)
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
@@ -420,10 +445,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
sharer.garbage_collect()
if(temperature_delta > MINIMUM_TEMPERATURE_TO_MOVE || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
- var/our_moles
- TOTAL_MOLES(cached_gases,our_moles)
- var/their_moles
- TOTAL_MOLES(sharer_gases,their_moles)
+ var/our_moles = values_sum(cached_moles)
+ var/their_moles = values_sum(sharer_cached_moles)
return (temperature_archived*(our_moles + moved_moles) - sharer.temperature_archived*(their_moles - moved_moles)) * R_IDEAL_GAS_EQUATION / volume
///Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
@@ -434,8 +457,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
sharer_temperature = sharer.temperature_archived
var/temperature_delta = temperature_archived - sharer_temperature
if(abs(temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)
- var/self_heat_capacity = heat_capacity(ARCHIVE)
- sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity(ARCHIVE)
+ var/self_heat_capacity = heat_capacity_archive()
+ sharer_heat_capacity = sharer_heat_capacity || sharer.heat_capacity_archive()
if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY))
// coefficient applied first because some turfs have very big heat caps.
@@ -451,27 +474,26 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
//thermal energy of the system (self and sharer) is unchanged
///Compares sample to self to see if within acceptable ranges that group processing may be enabled
+///Takes the bool as a second arg to read to read archived values for moles and temperature
///Returns: a string indicating what check failed, or "" if check passes
-/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
- var/list/sample_gases = sample.gases //accessing datum vars is slower than proc vars
- var/list/cached_gases = gases
- var/moles_sum = 0
-
- for(var/id in cached_gases | sample_gases) // compare gases from either mixture
- // Yes this is actually fast. I too hate it here
- var/gas_moles = cached_gases[id]?[MOLES] || 0
- var/sample_moles = sample_gases[id]?[MOLES] || 0
- // Brief explanation. We are much more likely to not pass this first check then pass the first and fail the second
- // Because of this, double calculating the delta is FASTER then inserting it into a var
- if(abs(gas_moles - sample_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
- if(abs(gas_moles - sample_moles) > gas_moles * MINIMUM_AIR_RATIO_TO_MOVE)
- return id
- // similarly, we will rarely get cut off, so this is cheaper then doing it later
- moles_sum += gas_moles
-
- if(moles_sum > MINIMUM_MOLES_DELTA_TO_MOVE) //Don't consider temp if there's not enough mols
- if(abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
- return "temp"
+/datum/gas_mixture/proc/compare(datum/gas_mixture/sample, cmp_archive)
+ var/list/cached_moles = (cmp_archive) ? moles_archive : moles
+ var/list/sample_cached_moles = (cmp_archive) ? sample.moles_archive : sample.moles //accessing datum vars is slower than proc vars
+
+ for(var/gas_id in cached_moles | sample_cached_moles) // compare gases from either mixture
+ var/gas_moles = cached_moles[gas_id] // it can be null, but everything coerce to 0 after, so we save JMP and Tst
+ var/sample_moles = sample_cached_moles[gas_id]
+ var/abs_delta = abs(gas_moles - sample_moles)
+ if((abs_delta > MINIMUM_MOLES_DELTA_TO_MOVE) && (abs_delta > gas_moles * MINIMUM_AIR_RATIO_TO_MOVE))
+ return gas_id
+
+ if(values_sum(cached_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) //Don't consider temp if there's not enough mols
+ if(cmp_archive)
+ if(abs(temperature_archived - sample.temperature_archived) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return "temp"
+ else
+ if(abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return "temp"
return ""
@@ -479,8 +501,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
///Returns: 1 if any reaction took place; 0 otherwise
/datum/gas_mixture/proc/react(datum/holder)
. = NO_REACTION
- var/list/cached_gases = gases
- if(!length(cached_gases))
+ var/list/cached_moles = moles
+ if(!length(cached_moles))
return
var/list/pre_formation = list()
@@ -488,7 +510,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
var/list/post_formation = list()
var/list/fires = list()
var/list/gas_reactions = SSair.gas_reactions
- for(var/gas_id in cached_gases)
+ for(var/gas_id in cached_moles)
var/list/reaction_set = gas_reactions[gas_id]
if(!reaction_set)
continue
@@ -503,14 +525,14 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
return
//Fuck you
- if(cached_gases[/datum/gas/hypernoblium] && cached_gases[/datum/gas/hypernoblium][MOLES] >= REACTION_OPPRESSION_THRESHOLD && temperature > 20)
+ if(cached_moles[/datum/gas/hypernoblium] >= REACTION_OPPRESSION_THRESHOLD && temperature > REACTION_OPPRESSION_MIN_TEMP)
return STOP_REACTIONS
reaction_results = new
//It might be worth looking into updating these after each reaction, but that makes us care more about order of operations, so be careful
var/temp = temperature
reaction_loop:
- for(var/datum/gas_reaction/reaction as anything in reactions)
+ for(var/datum/gas_reaction/standard/reaction as anything in reactions)
var/list/reqs = reaction.requirements
if((reqs["MIN_TEMP"] && temp < reqs["MIN_TEMP"]) || (reqs["MAX_TEMP"] && temp > reqs["MAX_TEMP"]))
@@ -519,7 +541,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
for(var/id in reqs)
if (id == "MIN_TEMP" || id == "MAX_TEMP")
continue
- if(!cached_gases[id] || cached_gases[id][MOLES] < reqs[id])
+ if(cached_moles[id] < reqs[id])
continue reaction_loop
//at this point, all requirements for the reaction are satisfied. we can now react()
@@ -534,8 +556,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
/**
* Returns the partial pressure of the gas in the breath based on BREATH_VOLUME
* eg:
- * Plas_PP = get_breath_partial_pressure(gas_mixture.gases[/datum/gas/plasma][MOLES])
- * O2_PP = get_breath_partial_pressure(gas_mixture.gases[/datum/gas/oxygen][MOLES])
+ * Plas_PP = get_breath_partial_pressure(gas_mixture.moles[/datum/gas/plasma])
+ * O2_PP = get_breath_partial_pressure(gas_mixture.moles[/datum/gas/oxygen])
* get_breath_partial_pressure(gas_mole_count) --> PV = nRT, P = nRT/V
*
* 10/20*5 = 2.5
@@ -719,3 +741,73 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
output_air.merge(removed)
return TRUE
+
+/**
+ * Calls for electrolyzer_reaction reactions on the gas_mixture.
+ * Arguments:
+ * * working_power - working_power to use for the electrolyzer_reaction reactions.
+ * * electrolyzer_args - electrolysis arguments to use for the electrolyzer_reaction reactions.
+ */
+// /datum/gas_mixture/proc/electrolyze(working_power = 0, electrolyzer_args = list())
+// for(var/reaction in GLOB.electrolyzer_reactions)
+// var/datum/gas_reaction/electrolyzer/current_reaction = GLOB.electrolyzer_reactions[reaction]
+
+// if(!current_reaction.reaction_check(air_mixture = src, electrolyzer_args = electrolyzer_args))
+// continue
+
+// current_reaction.react(air_mixture = src, working_power = working_power, electrolyzer_args = electrolyzer_args)
+
+// garbage_collect()
+
+/// Convert a gas mixture to a string (ie. "o2=22;n2=82;TEMP=180")
+/// Rounds all temperature and gases to 0.01 and skips any gases less than that amount
+/datum/gas_mixture/proc/to_string()
+ var/list/cached_moles = moles
+ var/rounded_temp = round(temperature, 0.01)
+
+ var/list/atmos_contents = list()
+ var/temperature_str = "TEMP=[num2text(rounded_temp)]"
+
+ if(!length(cached_moles) || total_moles() < 0.01)
+ return temperature_str
+
+ var/list/cached_gas_id = GAS_META[META_GAS_ID]
+ for(var/gas_id, gas_moles in cached_moles)
+ gas_moles = round(gas_moles, 0.01)
+ if(gas_moles >= 0.01)
+ atmos_contents += "[cached_gas_id[gas_id]]=[num2text(gas_moles)]"
+
+ atmos_contents += temperature_str
+ return atmos_contents.Join(";")
+
+/**
+ * A simple helper proc that checks if the contents of a list of gases are within acceptable terms.
+ *
+ * Arguments:
+ * * acceptable_gas_bounds: An associated list of gas types and acceptable boundaries in moles. e.g. /datum/gas/oxygen = list(16, 30)
+ * * * if the assoc list is null, then it'll be considered a safe gas and won't return FALSE.
+ * * extraneous_gas_limit: If a gas not in gases is found, this is the limit above which the proc will return FALSE.
+ *
+ * Returns TRUE if the list of gases is acceptable, FALSE otherwise.
+ */
+/datum/gas_mixture/proc/check_gases(list/acceptable_gas_bounds, extraneous_gas_limit = 0.1)
+ SHOULD_BE_PURE(TRUE)
+
+ var/list/gases_to_check = acceptable_gas_bounds.Copy() // thank you spaceman
+ var/list/cached_moles = moles
+ for(var/id in cached_moles)
+ var/gas_moles = cached_moles[id]
+ if(!(id in gases_to_check))
+ if(gas_moles > extraneous_gas_limit)
+ return FALSE
+ continue
+ var/list/boundaries = gases_to_check[id]
+ if(boundaries && !ISINRANGE(gas_moles, boundaries[1], boundaries[2]))
+ return FALSE
+ gases_to_check -= id
+ ///Check that gases absent from the turf have a lower boundary of zero or none at all, otherwise return FALSE
+ for(var/id in gases_to_check)
+ var/list/boundaries = gases_to_check[id]
+ if(boundaries && boundaries[1] > 0)
+ return FALSE
+ return TRUE
diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm
index ed655f1e59b4..ae50f80047e4 100644
--- a/code/modules/atmospherics/gasmixtures/gas_types.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_types.dm
@@ -1,21 +1,24 @@
/proc/meta_gas_list()
- . = subtypesof(/datum/gas)
- for(var/gas_path in .)
- var/list/gas_info = new(8)
- var/datum/gas/gas = gas_path
+ var/list/gas_info = new (META_GAS_LENGTH)
+ for (var/array_idx in 1 to gas_info.len)
+ gas_info[array_idx] = list()
- gas_info[META_GAS_SPECIFIC_HEAT] = initial(gas.specific_heat)
- gas_info[META_GAS_NAME] = initial(gas.name)
+ var/list/gas_types = subtypesof(/datum/gas)
+ ASSERT(GAS_TYPE_COUNT == length(gas_types),\
+ "GAS_TYPE_COUNT != length(subtypesof(gas_types)), if you added new gas please increment GAS_TYPE_COUNT")
+ for(var/datum/gas/gas_path as anything in gas_types)
+ gas_info[META_GAS_SPECIFIC_HEAT][gas_path] = initial(gas_path.specific_heat)
+ gas_info[META_GAS_NAME][gas_path] = initial(gas_path.name)
+ gas_info[META_GAS_MOLES_VISIBLE][gas_path] = initial(gas_path.moles_visible)
+ if (gas_info[META_GAS_MOLES_VISIBLE][gas_path])
+ gas_info[META_GAS_OVERLAY][gas_path] += generate_gas_overlays(0, SSmapping.max_plane_offset, gas_path)
+ gas_info[META_GAS_FUSION_POWER][gas_path] = initial(gas_path.fusion_power)
+ gas_info[META_GAS_DANGER][gas_path] = initial(gas_path.cargo_flags) & GAS_DANGEROUS
+ gas_info[META_GAS_ID][gas_path] = initial(gas_path.id)
+ gas_info[META_GAS_DESC][gas_path] = initial(gas_path.desc)
- gas_info[META_GAS_MOLES_VISIBLE] = initial(gas.moles_visible)
- if(initial(gas.moles_visible) != null)
- gas_info[META_GAS_OVERLAY] = generate_gas_overlays(0, SSmapping.max_plane_offset, gas)
-
- gas_info[META_GAS_FUSION_POWER] = initial(gas.fusion_power)
- gas_info[META_GAS_DANGER] = initial(gas.dangerous)
- gas_info[META_GAS_ID] = initial(gas.id)
- gas_info[META_GAS_DESC] = initial(gas.desc)
- .[gas_path] = gas_info
+ /datum/gas_mixture::gas_meta = gas_info // save the reference to the list
+ return gas_info
/proc/generate_gas_overlays(old_offset, new_offset, datum/gas/gas_type)
var/list/to_return = list()
@@ -28,43 +31,44 @@
return to_return
/proc/gas_id2path(id)
- var/list/meta_gas = GLOB.meta_gas_info
- if(id in meta_gas)
+ var/list/meta_gas_id = GLOB.meta_gas_info[META_GAS_ID]
+ if(id in meta_gas_id)
return id
- for(var/path in meta_gas)
- if(meta_gas[path][META_GAS_ID] == id)
+ for(var/path in meta_gas_id)
+ if(meta_gas_id[path] == id)
return path
return ""
-/*||||||||||||||/----------\||||||||||||||*\
-||||||||||||||||[GAS DATUMS]||||||||||||||||
-||||||||||||||||\__________/||||||||||||||||
-|||| These should never be instantiated. ||||
-|||| They exist only to make it easier ||||
-|||| to add a new gas. They are accessed ||||
-|||| only by meta_gas_list(). ||||
-\*||||||||||||||||||||||||||||||||||||||||*/
-
-//This is a plot created using the values for gas exports. Each gas has a value that works as it's kind of soft-cap, which limits you from making billions of credits per sale, based on the base_value variable on the gasses themselves. Most of these gasses as a result have a rather low value when sold, like nitrogen and oxygen at 1500 and 600 respectively at their maximum value. The
+/**
+ * # Gas datums
+ *
+ * These are never and should never be instantiated,
+ * they just exist to hold data which is passed into the gas metadata list.
+ *
+ * They are templates for how gases should generally act.
+ */
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
+ var/desc
///icon_state in icons/effects/atmospherics.dmi
var/gas_overlay = ""
var/moles_visible = null
- ///currently used by canisters
- var/dangerous = FALSE
///How much the gas accelerates a fusion reaction
var/fusion_power = 0
/// relative rarity compared to other gases, used when setting up the reactions list.
var/rarity = 0
- ///Can gas of this type can purchased through cargo?
- var/purchaseable = FALSE
- ///How does a single mole of this gas sell for? Formula to calculate maximum value is in code\modules\cargo\exports\large_objects.dm. Doesn't matter for roundstart gasses.
+ /// Flags that relate to how the gas is handled in cargo
+ /// GAS_PURCHASABLE - Can be purchased in cargo
+ /// GAS_EXPORTABLE - Can be sold in cargo
+ /// GAS_DANGEROUS - Is considered dangerous, requires elevated access to purchase
+ var/cargo_flags = NONE
+ /// How does a single mole of this gas buy/sell for?
+ /// Formula to calculate maximum value is in [code\modules\cargo\exports\large_objects.dm].
+ /// Only necessary for exportable or purchasable gases, otherwise meaningless.
var/base_value = 0
- var/desc
- ///RGB code for use when a generic color representing the gas is needed. Colors taken from contants.ts
+ /// RGB code for use when a generic color representing the gas is needed. Colors taken from contants.ts
var/primary_color
/// Smell string or typepath for this gas
var/smell
@@ -74,7 +78,7 @@
specific_heat = 20
name = "Oxygen"
rarity = 900
- purchaseable = TRUE
+ cargo_flags = GAS_PURCHASABLE
base_value = 0.2
desc = "The gas most life forms need to be able to survive. Also an oxidizer."
primary_color = "#0000ff"
@@ -84,7 +88,7 @@
specific_heat = 20
name = "Nitrogen"
rarity = 1000
- purchaseable = TRUE
+ cargo_flags = GAS_PURCHASABLE
base_value = 0.1
desc = "A very common gas that used to pad artifical atmospheres to habitable pressure."
primary_color = "#ffff00"
@@ -93,11 +97,11 @@
id = GAS_CO2
specific_heat = 30
name = "Carbon Dioxide"
- dangerous = TRUE
rarity = 700
- purchaseable = TRUE
+ cargo_flags = GAS_PURCHASABLE | GAS_DANGEROUS
base_value = 0.2
- desc = "What the fuck is carbon dioxide?"
+ desc = "What the fuck is Carbon Dioxide?"
+ // desc = "A colorless, odorless gas commonly produced by respiration and combustion. Potentially dangerous when inhaled in high concentrations."
primary_color = COLOR_GRAY
/datum/gas/plasma
@@ -106,10 +110,10 @@
name = "Plasma"
gas_overlay = "plasma"
moles_visible = MOLES_GAS_VISIBLE
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS
rarity = 800
base_value = 1.5
- desc = "A flammable gas with many other curious properties. It's research is one of NT's primary objective."
+ desc = "A flammable gas with many curious properties. Its research is one of Nanotrasen's primary objectives."
primary_color = "#ffc0cb"
smell = /datum/smell/plasma
@@ -121,7 +125,7 @@
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 8
rarity = 500
- purchaseable = TRUE
+ cargo_flags = GAS_PURCHASABLE
base_value = 0.5
desc = "Water, in gas form. Makes things slippery."
primary_color = "#b0c4de"
@@ -129,13 +133,14 @@
/datum/gas/hypernoblium
id = GAS_HYPER_NOBLIUM
specific_heat = 2000
- name = "Hyper-noblium"
+ name = "Hyper-Noblium"
gas_overlay = "freon"
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 10
rarity = 50
+ cargo_flags = GAS_EXPORTABLE
base_value = 2.5
- desc = "The most noble gas of them all. High quantities of hyper-noblium actively prevents reactions from occuring."
+ desc = "The most noble gas of them all. High quantities actively prevents reactions from occurring."
primary_color = COLOR_TEAL
/datum/gas/nitrous_oxide
@@ -145,11 +150,11 @@
gas_overlay = "nitrous_oxide"
moles_visible = MOLES_GAS_VISIBLE * 2
fusion_power = 10
- dangerous = TRUE
rarity = 600
- purchaseable = TRUE
+ cargo_flags = GAS_PURCHASABLE | GAS_DANGEROUS
base_value = 1.5
- desc = "Causes drowsiness, euphoria, and eventually unconsciousness."
+ desc = "A gas known to causes drowsiness, euphoria, and eventually unconsciousness. \
+ Commonly used as an anesthetic for surgical procedures, and occasionally, as a recreational drug."
primary_color = "#ffe4c4"
smell = "sweet"
@@ -160,10 +165,10 @@
fusion_power = 7
gas_overlay = "nitrium"
moles_visible = MOLES_GAS_VISIBLE
- dangerous = TRUE
+ cargo_flags = GAS_PURCHASABLE | GAS_DANGEROUS
rarity = 1
base_value = 6
- desc = "An experimental performance enhancing gas. Nitrium can have amplified effects as more of it gets into your bloodstream."
+ desc = "An experimental (and slightly toxic) performance enhancing gas that increases speed and alertness when inhaled."
primary_color = "#a52a2a"
smell = "coffee"
@@ -173,7 +178,7 @@
name = "Tritium"
gas_overlay = "tritium"
moles_visible = MOLES_GAS_VISIBLE
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
fusion_power = 5
rarity = 300
base_value = 2.5
@@ -184,10 +189,9 @@
id = GAS_BZ
specific_heat = 20
name = "BZ"
- dangerous = TRUE
fusion_power = 8
rarity = 400
- purchaseable = TRUE
+ cargo_flags = GAS_PURCHASABLE | GAS_EXPORTABLE | GAS_DANGEROUS
base_value = 1.5
desc = "A powerful hallucinogenic nerve agent able to induce cognitive damage."
primary_color = "#9370db"
@@ -199,8 +203,10 @@
name = "Pluoxium"
fusion_power = -10
rarity = 200
+ cargo_flags = GAS_EXPORTABLE
base_value = 2.5
- desc = "A gas that could supply even more oxygen to the bloodstream when inhaled, without being an oxidizer."
+ desc = "An alternative to oxygen that is eight times more efficient at lung diffusion \
+ and even has minor healing properties - while itself not being an oxidizer."
primary_color = "#7b68ee"
smell = "disinfectant"
@@ -208,12 +214,13 @@
id = GAS_MIASMA
specific_heat = 20
name = "Miasma"
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "miasma"
moles_visible = MOLES_GAS_VISIBLE * 60
rarity = 250
base_value = 1
- desc = "Not necessarily a gas, miasma refers to biological pollutants found in the atmosphere."
+ desc = "Miasma is not necessarily a gas, but more broadly refers to biological pollutants \
+ found in the atmosphere known to cause disease and nausea."
primary_color = COLOR_OLIVE
smell = /datum/smell/miasma
@@ -221,20 +228,20 @@
id = GAS_FREON
specific_heat = 600
name = "Freon"
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "freon"
moles_visible = MOLES_GAS_VISIBLE *30
fusion_power = -5
rarity = 10
base_value = 5
- desc = "A coolant gas. Mainly used for it's endothermic reaction with oxygen."
+ desc = "A coolant gas. Primarily used for its endothermic reaction with oxygen."
primary_color = "#afeeee"
/datum/gas/hydrogen
id = GAS_HYDROGEN
specific_heat = 15
name = "Hydrogen"
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
fusion_power = 2
rarity = 600
base_value = 1
@@ -245,25 +252,26 @@
id = GAS_HEALIUM
specific_heat = 10
name = "Healium"
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "healium"
moles_visible = MOLES_GAS_VISIBLE
rarity = 300
base_value = 5.5
- desc = "Causes deep, regenerative sleep."
+ desc = "An experimental alternative to anesthetic that induces a state of unconsciousness \
+ that accelerates healing and regeneration when inhaled."
primary_color = "#fa8072"
smell = "sweet"
/datum/gas/proto_nitrate
id = GAS_PROTO_NITRATE
specific_heat = 30
- name = "Proto Nitrate"
- dangerous = TRUE
+ name = "Proto-Nitrate"
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "proto_nitrate"
moles_visible = MOLES_GAS_VISIBLE
rarity = 200
base_value = 2.5
- desc = "A very volatile gas that reacts differently with various gases."
+ desc = "A volatile gas that has wildly different reactions with other gases."
primary_color = "#adff2f"
smell = "ozone"
@@ -271,12 +279,13 @@
id = GAS_ZAUKER
specific_heat = 350
name = "Zauker"
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "zauker"
moles_visible = MOLES_GAS_VISIBLE
rarity = 1
base_value = 7
- desc = "A highly toxic gas, it's production is highly regulated on top of being difficult. It also breaks down when in contact with nitrogen."
+ desc = "A highly toxic gas and difficult to produce gas. \
+ It breaks down when in contact with Nitrogen, making it relatively safe in Earth-like atmospheres."
primary_color = "#006400"
smell = "death"
@@ -284,12 +293,12 @@
id = GAS_HALON
specific_heat = 175
name = "Halon"
- dangerous = TRUE
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "halon"
moles_visible = MOLES_GAS_VISIBLE
rarity = 300
base_value = 4
- desc = "A potent fire supressant. Removes oxygen from high temperature fires and cools down the area"
+ desc = "A potent fire suppressant. It removes Oxygen from high temperature fires and cools down the area."
primary_color = COLOR_PURPLE
/datum/gas/helium
@@ -298,21 +307,24 @@
name = "Helium"
fusion_power = 7
rarity = 50
+ cargo_flags = GAS_EXPORTABLE
base_value = 3.5
- desc = "A very inert gas produced by the fusion of hydrogen and it's derivatives."
+ desc = "An inert noble gas produced by the fusion of Hydrogen and its derivatives. \
+ Commonly known for being lighter than air and its ability to raise voice pitch when inhaled."
primary_color = "#f0f8ff"
/datum/gas/antinoblium
id = GAS_ANTINOBLIUM
specific_heat = 1
- name = "Antinoblium"
- dangerous = TRUE
+ name = "Anti-Noblium"
+ cargo_flags = GAS_DANGEROUS | GAS_EXPORTABLE
gas_overlay = "antinoblium"
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 20
rarity = 1
base_value = 10
- desc = "We still don't know what it does, but it sells for a lot."
+ desc = "A mysterious and highly reactive gas known to replicate itself. \
+ It is highly sought after due to its rarity, and will export for a high price."
primary_color = COLOR_MAROON
smell = "yourself"
diff --git a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm
index ab0517f9d02c..dce8ab87ed5d 100644
--- a/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm
+++ b/code/modules/atmospherics/gasmixtures/immutable_mixtures.dm
@@ -10,9 +10,9 @@
garbage_collect()
/datum/gas_mixture/immutable/garbage_collect()
- temperature = initial_temperature
- temperature_archived = initial_temperature
- gases.Cut()
+ temperature = temperature_archived = initial_temperature
+ moles.Cut()
+ moles_archive.Cut()
/datum/gas_mixture/immutable/archive()
return TRUE //nothing changes, so we do nothing and the archive is successful
@@ -58,13 +58,11 @@
/datum/gas_mixture/immutable/planetary
var/list/initial_gas = list()
+// Intentionally duplicate code to save microseconds on a call to parent
/datum/gas_mixture/immutable/planetary/garbage_collect()
- ..()
- gases.Cut()
- for(var/id in initial_gas)
- ADD_GAS(id, gases)
- gases[id][MOLES] = initial_gas[id][MOLES]
- gases[id][ARCHIVE] = initial_gas[id][ARCHIVE]
+ temperature = temperature_archived = initial_temperature
+ moles = initial_gas.Copy()
+ moles_archive = initial_gas.Copy()
/datum/gas_mixture/immutable/planetary/proc/parse_string_immutable(gas_string) //I know I know, I need this tho
gas_string = SSair.preprocess_gas_string(gas_string)
@@ -81,13 +79,11 @@
var/path = id
if(!ispath(path))
path = gas_id2path(path) //a lot of these strings can't have embedded expressions (especially for mappers), so support for IDs needs to stick around
- ADD_GAS(path, mix)
- mix[path][MOLES] = text2num(gas[id])
- mix[path][ARCHIVE] = mix[path][MOLES]
-
- for(var/id in mix)
- ADD_GAS(id, gases)
- gases[id][MOLES] = mix[id][MOLES]
- gases[id][ARCHIVE] = mix[id][MOLES]
+ mix[path] = text2num(gas[id])
+
+ var/list/cached_moles = moles
+ var/list/cached_moles_archive = moles_archive
+ for(var/gas_id in mix)
+ cached_moles[gas_id] = cached_moles_archive[gas_id] = mix[gas_id]
diff --git a/code/modules/atmospherics/gasmixtures/reaction_factors.dm b/code/modules/atmospherics/gasmixtures/reaction_factors.dm
index 1b96a8976837..88eb79e3940e 100644
--- a/code/modules/atmospherics/gasmixtures/reaction_factors.dm
+++ b/code/modules/atmospherics/gasmixtures/reaction_factors.dm
@@ -1,208 +1,269 @@
-/datum/gas_reaction/water_vapor/init_factors()
+/datum/gas_reaction/standard/water_vapor/init_factors()
factor = list(
- /datum/gas/water_vapor = "Condensation will consume [MOLES_GAS_VISIBLE] moles, freezing will not consume any. Both needs a minimum of [MOLES_GAS_VISIBLE] moles to occur.",
- "Temperature" = "Freezes a turf at [WATER_VAPOR_DEPOSITION_POINT] Kelvins or below, wets it at [WATER_VAPOR_CONDENSATION_POINT] Kelvins or below.",
- "Location" = "Can only happen on turfs.",
+ /datum/gas/water_vapor = "Condensation will consume [MOLES_GAS_VISIBLE] moles. \
+ Freezing will not consume any. Both requires a minimum of [MOLES_GAS_VISIBLE] moles to occur.",
+ "Temperature" = "Freezes a tile at [WATER_VAPOR_DEPOSITION_POINT]K or below, \
+ wets it at [WATER_VAPOR_CONDENSATION_POINT]K or below.",
+ "Location" = "Can only happen on tiles.",
)
-/datum/gas_reaction/miaster/init_factors()
+/datum/gas_reaction/standard/miaster/init_factors()
factor = list(
- /datum/gas/miasma = "Miasma is sterilized at a rate that scales with the difference between the temperature and [MIASTER_STERILIZATION_TEMP]K.",
- /datum/gas/oxygen = "One mole of oxygen is released per mole of miasma consumed.",
- "Temperature" = "Higher temperature increases the speed of miasma sterilization.",
- "Energy" = "[MIASTER_STERILIZATION_ENERGY] joules of energy is released per mole of miasma sterilized.",
+ /datum/gas/miasma = "[/datum/gas/miasma::name] is sterilized at a rate that scales \
+ with the difference between the temperature and [MIASTER_STERILIZATION_TEMP]K.",
+ /datum/gas/oxygen = "One mole of [/datum/gas/oxygen::name] is released per mole of [/datum/gas/miasma::name] consumed.",
+ "Temperature" = "Higher temperature increases the speed of [/datum/gas/miasma::name] sterilization.",
+ "Energy" = "[MIASTER_STERILIZATION_ENERGY] joules of energy is released per mole of [/datum/gas/miasma::name] sterilized.",
)
-/datum/gas_reaction/plasmafire/init_factors()
+/datum/gas_reaction/standard/plasmafire/init_factors()
factor = list(
- /datum/gas/oxygen = "Oxygen consumption is determined by the temperature, ranging from [OXYGEN_BURN_RATIO_BASE] moles per mole of plasma consumed at [PLASMA_MINIMUM_BURN_TEMPERATURE] Kelvins to [OXYGEN_BURN_RATIO_BASE-1] moles per mole of plasma consumed at [PLASMA_UPPER_TEMPERATURE] Kelvins. Higher oxygen concentration up to [PLASMA_OXYGEN_FULLBURN] times the plasma increases the speed of plasma consumption.",
- /datum/gas/plasma = "Plasma is consumed at a rate that scales with the difference between the temperature and [PLASMA_MINIMUM_BURN_TEMPERATURE]K, with maximum scaling at [PLASMA_UPPER_TEMPERATURE]K.",
- /datum/gas/tritium = "Tritium is formed at 1 mole per mole of plasma consumed if there are at least 97 times more oxygen than plasma.",
- /datum/gas/water_vapor = "Water vapor is formed at 0.25 moles per mole of plasma consumed if tritium isn't being formed.",
- /datum/gas/carbon_dioxide = "Carbon Dioxide is formed at 0.75 moles per mole of plasma consumed if tritium isn't being formed.",
- "Temperature" = "Minimum temperature of [PLASMA_MINIMUM_BURN_TEMPERATURE] kelvin to occur. Higher temperature up to [PLASMA_UPPER_TEMPERATURE]K increases the oxygen efficiency and also the plasma consumption rate.",
- "Energy" = "[FIRE_PLASMA_ENERGY_RELEASED] joules of energy is released per mole of plasma consumed.",
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] consumption is determined by the temperature, \
+ ranging from [OXYGEN_BURN_RATIO_BASE] moles per mole of [/datum/gas/plasma::name] consumed at [PLASMA_MINIMUM_BURN_TEMPERATURE]K \
+ to [OXYGEN_BURN_RATIO_BASE-1] moles per mole of [/datum/gas/plasma::name] consumed at [PLASMA_UPPER_TEMPERATURE]K. \
+ Higher [/datum/gas/oxygen::name] concentration up to [PLASMA_OXYGEN_FULLBURN] times the [/datum/gas/plasma::name] \
+ increases the speed of [/datum/gas/plasma::name] consumption.",
+ /datum/gas/plasma = "[/datum/gas/plasma::name] is consumed at a rate that scales with the difference between the temperature \
+ and [PLASMA_MINIMUM_BURN_TEMPERATURE]K, with maximum scaling at [PLASMA_UPPER_TEMPERATURE]K.",
+ /datum/gas/tritium = "[/datum/gas/tritium::name] is formed at 1 mole per mole of [/datum/gas/plasma::name] consumed \
+ if there are at least 97 times more [/datum/gas/oxygen::name] than [/datum/gas/plasma::name].",
+ /datum/gas/water_vapor = "[/datum/gas/water_vapor::name] is formed at 0.25 moles per mole of [/datum/gas/plasma::name] consumed \
+ if [/datum/gas/tritium::name] isn't being formed.",
+ /datum/gas/carbon_dioxide = "[/datum/gas/carbon_dioxide::name] is formed at 0.75 moles per mole of [/datum/gas/plasma::name] consumed \
+ if [/datum/gas/tritium::name] isn't being formed.",
+ "Temperature" = "Minimum temperature of [PLASMA_MINIMUM_BURN_TEMPERATURE]K to occur. \
+ Higher temperature up to [PLASMA_UPPER_TEMPERATURE]K increases the [/datum/gas/oxygen::name] efficiency \
+ and also the [/datum/gas/plasma::name] consumption rate.",
+ "Energy" = "[FIRE_PLASMA_ENERGY_RELEASED] joules of energy is released per mole of [/datum/gas/plasma::name] consumed.",
)
-/datum/gas_reaction/h2fire/init_factors()
+/datum/gas_reaction/standard/h2fire/init_factors()
factor = list(
- /datum/gas/oxygen = "Oxygen is consumed at 0.5 moles per mole of hydrogen consumed. Higher oxygen concentration up to [HYDROGEN_OXYGEN_FULLBURN] times the hydrogen increases the hydrogen consumption rate.",
- /datum/gas/hydrogen = "Hydrogen is consumed rapidly fast as long as there's enough oxygen to allow combustion.",
- /datum/gas/water_vapor = "Water vapor is produced at 1 mole per mole of hydrogen combusted.",
- "Temperature" = "Minimum temperature of [FIRE_MINIMUM_TEMPERATURE_TO_EXIST] kelvin to occur",
- "Energy" = "[FIRE_HYDROGEN_ENERGY_RELEASED] joules of energy is released per mol of hydrogen consumed.",
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] is consumed at 0.5 moles per mole of [/datum/gas/hydrogen::name] consumed. \
+ Higher [/datum/gas/oxygen::name] concentration up to [HYDROGEN_OXYGEN_FULLBURN] times \
+ the [/datum/gas/hydrogen::name] increases the [/datum/gas/hydrogen::name] consumption rate.",
+ /datum/gas/hydrogen = "[/datum/gas/hydrogen::name] is consumed rapidly fast as long as there's enough [/datum/gas/oxygen::name] to allow combustion.",
+ /datum/gas/water_vapor = "[/datum/gas/water_vapor::name] is produced at 1 mole per mole of [/datum/gas/hydrogen::name] combusted.",
+ "Temperature" = "Minimum temperature of [FIRE_MINIMUM_TEMPERATURE_TO_EXIST]K to occur",
+ "Energy" = "[FIRE_HYDROGEN_ENERGY_RELEASED] joules of energy is released per mol of [/datum/gas/hydrogen::name] consumed.",
)
-/datum/gas_reaction/tritfire/init_factors()
+/datum/gas_reaction/standard/tritfire/init_factors()
factor = list(
- /datum/gas/oxygen = "Oxygen is consumed at 0.5 moles per mole of tritium consumed. Higher oxygen concentration up to [TRITIUM_OXYGEN_FULLBURN] times the tritium increases the tritium consumption rate.",
- /datum/gas/tritium = "Tritium is consumed at rapidly fast as long as there's enough oxygen to allow combustion.",
- /datum/gas/water_vapor = "Water vapor is produced at 1 mole per mole of tritium combusted.",
- "Temperature" = "Minimum temperature of [FIRE_MINIMUM_TEMPERATURE_TO_EXIST] kelvin to occur",
- "Energy" = "[FIRE_TRITIUM_ENERGY_RELEASED] joules of energy is released per mol of tritium consumed.",
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] is consumed at 0.5 moles per mole of [/datum/gas/tritium::name] consumed. \
+ Higher [/datum/gas/oxygen::name] concentration up to [TRITIUM_OXYGEN_FULLBURN] times \
+ the [/datum/gas/tritium::name] increases the [/datum/gas/tritium::name] consumption rate.",
+ /datum/gas/tritium = "[/datum/gas/tritium::name] is consumed at rapidly fast as long as there's enough [/datum/gas/oxygen::name] to allow combustion.",
+ /datum/gas/water_vapor = "[/datum/gas/water_vapor::name] is produced at 1 mole per mole of [/datum/gas/tritium::name] combusted.",
+ "Temperature" = "Minimum temperature of [FIRE_MINIMUM_TEMPERATURE_TO_EXIST]K to occur",
+ "Energy" = "[FIRE_TRITIUM_ENERGY_RELEASED] joules of energy is released per mol of [/datum/gas/tritium::name] consumed.",
"Radiation" = "This reaction emits radiation proportional to the amount of energy released.",
)
-/datum/gas_reaction/freonfire/init_factors()
+/datum/gas_reaction/standard/freonfire/init_factors()
factor = list(
- /datum/gas/oxygen = "Oxygen consumption is determined by the temperature, ranging from [OXYGEN_BURN_RATIO_BASE] moles per mole of freon consumed at [FREON_LOWER_TEMPERATURE] Kelvins to [OXYGEN_BURN_RATIO_BASE-1] moles per mole of freon consumed at [FREON_MAXIMUM_BURN_TEMPERATURE] Kelvins. Higher oxygen concentration up to [FREON_OXYGEN_FULLBURN] times the freon increases freon consumption rate.",
- /datum/gas/freon = "Freon is consumed at a rate that scales with the distance of the temperature from [FREON_MAXIMUM_BURN_TEMPERATURE]K. Its relationship with oxygen also determines consumption rate.",
- /datum/gas/carbon_dioxide = "Carbon Dioxide is formed at 1 mole per mole of freon consumed.",
- "Temperature" = "Can only occur between [FREON_LOWER_TEMPERATURE] - [FREON_MAXIMUM_BURN_TEMPERATURE] Kelvin",
- "Energy" = "[FIRE_FREON_ENERGY_CONSUMED] joules of energy is absorbed per mole of freon consumed.",
- "Hot Ice" = "This reaction produces hot ice when occuring between [HOT_ICE_FORMATION_MINIMUM_TEMPERATURE]-[HOT_ICE_FORMATION_MAXIMUM_TEMPERATURE] kelvins",
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] consumption is determined by the temperature, \
+ ranging from [OXYGEN_BURN_RATIO_BASE] moles per mole of [/datum/gas/freon::name] consumed at [FREON_LOWER_TEMPERATURE]K \
+ to [OXYGEN_BURN_RATIO_BASE - 1] moles per mole of [/datum/gas/freon::name] consumed at [FREON_MAXIMUM_BURN_TEMPERATURE]K. \
+ Higher [/datum/gas/oxygen::name] concentration up to [FREON_OXYGEN_FULLBURN] times \
+ the [/datum/gas/freon::name] increases [/datum/gas/freon::name] consumption rate.",
+ /datum/gas/freon = "[/datum/gas/freon::name] is consumed at a rate that scales with the distance of the temperature \
+ from [FREON_MAXIMUM_BURN_TEMPERATURE]K. Its relationship with [/datum/gas/oxygen::name] also determines consumption rate.",
+ /datum/gas/carbon_dioxide = "[/datum/gas/carbon_dioxide::name] is formed at 1 mole per mole of [/datum/gas/freon::name] consumed.",
+ "Temperature" = "Can only occur between [FREON_LOWER_TEMPERATURE] - [FREON_MAXIMUM_BURN_TEMPERATURE]K.",
+ "Energy" = "[FIRE_FREON_ENERGY_CONSUMED] joules of energy is absorbed per mole of [/datum/gas/freon::name] consumed.",
+ "Hot Ice" = "This reaction produces \"hot ice\" when occurring between [HOT_ICE_FORMATION_MINIMUM_TEMPERATURE]-[HOT_ICE_FORMATION_MAXIMUM_TEMPERATURE]K.",
)
-/datum/gas_reaction/nitrousformation/init_factors()
+/datum/gas_reaction/standard/nitrousformation/init_factors()
factor = list(
- /datum/gas/oxygen = "10 moles of Oxygen needs to be present for the reaction to occur. Oxygen is consumed at 0.5 moles per mole of nitrous oxide formed.",
- /datum/gas/nitrogen = " 20 moles of Nitrogen needs to be present for the reaction to occur. Nitrogen is consumed at 1 mole per mole of nitrous oxife formed.",
- /datum/gas/bz = "5 moles of BZ needs to be present for the reaction to occur. Not consumed.",
- /datum/gas/nitrous_oxide = "Nitrous oxide gets produced rapidly.",
- "Temperature" = "Can only occur between [N2O_FORMATION_MIN_TEMPERATURE] - [N2O_FORMATION_MAX_TEMPERATURE] Kelvin",
- "Energy" = "[N2O_FORMATION_ENERGY] joules of energy is released per mole of nitrous oxide formed.",
+ /datum/gas/oxygen = "10 moles of [/datum/gas/oxygen::name] needs to be present for the reaction to occur. \
+ [/datum/gas/oxygen::name] is consumed at 0.5 moles per mole of [/datum/gas/nitrous_oxide::name] formed.",
+ /datum/gas/nitrogen = " 20 moles of [/datum/gas/nitrogen::name] needs to be present for the reaction to occur. \
+ [/datum/gas/nitrogen::name] is consumed at 1 mole per mole of [/datum/gas/nitrous_oxide::name] formed.",
+ /datum/gas/bz = "5 moles of [/datum/gas/bz::name] needs to be present for the reaction to occur. Not consumed.",
+ /datum/gas/nitrous_oxide = "[/datum/gas/nitrous_oxide::name] gets produced rapidly.",
+ "Temperature" = "Can only occur between [N2O_FORMATION_MIN_TEMPERATURE] - [N2O_FORMATION_MAX_TEMPERATURE]K",
+ "Energy" = "[N2O_FORMATION_ENERGY] joules of energy is released per mole of [/datum/gas/nitrous_oxide::name] formed.",
)
-/datum/gas_reaction/nitrous_decomp/init_factors()
+/datum/gas_reaction/standard/nitrous_decomp/init_factors()
factor = list(
- /datum/gas/nitrous_oxide = "Nitrous Oxide is decomposed at a rate that scales negatively with the distance between the temperature and average of the minimum and maximum temperature of the reaction. Minimum of [MINIMUM_MOLE_COUNT * 2] to occur.", //okay this one isn't made into a define yet.
- /datum/gas/oxygen = "Oxygen is formed at 0.5 moles per mole of nitrous oxide decomposed.",
- /datum/gas/nitrogen = "Nitrogen is formed at 1 mole per mole of nitrous oxide decomposed.",
- "Temperature" = "The decomposition rate scales with the product of the distances between temperature and minimum and maximum temperature. Can only happen between [N2O_DECOMPOSITION_MIN_TEMPERATURE] - [N2O_DECOMPOSITION_MAX_TEMPERATURE] kelvin.",
- "Energy" = "[N2O_DECOMPOSITION_ENERGY] joules of energy is released per mole of nitrous oxide decomposed.",
+ /datum/gas/nitrous_oxide = "[/datum/gas/nitrous_oxide::name] is decomposed at a rate that scales negatively with the distance between \
+ the temperature and average of the minimum and maximum temperature of the reaction. \
+ Minimum of [MINIMUM_MOLE_COUNT * 2] to occur.", //okay this one isn't made into a define yet.
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] is formed at 0.5 moles per mole of [/datum/gas/nitrous_oxide::name] decomposed.",
+ /datum/gas/nitrogen = "[/datum/gas/nitrogen::name] is formed at 1 mole per mole of [/datum/gas/nitrous_oxide::name] decomposed.",
+ "Temperature" = "The decomposition rate scales with the product of the distances between temperature and minimum and maximum temperature. Can only happen between [N2O_DECOMPOSITION_MIN_TEMPERATURE] - [N2O_DECOMPOSITION_MAX_TEMPERATURE]K.",
+ "Energy" = "[N2O_DECOMPOSITION_ENERGY] joules of energy is released per mole of [/datum/gas/nitrous_oxide::name] decomposed.",
)
-/datum/gas_reaction/bzformation/init_factors()
+/datum/gas_reaction/standard/bzformation/init_factors()
factor = list(
- /datum/gas/plasma = "Each mole of BZ made consumes 0.8 moles of plasma. If there is more plasma than nitrous oxide, bz formation rate gets slowed down.",
- /datum/gas/nitrous_oxide = "Each mole of bz made consumes 0.4 moles of Nitrous oxide. If there is less nitrous oxide than plasma the reaction rate is slowed down. At three times the amount of plasma to Nitrous oxide it will start breaking down into Nitrogen and Oxygen, the lower the ratio the more Nitrous oxide decomposes.",
- /datum/gas/bz = "The lower the pressure and larger the volume the more bz gets made. Less nitrous oxide than plasma will slow down the reaction.",
- /datum/gas/nitrogen = "Each mole Nitrous oxide decomposed makes 1 mol Nitrogen. Lower ratio of Nitrous oxide to Plasma means a higher ratio of decomposition to BZ production.",
- /datum/gas/oxygen = "Each mole Nitrous oxide decomposed makes 0.5 moles Oxygen. Lower ratio of Nitrous oxide to Plasma means a higher ratio of decomposition to BZ production.",
- "Energy" = "[BZ_FORMATION_ENERGY] joules of energy is released per mol of BZ made. Nitrous oxide decomposition releases [N2O_DECOMPOSITION_ENERGY] per mol decomposed",
+ /datum/gas/plasma = "Each mole of [/datum/gas/bz::name] made consumes 0.8 moles of [/datum/gas/plasma::name]. \
+ If there is more [/datum/gas/plasma::name] than [/datum/gas/nitrous_oxide::name], [/datum/gas/bz::name] formation rate gets slowed down.",
+ /datum/gas/nitrous_oxide = "Each mole of [/datum/gas/bz::name] made consumes 0.4 moles of [/datum/gas/nitrous_oxide::name]. \
+ If there is less [/datum/gas/nitrous_oxide::name] than [/datum/gas/plasma::name] the reaction rate is slowed down. \
+ At three times the amount of [/datum/gas/plasma::name] to [/datum/gas/nitrous_oxide::name], \
+ it will start breaking down into [/datum/gas/nitrogen::name] and [/datum/gas/oxygen::name] - \
+ the lower the ratio, the more [/datum/gas/nitrous_oxide::name] decomposes.",
+ /datum/gas/bz = "The lower the pressure and larger the volume the more [/datum/gas/bz::name] gets made. \
+ Less [/datum/gas/nitrous_oxide::name] than [/datum/gas/plasma::name] will slow down the reaction.",
+ /datum/gas/nitrogen = "Each mole [/datum/gas/nitrous_oxide::name] decomposed makes 1 mol [/datum/gas/nitrogen::name]. \
+ Lower ratio of [/datum/gas/nitrous_oxide::name] to [/datum/gas/plasma::name] means a higher ratio of decomposition to [/datum/gas/bz::name] production.",
+ /datum/gas/oxygen = "Each mole [/datum/gas/nitrous_oxide::name] decomposed makes 0.5 moles [/datum/gas/oxygen::name]. \
+ Lower ratio of [/datum/gas/nitrous_oxide::name] to [/datum/gas/plasma::name] means a higher ratio of decomposition to [/datum/gas/bz::name] production.",
+ "Energy" = "[BZ_FORMATION_ENERGY] joules of energy is released per mol of [/datum/gas/bz::name] made. \
+ [/datum/gas/nitrous_oxide::name] decomposition releases [N2O_DECOMPOSITION_ENERGY] per mol decomposed.",
)
-/datum/gas_reaction/pluox_formation/init_factors()
+/datum/gas_reaction/standard/pluox_formation/init_factors()
factor = list(
- /datum/gas/carbon_dioxide = "1 mole of carbon dioxide gets consumed per mole of pluoxium formed.",
- /datum/gas/oxygen = "Oxygen is consumed at 0.5 moles per mole of pluoxium formed.",
- /datum/gas/tritium = "Tritium is converted into hydrogen at 0.01 moles per mole of pluoxium formed.",
- /datum/gas/pluoxium = "Pluoxium is produced at a constant rate in any given mixture.",
- /datum/gas/hydrogen = "Hydrogen is formed from the tritium losing their neutrons.",
- "Energy" = "[PLUOXIUM_FORMATION_ENERGY] joules of energy is released per mole of pluoxium formed.",
- "Temperature" = "Can only occur between [PLUOXIUM_FORMATION_MIN_TEMP] - [PLUOXIUM_FORMATION_MAX_TEMP] Kelvin",
+ /datum/gas/carbon_dioxide = "1 mole of [/datum/gas/carbon_dioxide::name] gets consumed per mole of [/datum/gas/pluoxium::name] formed.",
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] is consumed at 0.5 moles per mole of [/datum/gas/pluoxium::name] formed.",
+ /datum/gas/tritium = "[/datum/gas/tritium::name] is converted into [/datum/gas/hydrogen::name] at 0.01 moles per mole of [/datum/gas/pluoxium::name] formed.",
+ /datum/gas/pluoxium = "[/datum/gas/pluoxium::name] is produced at a constant rate in any given mixture.",
+ /datum/gas/hydrogen = "[/datum/gas/hydrogen::name] is formed from the [/datum/gas/tritium::name] losing their neutrons.",
+ "Energy" = "[PLUOXIUM_FORMATION_ENERGY] joules of energy is released per mole of [/datum/gas/pluoxium::name] formed.",
+ "Temperature" = "Can only occur between [PLUOXIUM_FORMATION_MIN_TEMP] - [PLUOXIUM_FORMATION_MAX_TEMP]K",
)
-/datum/gas_reaction/nitrium_formation/init_factors()
+/datum/gas_reaction/standard/nitrium_formation/init_factors()
factor = list(
- /datum/gas/bz = "5 moles of BZ needs to be present for the reaction to occur. BZ is consumed at 0.05 moles per mole of nitrium formed.",
- /datum/gas/tritium = "20 moles of tritium needs to be present for the reaction to occur. Tritium is consumed at 1 mole per mole of nitroum formed.",
- /datum/gas/nitrogen = "10 moles of nitrogen needs to be present for the reaction to occur. Nitrogen is consumed at 1 mole per mole of nitrium formed.",
- /datum/gas/nitrium = "Nitrium is produced at a rate that scales with the temperature.",
- "Temperature" = "Can only occur above [NITRIUM_FORMATION_MIN_TEMP] kelvins",
- "Energy" = "[NITRIUM_FORMATION_ENERGY] joules of energy is absorbed per mole of nitrium formed.",
+ /datum/gas/bz = "5 moles of [/datum/gas/bz::name] needs to be present for the reaction to occur. \
+ [/datum/gas/bz::name] is consumed at 0.05 moles per mole of [/datum/gas/nitrium::name] formed.",
+ /datum/gas/tritium = "20 moles of [/datum/gas/tritium::name] needs to be present for the reaction to occur. \
+ [/datum/gas/tritium::name] is consumed at 1 mole per mole of [/datum/gas/nitrium::name] formed.",
+ /datum/gas/nitrogen = "10 moles of [/datum/gas/nitrogen::name] needs to be present for the reaction to occur. \
+ [/datum/gas/nitrogen::name] is consumed at 1 mole per mole of [/datum/gas/nitrium::name] formed.",
+ /datum/gas/nitrium = "[/datum/gas/nitrium::name] is produced at a rate that scales with the temperature.",
+ "Temperature" = "Can only occur above [NITRIUM_FORMATION_MIN_TEMP]K",
+ "Energy" = "[NITRIUM_FORMATION_ENERGY] joules of energy is absorbed per mole of [/datum/gas/nitrium::name] formed.",
)
-/datum/gas_reaction/nitrium_decomposition/init_factors()
+/datum/gas_reaction/standard/nitrium_decomposition/init_factors()
factor = list(
- /datum/gas/oxygen = "[MINIMUM_MOLE_COUNT] moles of oxygen need to be present for the reaction to occur. Not consumed.",
- /datum/gas/nitrium = "Nitrium is consumed at a rate that scales with the temperature.",
- /datum/gas/hydrogen = "Hydrogen is produced at 1 mole per mole of nitrium decomposed.",
- /datum/gas/nitrogen = "Nitrogen is produced at 1 mole per mole of nitrium decomposed.",
- "Temperature" = "Can only occur below [NITRIUM_DECOMPOSITION_MAX_TEMP]. Higher temperature increases the nitrium decomposition rate.",
- "Energy" = "[NITRIUM_DECOMPOSITION_ENERGY] joules of energy is released per mole of nitrium decomposed.",
+ /datum/gas/oxygen = "[MINIMUM_MOLE_COUNT] moles of [/datum/gas/oxygen::name] need to be present for the reaction to occur. Not consumed.",
+ /datum/gas/nitrium = "[/datum/gas/nitrium::name] is consumed at a rate that scales with the temperature.",
+ /datum/gas/hydrogen = "[/datum/gas/hydrogen::name] is produced at 1 mole per mole of [/datum/gas/nitrium::name] decomposed.",
+ /datum/gas/nitrogen = "[/datum/gas/nitrogen::name] is produced at 1 mole per mole of [/datum/gas/nitrium::name] decomposed.",
+ "Temperature" = "Can only occur below [NITRIUM_DECOMPOSITION_MAX_TEMP]. Higher temperature increases the [/datum/gas/nitrium::name] decomposition rate.",
+ "Energy" = "[NITRIUM_DECOMPOSITION_ENERGY] joules of energy is released per mole of [/datum/gas/nitrium::name] decomposed.",
)
-/datum/gas_reaction/freonformation/init_factors()
+/datum/gas_reaction/standard/freonformation/init_factors()
factor = list(
- /datum/gas/plasma = "At least 0.06 moles of plasma needs to be present. Plasma is consumed at 0.6 moles per mole of freon formed.",
- /datum/gas/carbon_dioxide = "At least 0.03 moles of CO2 needs to be present. CO2 is consumed at 0.3 moles per mole of freon formed.",
- /datum/gas/bz = "At least 0.01 moles of BZ needs to be present. BZ is consumed at 0.1 moles per mole of freon formed.",
- /datum/gas/freon = "Freon is produced at a rate that scales with the sum of a quadratic exponential and sigmoidal function, with the quadratic exponential peaking at 800 Kelvin, but the sigmoidal function takes dominance at over 5,500K being up to 3 times more efficient.",
- "Energy" = "Between 100 and 800 joules of energy is absorbed per mole of freon produced", // I don't know why the energy release is also a sigmoidal function, but it should really just be constant to be honest.
- "Temperature" = "Minimum temperature of [FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100] Kelvin to occur, with production peak at 800 K. However at temperatures above 5500 K higher rates are possible maxing out at three times the low temperature rate at over 8500 K.",
+ /datum/gas/plasma = "At least 0.06 moles of [/datum/gas/plasma::name] needs to be present. \
+ [/datum/gas/plasma::name] is consumed at 0.6 moles per mole of [/datum/gas/freon::name] formed.",
+ /datum/gas/carbon_dioxide = "At least 0.03 moles of [/datum/gas/carbon_dioxide::name] needs to be present. \
+ [/datum/gas/carbon_dioxide::name] is consumed at 0.3 moles per mole of [/datum/gas/freon::name] formed.",
+ /datum/gas/bz = "At least 0.01 moles of [/datum/gas/bz::name] needs to be present. \
+ [/datum/gas/bz::name] is consumed at 0.1 moles per mole of [/datum/gas/freon::name] formed.",
+ /datum/gas/freon = "[/datum/gas/freon::name] is produced at a rate that scales with temperature. \
+ See temperature factor for more information.",
+ "Energy" = "Between 100 and 800 joules of energy is absorbed per mole of [/datum/gas/freon::name] produced", // I don't know why the energy release is also a sigmoidal function, but it should really just be constant to be honest.
+ "Temperature" = "Minimum temperature of [FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100]K to occur. \
+ Production speed peaks at 800K - However, at temperatures above 5500K, \
+ production speed can exceed the low temperature peak (reaching up to three times fast at 8500K).",
)
-/datum/gas_reaction/nobliumformation/init_factors()
+/datum/gas_reaction/standard/nobliumformation/init_factors()
factor = list(
- /datum/gas/nitrogen = "10 moles of nitrogen needs to be present for the reaction to occur. Nitrogen is consumed at 10 moles per mole of hypernoblium formed.",
- /datum/gas/tritium = "5 moles of tritium needs to be present for the reaction to occur. Tritium is consumed at 5 moles per mole of hypernoblium formed. The relative consumption rate of tritium decreases in the exposure of BZ.",
- /datum/gas/hypernoblium = "Hyper-Noblium production scales based on the sum of the nitrogen and tritium moles.",
- "Energy" = "[NOBLIUM_FORMATION_ENERGY] joules of energy is released per mole of hypernoblium produced.",
- /datum/gas/bz = "BZ is not consumed in the reaction but will lower the amount of energy released. It also reduces amount of tritium consumed by a ratio between tritium and bz, greater bz than tritium will reduce more.",
- "Temperature" = "Can only occur between [NOBLIUM_FORMATION_MIN_TEMP] - [NOBLIUM_FORMATION_MAX_TEMP] kelvin",
+ /datum/gas/nitrogen = "10 moles of [/datum/gas/nitrogen::name] needs to be present for the reaction to occur. \
+ [/datum/gas/nitrogen::name] is consumed at 10 moles per mole of [/datum/gas/hypernoblium::name] formed.",
+ /datum/gas/tritium = "5 moles of [/datum/gas/tritium::name] needs to be present for the reaction to occur. \
+ [/datum/gas/tritium::name] is consumed at 5 moles per mole of [/datum/gas/hypernoblium::name] formed. \
+ The relative consumption rate of [/datum/gas/tritium::name] decreases in the exposure of [/datum/gas/bz::name].",
+ /datum/gas/hypernoblium = "[/datum/gas/hypernoblium::name] production scales based on the sum \
+ of the [/datum/gas/nitrogen::name] and [/datum/gas/tritium::name] moles.",
+ "Energy" = "[NOBLIUM_FORMATION_ENERGY] joules of energy is released per mole of [/datum/gas/hypernoblium::name] produced.",
+ /datum/gas/bz = "[/datum/gas/bz::name] is not consumed in the reaction but will lower the amount of energy released. \
+ It also reduces amount of [/datum/gas/tritium::name] consumed by a ratio \
+ between [/datum/gas/tritium::name] and [/datum/gas/bz::name], greater [/datum/gas/bz::name] than [/datum/gas/tritium::name] will reduce more.",
+ "Temperature" = "Can only occur between [NOBLIUM_FORMATION_MIN_TEMP] - [NOBLIUM_FORMATION_MAX_TEMP]K",
)
-/datum/gas_reaction/halon_o2removal/init_factors()
+/datum/gas_reaction/standard/halon_o2removal/init_factors()
factor = list(
- /datum/gas/halon = "Halon is consumed at a rate that scales with temperature.",
- /datum/gas/oxygen = "20 moles of oxygen is consumed per mole of halon combusted.",
- /datum/gas/carbon_dioxide = "Carbon dioxide is produced at 5 moles per mole of halon consumed.",
- "Energy" = "[HALON_COMBUSTION_ENERGY] joules of energy is absorbed per mole of halon consumed.",
- "Temperature" = "Can only occur above [FIRE_MINIMUM_TEMPERATURE_TO_EXIST] kelvin. Higher temperature increases halon consumption rate.",
+ /datum/gas/halon = "[/datum/gas/halon::name] is consumed at a rate that scales with temperature.",
+ /datum/gas/oxygen = "20 moles of [/datum/gas/oxygen::name] is consumed per mole of [/datum/gas/halon::name] combusted.",
+ /datum/gas/carbon_dioxide = "[/datum/gas/carbon_dioxide::name] is produced at 5 moles per mole of [/datum/gas/halon::name] consumed.",
+ "Energy" = "[HALON_COMBUSTION_ENERGY] joules of energy is absorbed per mole of [/datum/gas/halon::name] consumed.",
+ "Temperature" = "Can only occur above [FIRE_MINIMUM_TEMPERATURE_TO_EXIST]K. Higher temperature increases [/datum/gas/halon::name] consumption rate.",
)
-/datum/gas_reaction/healium_formation/init_factors()
+/datum/gas_reaction/standard/healium_formation/init_factors()
factor = list(
- /datum/gas/bz = "BZ is consumed at 1/12th of a mole per mole of healium formed.",
- /datum/gas/freon = "Freon is consumed at 11/12th of a mole per mole of healium formed.",
- /datum/gas/healium = "Healium is formed at a rate that scales with the temperature.",
- "Temperature" = "Can only occur between [HEALIUM_FORMATION_MIN_TEMP] - [HEALIUM_FORMATION_MAX_TEMP]. Higher temperature increases healium formation rate.",
- "Energy" = "[HEALIUM_FORMATION_ENERGY/3] joules of energy is released per mole of healium formed.",
+ /datum/gas/bz = "[/datum/gas/bz::name] is consumed at 1/12th of a mole per mole of [/datum/gas/healium::name] formed.",
+ /datum/gas/freon = "[/datum/gas/freon::name] is consumed at 11/12th of a mole per mole of [/datum/gas/healium::name] formed.",
+ /datum/gas/healium = "[/datum/gas/healium::name] is formed at a rate that scales with the temperature.",
+ "Temperature" = "Can only occur between [HEALIUM_FORMATION_MIN_TEMP] - [HEALIUM_FORMATION_MAX_TEMP]. \
+ Higher temperature increases [/datum/gas/healium::name] formation rate.",
+ "Energy" = "[HEALIUM_FORMATION_ENERGY/3] joules of energy is released per mole of [/datum/gas/healium::name] formed.",
)
-/datum/gas_reaction/zauker_formation/init_factors()
+/datum/gas_reaction/standard/zauker_formation/init_factors()
factor = list(
- /datum/gas/hypernoblium = "Hyper-Noblium is consumed at 0.02 moles per mole of zauker formed.",
- /datum/gas/nitrium = "Nitrium is consumed at 1 mole per mole of zauker formed.",
- /datum/gas/zauker = "Zauker is produced at a rate that scales with the temperature.",
- "Temperature" = "Can only occur between [ZAUKER_FORMATION_MIN_TEMPERATURE] - [ZAUKER_FORMATION_MAX_TEMPERATURE] kelvin. Zauker formation rate is proportional to the temperature.",
- "Energy" = "[2 * ZAUKER_FORMATION_ENERGY] joules of energy is absorbed per mole of zauker formed.",
+ /datum/gas/hypernoblium = "[/datum/gas/hypernoblium::name] is consumed at 0.02 moles per mole of [/datum/gas/zauker::name] formed.",
+ /datum/gas/nitrium = "[/datum/gas/nitrium::name] is consumed at 1 mole per mole of [/datum/gas/zauker::name] formed.",
+ /datum/gas/zauker = "[/datum/gas/zauker::name] is produced at a rate that scales with the temperature.",
+ "Temperature" = "Can only occur between [ZAUKER_FORMATION_MIN_TEMPERATURE] - [ZAUKER_FORMATION_MAX_TEMPERATURE]K. [/datum/gas/zauker::name] formation rate is proportional to the temperature.",
+ "Energy" = "[2 * ZAUKER_FORMATION_ENERGY] joules of energy is absorbed per mole of [/datum/gas/zauker::name] formed.",
)
-/datum/gas_reaction/zauker_decomp/init_factors() //Fixed reaction rate
+/datum/gas_reaction/standard/zauker_decomp/init_factors() //Fixed reaction rate
factor = list(
- /datum/gas/zauker = "Zauker is consumed at [ZAUKER_DECOMPOSITION_MAX_RATE SECONDS / SSair.wait] moles per second in any unique gas mixture.",
- /datum/gas/nitrogen = "At least [MINIMUM_MOLE_COUNT] moles of Nitrogen needs to be present for this reaction to occur. Nitrogen is produced at 0.7 moles per mole of Zauker decomposed.",
- /datum/gas/oxygen = "Oxygen is produced at 0.3 moles per mole of zauker decomposed.",
- "Energy" = "[ZAUKER_DECOMPOSITION_ENERGY] joules of energy is released per mole of zauker decomposed.",
+ /datum/gas/zauker = "[/datum/gas/zauker::name] is consumed at [ZAUKER_DECOMPOSITION_MAX_RATE SECONDS / SSair.wait] moles per second in any unique gas mixture.",
+ /datum/gas/nitrogen = "At least [MINIMUM_MOLE_COUNT] moles of [/datum/gas/nitrogen::name] needs to be present for this reaction to occur. \
+ [/datum/gas/nitrogen::name] is produced at 0.7 moles per mole of [/datum/gas/zauker::name] decomposed.",
+ /datum/gas/oxygen = "[/datum/gas/oxygen::name] is produced at 0.3 moles per mole of [/datum/gas/zauker::name] decomposed.",
+ "Energy" = "[ZAUKER_DECOMPOSITION_ENERGY] joules of energy is released per mole of [/datum/gas/zauker::name] decomposed.",
)
-/datum/gas_reaction/proto_nitrate_formation/init_factors()
+/datum/gas_reaction/standard/proto_nitrate_formation/init_factors()
factor = list(
- /datum/gas/pluoxium = "Pluoxium is consumed at 1/11th of a mole per mole of proto-nitrate formed.",
- /datum/gas/hydrogen = "Hydrogen is consumed at 10/11th of a mole per mole of proto-nitrate formed.",
- /datum/gas/proto_nitrate = "Proto-Nitrate is produced at a rate that scales with the temperature.",
- "Energy" = "[PN_FORMATION_ENERGY / 2.2] joules of energy is released per mole of proto-nitrate formed.",
- "Temperature" = "Can only occur between [PN_FORMATION_MIN_TEMPERATURE] - [PN_FORMATION_MAX_TEMPERATURE] kelvin. Higher temperature increases proto-nitrate formation rate.",
+ /datum/gas/pluoxium = "[/datum/gas/pluoxium::name] is consumed at 1/11th of a mole per mole of [/datum/gas/proto_nitrate::name] formed.",
+ /datum/gas/hydrogen = "[/datum/gas/hydrogen::name] is consumed at 10/11th of a mole per mole of [/datum/gas/proto_nitrate::name] formed.",
+ /datum/gas/proto_nitrate = "[/datum/gas/proto_nitrate::name] is produced at a rate that scales with the temperature.",
+ "Energy" = "[PN_FORMATION_ENERGY / 2.2] joules of energy is released per mole of [/datum/gas/proto_nitrate::name] formed.",
+ "Temperature" = "Can only occur between [PN_FORMATION_MIN_TEMPERATURE] - [PN_FORMATION_MAX_TEMPERATURE]K. \
+ Higher temperature increases [/datum/gas/proto_nitrate::name] formation rate.",
)
-/datum/gas_reaction/proto_nitrate_hydrogen_response/init_factors() // Fixed reaction rate
+/datum/gas_reaction/standard/proto_nitrate_hydrogen_response/init_factors() // Fixed reaction rate
factor = list(
- /datum/gas/hydrogen = "[PN_HYDROGEN_CONVERSION_THRESHOLD] moles of hydrogen needs to be present for the reaction to occur. Hydrogen is consumed at 2 moles per mole of proto-nitrate formed.",
- /datum/gas/proto_nitrate = "[MINIMUM_MOLE_COUNT] moles of proto-nitrate needs to be present for the reaction to occur. Proto nitrate is produced a rate that scales with its mole count, up to a max of [PN_HYDROGEN_CONVERSION_MAX_RATE * 0.5 SECONDS / SSair.wait] moles per second.",
- "Energy" = "[PN_HYDROGEN_CONVERSION_ENERGY * 2] joules of energy is absorbed per mole of proto-nitrate formed.",
+ /datum/gas/hydrogen = "[PN_HYDROGEN_CONVERSION_THRESHOLD] moles of [/datum/gas/hydrogen::name] needs to be present for the reaction to occur. \
+ [/datum/gas/hydrogen::name] is consumed at 2 moles per mole of [/datum/gas/proto_nitrate::name] formed.",
+ /datum/gas/proto_nitrate = "[MINIMUM_MOLE_COUNT] moles of [/datum/gas/proto_nitrate::name] needs to be present for the reaction to occur. \
+ [/datum/gas/proto_nitrate::name] is produced a rate that scales with its mole count, \
+ up to a max of [PN_HYDROGEN_CONVERSION_MAX_RATE * 0.5 SECONDS / SSair.wait] moles per second.",
+ "Energy" = "[PN_HYDROGEN_CONVERSION_ENERGY * 2] joules of energy is absorbed per mole of [/datum/gas/proto_nitrate::name] formed.",
)
-/datum/gas_reaction/proto_nitrate_tritium_response/init_factors()
+/datum/gas_reaction/standard/proto_nitrate_tritium_response/init_factors()
factor = list(
- /datum/gas/tritium = "Tritium radiates its neutrons at a rate that scales with the temperature and proto-nitrate mole count.",
- /datum/gas/proto_nitrate = "Proto nitrate is consumed at 0.005 moles per mole of neutrons released.",
- /datum/gas/hydrogen = "Hydrogen remains after the neutrons escape.",
+ /datum/gas/tritium = "[/datum/gas/tritium::name] radiates its neutrons at a rate that scales with the temperature and [/datum/gas/proto_nitrate::name] mole count.",
+ /datum/gas/proto_nitrate = "[/datum/gas/proto_nitrate::name] is consumed at 0.005 moles per mole of neutrons released.",
+ /datum/gas/hydrogen = "[/datum/gas/hydrogen::name] remains after the neutrons escape.",
"Energy" = "[PN_TRITIUM_CONVERSION_ENERGY / 2] joules of energy is released per mole of neutron released.",
- "Radiation" = "Neutrons get released as ionising radiation.",
+ "Radiation" = "Neutrons are released as ionising radiation.",
)
-/datum/gas_reaction/proto_nitrate_bz_response/init_factors()
+/datum/gas_reaction/standard/proto_nitrate_bz_response/init_factors()
factor = list(
- /datum/gas/proto_nitrate = "[MINIMUM_MOLE_COUNT] moles of proto-nitrate needs to be present for the reaction to occur. Proto-nitrate accelerates the BZ decomposition.",
- /datum/gas/bz = "BZ gets decomposed into plasma and nitrous oxide. The nitrous oxide then decomposes into nitrogen and oxygen, with the oxygen then decaying into helium.",
- /datum/gas/nitrogen = "Nitrogen is produced at 0.4 moles per mole of BZ decomposed.",
- /datum/gas/helium = "Helium is produced at 1.6 moles per mole of BZ decomposed.",
- /datum/gas/plasma = "Plasma is produced at 0.8 moles per mole of BZ decomposed.",
- "Energy" = "[PN_BZASE_ENERGY] joules of energy is released per mole of BZ decomposed.",
- "Radiation" = "Radiation gets released during this decomposition process.",
+ /datum/gas/proto_nitrate = "[MINIMUM_MOLE_COUNT] moles of [/datum/gas/proto_nitrate::name] needs to be present for the reaction to occur. \
+ [/datum/gas/proto_nitrate::name] accelerates the [/datum/gas/bz::name] decomposition.",
+ /datum/gas/bz = "[/datum/gas/bz::name] gets decomposed into [/datum/gas/plasma::name] and [/datum/gas/nitrous_oxide::name]. \
+ The [/datum/gas/nitrous_oxide::name] then decomposes into [/datum/gas/nitrogen::name] and [/datum/gas/oxygen::name], \
+ with the [/datum/gas/oxygen::name] then decaying into [/datum/gas/helium::name].",
+ /datum/gas/nitrogen = "[/datum/gas/nitrogen::name] is produced at 0.4 moles per mole of [/datum/gas/bz::name] decomposed.",
+ /datum/gas/helium = "[/datum/gas/helium::name] is produced at 1.6 moles per mole of [/datum/gas/bz::name] decomposed.",
+ /datum/gas/plasma = "[/datum/gas/plasma::name] is produced at 0.8 moles per mole of [/datum/gas/bz::name] decomposed.",
+ "Energy" = "[PN_BZASE_ENERGY] joules of energy is released per mole of [/datum/gas/bz::name] decomposed.",
+ "Radiation" = "Radiation is released during this decomposition process.",
"Hallucinations" = "This reaction can cause various carbon based lifeforms in the vicinity to hallucinate.",
- "Nuclear Particles" = "This reaction emits extremely high energy nuclear particles, up to [2 * PN_BZASE_NUCLEAR_PARTICLE_MAXIMUM] per second per unique gas mixture.",
+ "Nuclear Particles" = "This reaction emits extremely high energy nuclear particles, \
+ up to [2 * PN_BZASE_NUCLEAR_PARTICLE_MAXIMUM] per second per unique gas mixture.",
+ "Temperature" = "Can only occur between [PN_BZASE_MIN_TEMP] - [PN_BZASE_MAX_TEMP]K.",
)
diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm
index 24a7c799ad91..051c9b48f96e 100644
--- a/code/modules/atmospherics/gasmixtures/reactions.dm
+++ b/code/modules/atmospherics/gasmixtures/reactions.dm
@@ -5,7 +5,7 @@
var/list/priority_reactions = list()
//Builds a list of gas id to reaction group
- for(var/gas_id in GLOB.meta_gas_info)
+ for(var/gas_id in GLOB.meta_gas_info[META_GAS_ID])
priority_reactions[gas_id] = list(
/* PRIORITY_PRE_FORMATION = */ list(),
/* PRIORITY_FORMATION = */ list(),
@@ -13,7 +13,7 @@
/* PRIORITY_FIRE = */ list()
)
- for(var/datum/gas_reaction/reaction as anything in subtypesof(/datum/gas_reaction))
+ for(var/datum/gas_reaction/standard/reaction as anything in subtypesof(/datum/gas_reaction/standard))
if(initial(reaction.exclude))
continue
reaction = new reaction
@@ -27,7 +27,7 @@
priority_reactions[reaction_key][reaction.priority_group] += reaction
//Culls empty gases
- for(var/gas_id in GLOB.meta_gas_info)
+ for(var/gas_id in GLOB.meta_gas_info[META_GAS_ID])
var/passed = FALSE
for(var/list/priority_grouping in priority_reactions[gas_id])
if(length(priority_grouping))
@@ -40,6 +40,13 @@
return priority_reactions
/datum/gas_reaction
+ abstract_type = /datum/gas_reaction
+ /// Name of the reaction
+ var/name = "reaction"
+ /// A short string describing this reaction.
+ var/desc
+ /// ID of the reaction
+ var/id = "r"
/**
* Regarding the requirements list: the minimum or maximum requirements must be non-zero.
* When in doubt, use MINIMUM_MOLE_COUNT.
@@ -47,16 +54,6 @@
* More complex implementations will require modifications to gas_mixture.react()
*/
var/list/requirements
- var/major_gas //the highest rarity gas used in the reaction.
- var/exclude = FALSE //do it this way to allow for addition/removal of reactions midmatch in the future
- ///The priority group this reaction is a part of. You can think of these as processing in batches, put your reaction into the one that's most fitting
- var/priority_group
- var/name = "reaction"
- var/id = "r"
- /// Whether the presence of our reaction should make fires bigger or not.
- var/expands_hotspot = FALSE
- /// A short string describing this reaction.
- var/desc
/** REACTION FACTORS
*
* Describe (to a human) factors influencing this reaction in an assoc list format.
@@ -69,16 +66,26 @@
*/
var/list/factor
-/datum/gas_reaction/New()
+/datum/gas_reaction/standard
+ abstract_type = /datum/gas_reaction/standard
+ var/major_gas //the highest rarity gas used in the reaction.
+ var/exclude = FALSE //do it this way to allow for addition/removal of reactions midmatch in the future
+ ///The priority group this reaction is a part of. You can think of these as processing in batches, put your reaction into the one that's most fitting
+ var/priority_group
+ /// Whether the presence of our reaction should make fires bigger or not.
+ var/expands_hotspot = FALSE
+
+/datum/gas_reaction/standard/New()
+ . = ..()
init_reqs()
init_factors()
-/datum/gas_reaction/proc/init_reqs() // Override this
+/datum/gas_reaction/standard/proc/init_reqs() // Override this
CRASH("Reaction [type] made without specifying requirements.")
-/datum/gas_reaction/proc/init_factors()
+/datum/gas_reaction/standard/proc/init_factors()
-/datum/gas_reaction/proc/react(datum/gas_mixture/air, atom/location)
+/datum/gas_reaction/standard/proc/react(datum/gas_mixture/air, atom/location)
return NO_REACTION
@@ -88,19 +95,19 @@
* Makes turfs slippery.
* Can frost things if the gas is cold enough.
*/
-/datum/gas_reaction/water_vapor
+/datum/gas_reaction/standard/water_vapor
priority_group = PRIORITY_POST_FORMATION
name = "Water Vapor Condensation"
id = "vapor"
- desc = "Water vapor condensation that can make things slippery."
+ desc = "Water Vapor condensation that may make things slippery."
-/datum/gas_reaction/water_vapor/init_reqs()
+/datum/gas_reaction/standard/water_vapor/init_reqs()
requirements = list(
/datum/gas/water_vapor = MOLES_GAS_VISIBLE,
"MAX_TEMP" = WATER_VAPOR_CONDENSATION_POINT,
)
-/datum/gas_reaction/water_vapor/react(datum/gas_mixture/air, datum/holder)
+/datum/gas_reaction/standard/water_vapor/react(datum/gas_mixture/air, datum/holder)
. = NO_REACTION
if(!isturf(holder))
return
@@ -116,7 +123,7 @@
consumed = MOLES_GAS_VISIBLE
if(consumed)
- air.gases[/datum/gas/water_vapor][MOLES] -= consumed
+ air.moles[/datum/gas/water_vapor] -= consumed
SET_REACTION_RESULTS(consumed)
. = REACTING
@@ -126,29 +133,30 @@
*
* Clears out pathogens in the air.
*/
-/datum/gas_reaction/miaster
+/datum/gas_reaction/standard/miaster
priority_group = PRIORITY_POST_FORMATION
name = "Dry Heat Sterilization"
id = "sterilization"
desc = "Pathogens cannot survive in a hot environment. Miasma decomposes on high temperature."
-/datum/gas_reaction/miaster/init_reqs()
+/datum/gas_reaction/standard/miaster/init_reqs()
requirements = list(
/datum/gas/miasma = MINIMUM_MOLE_COUNT,
"MIN_TEMP" = MIASTER_STERILIZATION_TEMP,
)
-/datum/gas_reaction/miaster/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/miaster/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles
+ var/water_vapor_moles = cached_moles[/datum/gas/water_vapor]
+ var/miasma_moles = cached_moles[/datum/gas/miasma]
// As the name says it, it needs to be dry
- if(cached_gases[/datum/gas/water_vapor] && cached_gases[/datum/gas/water_vapor][MOLES] / air.total_moles() > MIASTER_STERILIZATION_MAX_HUMIDITY)
+ if(water_vapor_moles && water_vapor_moles / air.total_moles() > MIASTER_STERILIZATION_MAX_HUMIDITY)
return NO_REACTION
//Replace miasma with oxygen
- var/cleaned_air = min(cached_gases[/datum/gas/miasma][MOLES], MIASTER_STERILIZATION_RATE_BASE + (air.temperature - MIASTER_STERILIZATION_TEMP) / MIASTER_STERILIZATION_RATE_SCALE)
- cached_gases[/datum/gas/miasma][MOLES] -= cleaned_air
- ASSERT_GAS(/datum/gas/oxygen, air)
- cached_gases[/datum/gas/oxygen][MOLES] += cleaned_air
+ var/cleaned_air = min(miasma_moles, MIASTER_STERILIZATION_RATE_BASE + (air.temperature - MIASTER_STERILIZATION_TEMP) / MIASTER_STERILIZATION_RATE_SCALE)
+ cached_moles[/datum/gas/miasma] -= cleaned_air
+ cached_moles[/datum/gas/oxygen] += cleaned_air
//Possibly burning a bit of organic matter through maillard reaction, so a *tiny* bit more heat would be understandable
air.temperature += cleaned_air * MIASTER_STERILIZATION_ENERGY
@@ -166,21 +174,23 @@
* The reaction rate is dependent on the temperature of the gasmix.
* May produce either tritium or carbon dioxide and water vapor depending on the fuel/oxydizer ratio of the gasmix.
*/
-/datum/gas_reaction/plasmafire
+/datum/gas_reaction/standard/plasmafire
priority_group = PRIORITY_FIRE
name = "Plasma Combustion"
id = "plasmafire"
expands_hotspot = TRUE
- desc = "Combustion of oxygen and plasma. Able to produce tritium or carbon dioxade and water vapor."
+ desc = "Combustion of Oxygen and Plasma. Produces Carbon Dioxide and Water Vapor, \
+ and potentially even Tritium if the mixture is rich enough in Plasma."
-/datum/gas_reaction/plasmafire/init_reqs()
+/datum/gas_reaction/standard/plasmafire/init_reqs()
requirements = list(
/datum/gas/plasma = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
"MIN_TEMP" = PLASMA_MINIMUM_BURN_TEMPERATURE,
)
-/datum/gas_reaction/plasmafire/react(datum/gas_mixture/air, datum/holder)
+/datum/gas_reaction/standard/plasmafire/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
// This reaction should proceed faster at higher temperatures.
var/temperature = air.temperature
var/temperature_scale = 0
@@ -194,31 +204,31 @@
var/oxygen_burn_ratio = OXYGEN_BURN_RATIO_BASE - temperature_scale
var/plasma_burn_rate = 0
var/super_saturation = FALSE // Whether we should make tritium.
- var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
- switch(cached_gases[/datum/gas/oxygen][MOLES] / cached_gases[/datum/gas/plasma][MOLES])
+ var/list/cached_moles = air.moles //this speeds things up because accessing datum vars is slow
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen] || 0
+ var/plasma_moles = cached_moles[/datum/gas/plasma] || 0
+ switch(oxygen_moles / plasma_moles)
if(SUPER_SATURATION_THRESHOLD to INFINITY)
- plasma_burn_rate = (cached_gases[/datum/gas/plasma][MOLES] / PLASMA_BURN_RATE_DELTA) * temperature_scale
+ plasma_burn_rate = (plasma_moles / PLASMA_BURN_RATE_DELTA) * temperature_scale
super_saturation = TRUE // Begin to form tritium
if(PLASMA_OXYGEN_FULLBURN to SUPER_SATURATION_THRESHOLD)
- plasma_burn_rate = (cached_gases[/datum/gas/plasma][MOLES] / PLASMA_BURN_RATE_DELTA) * temperature_scale
+ plasma_burn_rate = (plasma_moles / PLASMA_BURN_RATE_DELTA) * temperature_scale
else
- plasma_burn_rate = ((cached_gases[/datum/gas/oxygen][MOLES] / PLASMA_OXYGEN_FULLBURN) / PLASMA_BURN_RATE_DELTA) * temperature_scale
+ plasma_burn_rate = ((oxygen_moles / PLASMA_OXYGEN_FULLBURN) / PLASMA_BURN_RATE_DELTA) * temperature_scale
if(plasma_burn_rate < MINIMUM_HEAT_CAPACITY)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- plasma_burn_rate = min(plasma_burn_rate, cached_gases[/datum/gas/plasma][MOLES], cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(oxygen_burn_ratio)) //Ensures matter is conserved properly
- cached_gases[/datum/gas/plasma][MOLES] = QUANTIZE(cached_gases[/datum/gas/plasma][MOLES] - plasma_burn_rate)
- cached_gases[/datum/gas/oxygen][MOLES] = QUANTIZE(cached_gases[/datum/gas/oxygen][MOLES] - (plasma_burn_rate * oxygen_burn_ratio))
- if (super_saturation)
- ASSERT_GAS(/datum/gas/tritium, air)
- cached_gases[/datum/gas/tritium][MOLES] += plasma_burn_rate
+ plasma_burn_rate = min(plasma_burn_rate, plasma_moles, oxygen_moles * INVERSE(oxygen_burn_ratio)) //Ensures matter is conserved properly
+ cached_moles[/datum/gas/plasma] = QUANTIZE(plasma_moles - plasma_burn_rate)
+ cached_moles[/datum/gas/oxygen] = QUANTIZE(oxygen_moles - (plasma_burn_rate * oxygen_burn_ratio))
+ if(super_saturation)
+ cached_moles[/datum/gas/tritium] += plasma_burn_rate
else
- ASSERT_GAS(/datum/gas/carbon_dioxide, air)
- ASSERT_GAS(/datum/gas/water_vapor, air)
- cached_gases[/datum/gas/carbon_dioxide][MOLES] += plasma_burn_rate * 0.75
- cached_gases[/datum/gas/water_vapor][MOLES] += plasma_burn_rate * 0.25
+ cached_moles[/datum/gas/carbon_dioxide] += plasma_burn_rate * 0.75
+ cached_moles[/datum/gas/water_vapor] += plasma_burn_rate * 0.25
+
SET_REACTION_RESULTS((plasma_burn_rate) * (1 + oxygen_burn_ratio))
var/energy_released = FIRE_PLASMA_ENERGY_RELEASED * plasma_burn_rate
@@ -243,33 +253,35 @@
* Highly exothermic.
* Creates hotspots.
*/
-/datum/gas_reaction/h2fire
+/datum/gas_reaction/standard/h2fire
priority_group = PRIORITY_FIRE
name = "Hydrogen Combustion"
id = "h2fire"
expands_hotspot = TRUE
- desc = "Combustion of hydrogen with oxygen. Can be extremely fast and energetic if a few conditions are fulfilled."
+ desc = "Combustion of Hydrogen with Oxygen. May be extremely fast and energetic, if a few conditions are fulfilled."
-/datum/gas_reaction/h2fire/init_reqs()
+/datum/gas_reaction/standard/h2fire/init_reqs()
requirements = list(
/datum/gas/hydrogen = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
"MIN_TEMP" = HYDROGEN_MINIMUM_BURN_TEMPERATURE,
)
-/datum/gas_reaction/h2fire/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
+/datum/gas_reaction/standard/h2fire/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
+ var/list/cached_moles = air.moles //this speeds things up because accessing datum vars is slow
+ var/hydrogen_moles = cached_moles[/datum/gas/hydrogen]
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen]
var/old_heat_capacity = air.heat_capacity()
var/temperature = air.temperature
- var/burned_fuel = min(cached_gases[/datum/gas/hydrogen][MOLES] / FIRE_HYDROGEN_BURN_RATE_DELTA, cached_gases[/datum/gas/oxygen][MOLES] / (FIRE_HYDROGEN_BURN_RATE_DELTA * HYDROGEN_OXYGEN_FULLBURN), cached_gases[/datum/gas/hydrogen][MOLES], cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(0.5))
- if(burned_fuel <= 0 || cached_gases[/datum/gas/hydrogen][MOLES] - burned_fuel < 0 || cached_gases[/datum/gas/oxygen][MOLES] - burned_fuel * 0.5 < 0) //Shouldn't produce gas from nothing.
- return NO_REACTION
+ var/burned_fuel = min(hydrogen_moles / FIRE_HYDROGEN_BURN_RATE_DELTA, oxygen_moles / (FIRE_HYDROGEN_BURN_RATE_DELTA * HYDROGEN_OXYGEN_FULLBURN), hydrogen_moles, oxygen_moles * INVERSE(0.5))
+ if(burned_fuel <= 0 || hydrogen_moles - burned_fuel < 0 || oxygen_moles - burned_fuel * 0.5 < 0) //Shouldn't produce gas from nothing.
+ return
- cached_gases[/datum/gas/hydrogen][MOLES] -= burned_fuel
- cached_gases[/datum/gas/oxygen][MOLES] -= burned_fuel * 0.5
- ASSERT_GAS(/datum/gas/water_vapor, air)
- cached_gases[/datum/gas/water_vapor][MOLES] += burned_fuel
+ cached_moles[/datum/gas/hydrogen] -= burned_fuel
+ cached_moles[/datum/gas/oxygen] -= burned_fuel * 0.5
+ cached_moles[/datum/gas/water_vapor] += burned_fuel
SET_REACTION_RESULTS(burned_fuel)
@@ -297,33 +309,35 @@
* Creates hotspots.
* Creates radiation.
*/
-/datum/gas_reaction/tritfire
+/datum/gas_reaction/standard/tritfire
priority_group = PRIORITY_FIRE
name = "Tritium Combustion"
id = "tritfire"
expands_hotspot = TRUE
- desc = "Combustion of tritium with oxygen. Can be extremely fast and energetic if a few conditions are fulfilled."
+ desc = "Combustion of Tritium with Oxygen. May be extremely fast and energetic, if a few conditions are fulfilled."
-/datum/gas_reaction/tritfire/init_reqs()
+/datum/gas_reaction/standard/tritfire/init_reqs()
requirements = list(
/datum/gas/tritium = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
"MIN_TEMP" = TRITIUM_MINIMUM_BURN_TEMPERATURE,
)
-/datum/gas_reaction/tritfire/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
+/datum/gas_reaction/standard/tritfire/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
+ var/list/cached_moles = air.moles //this speeds things up because accessing datum vars is slow
+ var/tritium_moles = cached_moles[/datum/gas/tritium]
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen]
var/old_heat_capacity = air.heat_capacity()
var/temperature = air.temperature
- var/burned_fuel = min(cached_gases[/datum/gas/tritium][MOLES] / FIRE_TRITIUM_BURN_RATE_DELTA, cached_gases[/datum/gas/oxygen][MOLES] / (FIRE_TRITIUM_BURN_RATE_DELTA * TRITIUM_OXYGEN_FULLBURN), cached_gases[/datum/gas/tritium][MOLES], cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(0.5))
- if(burned_fuel <= 0 || cached_gases[/datum/gas/tritium][MOLES] - burned_fuel < 0 || cached_gases[/datum/gas/oxygen][MOLES] - burned_fuel * 0.5 < 0) //Shouldn't produce gas from nothing.
- return NO_REACTION
+ var/burned_fuel = min(tritium_moles / FIRE_TRITIUM_BURN_RATE_DELTA, oxygen_moles / (FIRE_TRITIUM_BURN_RATE_DELTA * TRITIUM_OXYGEN_FULLBURN), tritium_moles, oxygen_moles * INVERSE(0.5))
+ if(burned_fuel <= 0 || tritium_moles - burned_fuel < 0 || oxygen_moles - burned_fuel * 0.5 < 0) //Shouldn't produce gas from nothing.
+ return
- cached_gases[/datum/gas/tritium][MOLES] -= burned_fuel
- cached_gases[/datum/gas/oxygen][MOLES] -= burned_fuel * 0.5
- ASSERT_GAS(/datum/gas/water_vapor, air)
- cached_gases[/datum/gas/water_vapor][MOLES] += burned_fuel
+ cached_moles[/datum/gas/tritium] -= burned_fuel
+ cached_moles[/datum/gas/oxygen] -= burned_fuel * 0.5
+ cached_moles[/datum/gas/water_vapor] += burned_fuel
SET_REACTION_RESULTS(burned_fuel)
@@ -359,14 +373,14 @@
* Combustion of oxygen and freon.
* Endothermic.
*/
-/datum/gas_reaction/freonfire
+/datum/gas_reaction/standard/freonfire
priority_group = PRIORITY_FIRE
name = "Freon Combustion"
id = "freonfire"
expands_hotspot = TRUE
- desc = "Reaction between oxygen and freon that consumes a huge amount of energy and can cool things significantly. Also able to produce hot ice."
+ desc = "Reaction between Oxygen and Freon that consumes a huge amount of energy, cooling the atmosphere significantly. May produce \"hot ice\"."
-/datum/gas_reaction/freonfire/init_reqs()
+/datum/gas_reaction/standard/freonfire/init_reqs()
requirements = list(
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
/datum/gas/freon = MINIMUM_MOLE_COUNT,
@@ -374,7 +388,8 @@
"MAX_TEMP" = FREON_MAXIMUM_BURN_TEMPERATURE,
)
-/datum/gas_reaction/freonfire/react(datum/gas_mixture/air, datum/holder)
+/datum/gas_reaction/standard/freonfire/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
var/temperature = air.temperature
var/temperature_scale
if(temperature < FREON_TERMINAL_TEMPERATURE) //stop the reaction when too cold
@@ -388,21 +403,22 @@
var/oxygen_burn_ratio = OXYGEN_BURN_RATIO_BASE - temperature_scale
var/freon_burn_rate
- var/list/cached_gases = air.gases
- if(cached_gases[/datum/gas/oxygen][MOLES] < cached_gases[/datum/gas/freon][MOLES] * FREON_OXYGEN_FULLBURN)
- freon_burn_rate = ((cached_gases[/datum/gas/oxygen][MOLES] / FREON_OXYGEN_FULLBURN) / FREON_BURN_RATE_DELTA) * temperature_scale
+ var/list/cached_moles = air.moles
+ var/freon_moles = cached_moles[/datum/gas/freon]
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen]
+ if(oxygen_moles < freon_moles * FREON_OXYGEN_FULLBURN)
+ freon_burn_rate = ((oxygen_moles / FREON_OXYGEN_FULLBURN) / FREON_BURN_RATE_DELTA) * temperature_scale
else
- freon_burn_rate = (cached_gases[/datum/gas/freon][MOLES] / FREON_BURN_RATE_DELTA) * temperature_scale
+ freon_burn_rate = (freon_moles / FREON_BURN_RATE_DELTA) * temperature_scale
if (freon_burn_rate < MINIMUM_HEAT_CAPACITY)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- freon_burn_rate = min(freon_burn_rate, cached_gases[/datum/gas/freon][MOLES], cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(oxygen_burn_ratio)) //Ensures matter is conserved properly
- cached_gases[/datum/gas/freon][MOLES] = QUANTIZE(cached_gases[/datum/gas/freon][MOLES] - freon_burn_rate)
- cached_gases[/datum/gas/oxygen][MOLES] = QUANTIZE(cached_gases[/datum/gas/oxygen][MOLES] - (freon_burn_rate * oxygen_burn_ratio))
- ASSERT_GAS(/datum/gas/carbon_dioxide, air)
- cached_gases[/datum/gas/carbon_dioxide][MOLES] += freon_burn_rate
+ freon_burn_rate = min(freon_burn_rate, freon_moles, oxygen_moles * INVERSE(oxygen_burn_ratio)) //Ensures matter is conserved properly
+ cached_moles[/datum/gas/freon] = QUANTIZE(freon_moles - freon_burn_rate)
+ cached_moles[/datum/gas/oxygen] = QUANTIZE(oxygen_moles - (freon_burn_rate * oxygen_burn_ratio))
+ cached_moles[/datum/gas/carbon_dioxide] += freon_burn_rate
if(temperature < HOT_ICE_FORMATION_MAXIMUM_TEMPERATURE && temperature > HOT_ICE_FORMATION_MINIMUM_TEMPERATURE && prob(HOT_ICE_FORMATION_PROB) && isturf(holder))
new /obj/item/stack/sheet/hot_ice(holder)
@@ -431,13 +447,13 @@
* Endothermic.
* Requires BZ as a catalyst.
*/
-/datum/gas_reaction/nitrousformation //formation of n2o, exothermic, requires bz as catalyst
+/datum/gas_reaction/standard/nitrousformation //formation of n2o, exothermic, requires bz as catalyst
priority_group = PRIORITY_FORMATION
name = "Nitrous Oxide Formation"
id = "nitrousformation"
- desc = "Production of nitrous oxide with BZ as a catalyst."
+ desc = "Production of Nitrous Oxide with BZ as a catalyst."
-/datum/gas_reaction/nitrousformation/init_reqs()
+/datum/gas_reaction/standard/nitrousformation/init_reqs()
requirements = list(
/datum/gas/oxygen = 10,
/datum/gas/nitrogen = 20,
@@ -446,20 +462,21 @@
"MAX_TEMP" = N2O_FORMATION_MAX_TEMPERATURE,
)
-/datum/gas_reaction/nitrousformation/react(datum/gas_mixture/air)
- var/list/cached_gases = air.gases
- var/heat_efficency = min(cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(0.5), cached_gases[/datum/gas/nitrogen][MOLES])
- if ((cached_gases[/datum/gas/oxygen][MOLES] - heat_efficency * 0.5 < 0 ) || (cached_gases[/datum/gas/nitrogen][MOLES] - heat_efficency < 0))
+/datum/gas_reaction/standard/nitrousformation/react(datum/gas_mixture/air)
+ var/list/cached_moles = air.moles
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen]
+ var/nitrogen_moles = cached_moles[/datum/gas/nitrogen]
+ var/heat_efficiency = min(oxygen_moles * INVERSE(0.5), nitrogen_moles)
+ if ((oxygen_moles - heat_efficiency * 0.5 < 0 ) || (nitrogen_moles - heat_efficiency < 0))
return NO_REACTION // Shouldn't produce gas from nothing.
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/oxygen][MOLES] -= heat_efficency * 0.5
- cached_gases[/datum/gas/nitrogen][MOLES] -= heat_efficency
- ASSERT_GAS(/datum/gas/nitrous_oxide, air)
- cached_gases[/datum/gas/nitrous_oxide][MOLES] += heat_efficency
+ cached_moles[/datum/gas/oxygen] -= heat_efficiency * 0.5
+ cached_moles[/datum/gas/nitrogen] -= heat_efficiency
+ cached_moles[/datum/gas/nitrous_oxide] += heat_efficiency
- SET_REACTION_RESULTS(heat_efficency)
- var/energy_released = heat_efficency * N2O_FORMATION_ENERGY
+ SET_REACTION_RESULTS(heat_efficiency)
+ var/energy_released = heat_efficiency * N2O_FORMATION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((air.temperature * old_heat_capacity + energy_released) / new_heat_capacity), TCMB) // The air cools down when reacting.
@@ -472,34 +489,31 @@
* Decomposition of N2O.
* Exothermic.
*/
-/datum/gas_reaction/nitrous_decomp
+/datum/gas_reaction/standard/nitrous_decomp
priority_group = PRIORITY_POST_FORMATION
name = "Nitrous Oxide Decomposition"
id = "nitrous_decomp"
- desc = "Decomposition of nitrous oxide under high temperature."
+ desc = "Decomposition of Nitrous Oxide under high temperature."
-/datum/gas_reaction/nitrous_decomp/init_reqs()
+/datum/gas_reaction/standard/nitrous_decomp/init_reqs()
requirements = list(
/datum/gas/nitrous_oxide = MINIMUM_MOLE_COUNT * 2,
"MIN_TEMP" = N2O_DECOMPOSITION_MIN_TEMPERATURE,
"MAX_TEMP" = N2O_DECOMPOSITION_MAX_TEMPERATURE,
)
-/datum/gas_reaction/nitrous_decomp/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
+/datum/gas_reaction/standard/nitrous_decomp/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles //this speeds things up because accessing datum vars is slow
+ var/nitrous_oxide_moles = cached_moles[/datum/gas/nitrous_oxide]
var/temperature = air.temperature
- var/burned_fuel = (cached_gases[/datum/gas/nitrous_oxide][MOLES] / N2O_DECOMPOSITION_RATE_DIVISOR) * ((temperature - N2O_DECOMPOSITION_MIN_SCALE_TEMP) * (temperature - N2O_DECOMPOSITION_MAX_SCALE_TEMP) / (N2O_DECOMPOSITION_SCALE_DIVISOR))
- if(burned_fuel <= 0)
- return NO_REACTION
- if(cached_gases[/datum/gas/nitrous_oxide][MOLES] - burned_fuel < 0)
+ var/burned_fuel = (nitrous_oxide_moles / N2O_DECOMPOSITION_RATE_DIVISOR) * ((temperature - N2O_DECOMPOSITION_MIN_SCALE_TEMP) * (temperature - N2O_DECOMPOSITION_MAX_SCALE_TEMP) / (N2O_DECOMPOSITION_SCALE_DIVISOR))
+ if(burned_fuel <= 0 || nitrous_oxide_moles - burned_fuel < 0)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/nitrous_oxide][MOLES] -= burned_fuel
- ASSERT_GAS(/datum/gas/nitrogen, air)
- cached_gases[/datum/gas/nitrogen][MOLES] += burned_fuel
- ASSERT_GAS(/datum/gas/oxygen, air)
- cached_gases[/datum/gas/oxygen][MOLES] += burned_fuel / 2
+ cached_moles[/datum/gas/nitrous_oxide] -= burned_fuel
+ cached_moles[/datum/gas/nitrogen] += burned_fuel
+ cached_moles[/datum/gas/oxygen] += burned_fuel / 2
SET_REACTION_RESULTS(burned_fuel)
var/energy_released = N2O_DECOMPOSITION_ENERGY * burned_fuel
@@ -517,29 +531,31 @@
* Formation of BZ by combining plasma and nitrous oxide at low pressures.
* Exothermic.
*/
-/datum/gas_reaction/bzformation
+/datum/gas_reaction/standard/bzformation
priority_group = PRIORITY_FORMATION
name = "BZ Gas Formation"
id = "bzformation"
- desc = "Production of BZ using plasma and nitrous oxide."
+ desc = "Production of BZ using Plasma and Nitrous Oxide."
-/datum/gas_reaction/bzformation/init_reqs()
+/datum/gas_reaction/standard/bzformation/init_reqs()
requirements = list(
/datum/gas/nitrous_oxide = 10,
/datum/gas/plasma = 10,
"MAX_TEMP" = BZ_FORMATION_MAX_TEMPERATURE,
)
-/datum/gas_reaction/bzformation/react(datum/gas_mixture/air)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/bzformation/react(datum/gas_mixture/air)
+ var/list/cached_moles = air.moles
+ var/nitrous_oxide_moles = cached_moles[/datum/gas/nitrous_oxide]
+ var/plasma_moles = cached_moles[/datum/gas/plasma]
var/pressure = air.return_pressure()
var/volume = air.return_volume()
var/environment_effciency = volume/pressure //More volume and less pressure gives better rates
- var/ratio_efficency = min(cached_gases[/datum/gas/nitrous_oxide][MOLES]/cached_gases[/datum/gas/plasma][MOLES], 1) //Less n2o than plasma give lower rates
- var/nitrous_oxide_decomposed_factor = max(4 * (cached_gases[/datum/gas/plasma][MOLES] / (cached_gases[/datum/gas/nitrous_oxide][MOLES] + cached_gases[/datum/gas/plasma][MOLES]) - 0.75), 0) // Nitrous oxide decomposes when there are more than 3 parts plasma per n2o.
- var/bz_formed = min(0.01 * ratio_efficency * environment_effciency, cached_gases[/datum/gas/nitrous_oxide][MOLES] * INVERSE(0.4), cached_gases[/datum/gas/plasma][MOLES] * INVERSE(0.8 * (1 - nitrous_oxide_decomposed_factor)))
+ var/ratio_efficency = min(nitrous_oxide_moles/plasma_moles, 1) //Less n2o than plasma give lower rates
+ var/nitrous_oxide_decomposed_factor = max(4 * (plasma_moles / (nitrous_oxide_moles + plasma_moles) - 0.75), 0) // Nitrous oxide decomposes when there are more than 3 parts plasma per n2o.
+ var/bz_formed = min(0.01 * ratio_efficency * environment_effciency, nitrous_oxide_moles * INVERSE(0.4), plasma_moles * INVERSE(0.8 * (1 - nitrous_oxide_decomposed_factor)))
- if (cached_gases[/datum/gas/nitrous_oxide][MOLES] - bz_formed * 0.4 < 0 || cached_gases[/datum/gas/plasma][MOLES] - 0.8 * bz_formed * (1 - nitrous_oxide_decomposed_factor) < 0 || bz_formed <= 0)
+ if (nitrous_oxide_moles - bz_formed * 0.4 < 0 || plasma_moles - 0.8 * bz_formed * (1 - nitrous_oxide_decomposed_factor) < 0 || bz_formed <= 0)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
@@ -551,16 +567,13 @@
*N2O decomposes with its normal decomposition energy
*/
if (nitrous_oxide_decomposed_factor>0)
- ASSERT_GAS(/datum/gas/nitrogen, air)
- ASSERT_GAS(/datum/gas/oxygen, air)
var/amount_decomposed = 0.4 * bz_formed * nitrous_oxide_decomposed_factor
- cached_gases[/datum/gas/nitrogen] += amount_decomposed
- cached_gases[/datum/gas/oxygen] += 0.5 * amount_decomposed
+ cached_moles[/datum/gas/nitrogen] += amount_decomposed
+ cached_moles[/datum/gas/oxygen] += 0.5 * amount_decomposed
- ASSERT_GAS(/datum/gas/bz, air)
- cached_gases[/datum/gas/bz][MOLES] += bz_formed * (1-nitrous_oxide_decomposed_factor)
- cached_gases[/datum/gas/nitrous_oxide][MOLES] -= 0.4 * bz_formed
- cached_gases[/datum/gas/plasma][MOLES] -= 0.8 * bz_formed * (1-nitrous_oxide_decomposed_factor)
+ cached_moles[/datum/gas/bz] += bz_formed * (1-nitrous_oxide_decomposed_factor)
+ cached_moles[/datum/gas/nitrous_oxide] -= 0.4 * bz_formed
+ cached_moles[/datum/gas/plasma] -= 0.8 * bz_formed * (1-nitrous_oxide_decomposed_factor)
SET_REACTION_RESULTS(bz_formed)
var/energy_released = bz_formed * (BZ_FORMATION_ENERGY + nitrous_oxide_decomposed_factor * (N2O_DECOMPOSITION_ENERGY - BZ_FORMATION_ENERGY))
@@ -578,13 +591,13 @@
* Consumes a tiny amount of tritium to convert CO2 and oxygen to pluoxium.
* Exothermic.
*/
-/datum/gas_reaction/pluox_formation
+/datum/gas_reaction/standard/pluox_formation
priority_group = PRIORITY_FORMATION
name = "Pluoxium Formation"
id = "pluox_formation"
- desc = "Alternate production for pluoxium which uses tritium."
+ desc = "Alternate Production method for Pluoxium which uses Tritium."
-/datum/gas_reaction/pluox_formation/init_reqs()
+/datum/gas_reaction/standard/pluox_formation/init_reqs()
requirements = list(
/datum/gas/carbon_dioxide = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
@@ -593,20 +606,21 @@
"MAX_TEMP" = PLUOXIUM_FORMATION_MAX_TEMP,
)
-/datum/gas_reaction/pluox_formation/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
- var/produced_amount = min(PLUOXIUM_FORMATION_MAX_RATE, cached_gases[/datum/gas/carbon_dioxide][MOLES], cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(0.5), cached_gases[/datum/gas/tritium][MOLES] * INVERSE(0.01))
- if (produced_amount <= 0 || cached_gases[/datum/gas/carbon_dioxide][MOLES] - produced_amount < 0 || cached_gases[/datum/gas/oxygen][MOLES] - produced_amount * 0.5 < 0 || cached_gases[/datum/gas/tritium][MOLES] - produced_amount * 0.01 < 0)
+/datum/gas_reaction/standard/pluox_formation/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles
+ var/carbon_dioxide_moles = cached_moles[/datum/gas/carbon_dioxide]
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen]
+ var/tritium_moles = cached_moles[/datum/gas/tritium]
+ var/produced_amount = min(PLUOXIUM_FORMATION_MAX_RATE, carbon_dioxide_moles, oxygen_moles * INVERSE(0.5), tritium_moles * INVERSE(0.01))
+ if (produced_amount <= 0 || carbon_dioxide_moles - produced_amount < 0 || oxygen_moles - produced_amount * 0.5 < 0 || tritium_moles - produced_amount * 0.01 < 0)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/carbon_dioxide][MOLES] -= produced_amount
- cached_gases[/datum/gas/oxygen][MOLES] -= produced_amount * 0.5
- cached_gases[/datum/gas/tritium][MOLES] -= produced_amount * 0.01
- ASSERT_GAS(/datum/gas/pluoxium, air)
- cached_gases[/datum/gas/pluoxium][MOLES] += produced_amount
- ASSERT_GAS(/datum/gas/hydrogen, air)
- cached_gases[/datum/gas/hydrogen][MOLES] += produced_amount * 0.01
+ cached_moles[/datum/gas/carbon_dioxide] -= produced_amount
+ cached_moles[/datum/gas/oxygen] -= produced_amount * 0.5
+ cached_moles[/datum/gas/tritium] -= produced_amount * 0.01
+ cached_moles[/datum/gas/pluoxium] += produced_amount
+ cached_moles[/datum/gas/hydrogen] += produced_amount * 0.01
SET_REACTION_RESULTS(produced_amount)
var/energy_released = produced_amount * PLUOXIUM_FORMATION_ENERGY
@@ -625,13 +639,13 @@
* Endothermic.
* Requires BZ.
*/
-/datum/gas_reaction/nitrium_formation
+/datum/gas_reaction/standard/nitrium_formation
priority_group = PRIORITY_FORMATION
name = "Nitrium Formation"
id = "nitrium_formation"
- desc = "Production of nitrium from BZ, tritium, and nitrogen."
+ desc = "Production of Nitrium from BZ, Tritium, and Nitrogen."
-/datum/gas_reaction/nitrium_formation/init_reqs()
+/datum/gas_reaction/standard/nitrium_formation/init_reqs()
requirements = list(
/datum/gas/tritium = 20,
/datum/gas/nitrogen = 10,
@@ -639,23 +653,26 @@
"MIN_TEMP" = NITRIUM_FORMATION_MIN_TEMP,
)
-/datum/gas_reaction/nitrium_formation/react(datum/gas_mixture/air)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/nitrium_formation/react(datum/gas_mixture/air)
+ var/list/cached_moles = air.moles
+ var/tritium_moles = cached_moles[/datum/gas/tritium]
+ var/nitrogen_moles = cached_moles[/datum/gas/nitrogen]
+ var/bz_moles = cached_moles[/datum/gas/bz]
var/temperature = air.temperature
- var/heat_efficency = min(temperature / NITRIUM_FORMATION_TEMP_DIVISOR, cached_gases[/datum/gas/tritium][MOLES], cached_gases[/datum/gas/nitrogen][MOLES], cached_gases[/datum/gas/bz][MOLES] * INVERSE(0.05))
+ var/heat_efficiency = min(temperature / NITRIUM_FORMATION_TEMP_DIVISOR, tritium_moles, nitrogen_moles, bz_moles * INVERSE(0.05))
- if( heat_efficency <= 0 || (cached_gases[/datum/gas/tritium][MOLES] - heat_efficency < 0 ) || (cached_gases[/datum/gas/nitrogen][MOLES] - heat_efficency < 0) || (cached_gases[/datum/gas/bz][MOLES] - heat_efficency * 0.05 < 0)) //Shouldn't produce gas from nothing.
+ if( heat_efficiency <= 0 || (tritium_moles - heat_efficiency < 0 ) || (nitrogen_moles - heat_efficiency < 0) || (bz_moles - heat_efficiency * 0.05 < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- ASSERT_GAS(/datum/gas/nitrium, air)
- cached_gases[/datum/gas/tritium][MOLES] -= heat_efficency
- cached_gases[/datum/gas/nitrogen][MOLES] -= heat_efficency
- cached_gases[/datum/gas/bz][MOLES] -= heat_efficency * 0.05 //bz gets consumed to balance the nitrium production and not make it too common and/or easy
- cached_gases[/datum/gas/nitrium][MOLES] += heat_efficency
-
- SET_REACTION_RESULTS(heat_efficency)
- var/energy_used = heat_efficency * NITRIUM_FORMATION_ENERGY
+ cached_moles[/datum/gas/tritium] -= heat_efficiency
+ cached_moles[/datum/gas/nitrogen] -= heat_efficiency
+ cached_moles[/datum/gas/bz] -= heat_efficiency * 0.05 //bz gets consumed to balance the nitrium production and not make it too common and/or easy
+ cached_moles[/datum/gas/nitrium] += heat_efficiency
+
+
+ SET_REACTION_RESULTS(heat_efficiency)
+ var/energy_used = heat_efficiency * NITRIUM_FORMATION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature * old_heat_capacity - energy_used) / new_heat_capacity), TCMB) //the air cools down when reacting
@@ -669,37 +686,38 @@
* Exothermic.
* Requires oxygen as catalyst.
*/
-/datum/gas_reaction/nitrium_decomposition
+/datum/gas_reaction/standard/nitrium_decomposition
priority_group = PRIORITY_PRE_FORMATION
name = "Nitrium Decomposition"
id = "nitrium_decomp"
- desc = "Decomposition of nitrium when exposed to oxygen under normal temperatures."
+ desc = "Decomposition of Nitrium when exposed to Oxygen under normal temperatures."
-/datum/gas_reaction/nitrium_decomposition/init_reqs()
+/datum/gas_reaction/standard/nitrium_decomposition/init_reqs()
requirements = list(
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
/datum/gas/nitrium = MINIMUM_MOLE_COUNT,
"MAX_TEMP" = NITRIUM_DECOMPOSITION_MAX_TEMP,
)
-/datum/gas_reaction/nitrium_decomposition/react(datum/gas_mixture/air)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/nitrium_decomposition/react(datum/gas_mixture/air)
+ var/list/cached_moles = air.moles
+ var/nitrium_moles = cached_moles[/datum/gas/nitrium]
var/temperature = air.temperature
- //This reaction is agressively slow. like, a tenth of a mole per fire slow. Keep that in mind
- var/heat_efficency = min(temperature / NITRIUM_DECOMPOSITION_TEMP_DIVISOR, cached_gases[/datum/gas/nitrium][MOLES])
+ //This reaction is aggressively slow. like, a tenth of a mole per fire slow. Keep that in mind
+ var/heat_efficiency = min(temperature / NITRIUM_DECOMPOSITION_TEMP_DIVISOR, nitrium_moles)
- if (heat_efficency <= 0 || (cached_gases[/datum/gas/nitrium][MOLES] - heat_efficency < 0)) //Shouldn't produce gas from nothing.
+ if (heat_efficiency <= 0 || (nitrium_moles - heat_efficiency < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
air.assert_gases(/datum/gas/nitrogen, /datum/gas/hydrogen)
- cached_gases[/datum/gas/nitrium][MOLES] -= heat_efficency
- cached_gases[/datum/gas/hydrogen][MOLES] += heat_efficency
- cached_gases[/datum/gas/nitrogen][MOLES] += heat_efficency
+ cached_moles[/datum/gas/nitrium] -= heat_efficiency
+ cached_moles[/datum/gas/hydrogen] += heat_efficiency
+ cached_moles[/datum/gas/nitrogen] += heat_efficiency
- SET_REACTION_RESULTS(heat_efficency)
- var/energy_released = heat_efficency * NITRIUM_DECOMPOSITION_ENERGY
+ SET_REACTION_RESULTS(heat_efficiency)
+ var/energy_released = heat_efficiency * NITRIUM_DECOMPOSITION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature * old_heat_capacity + energy_released) / new_heat_capacity), TCMB) //the air heats up when reacting
@@ -712,13 +730,13 @@
* The formation of freon.
* Endothermic.
*/
-/datum/gas_reaction/freonformation
+/datum/gas_reaction/standard/freonformation
priority_group = PRIORITY_FORMATION
name = "Freon Formation"
id = "freonformation"
- desc = "Production of freon using plasma, carbon dioxide, and BZ under high temperature."
+ desc = "Production of Freon using Plasma, Carbon Dioxide, and BZ under high temperature."
-/datum/gas_reaction/freonformation/init_reqs() //minimum requirements for freon formation
+/datum/gas_reaction/standard/freonformation/init_reqs() //minimum requirements for freon formation
requirements = list(
/datum/gas/plasma = MINIMUM_MOLE_COUNT * 6,
/datum/gas/carbon_dioxide = MINIMUM_MOLE_COUNT * 3,
@@ -726,25 +744,27 @@
"MIN_TEMP" = FREON_FORMATION_MIN_TEMPERATURE,
)
-/datum/gas_reaction/freonformation/react(datum/gas_mixture/air)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/freonformation/react(datum/gas_mixture/air)
+ var/list/cached_moles = air.moles
+ var/plasma_moles = cached_moles[/datum/gas/plasma]
+ var/carbon_dioxide_moles = cached_moles[/datum/gas/carbon_dioxide]
+ var/bz_moles = cached_moles[/datum/gas/bz]
var/temperature = air.temperature
- var/minimal_mole_factor = min(cached_gases[/datum/gas/plasma][MOLES] * INVERSE(0.6), cached_gases[/datum/gas/bz][MOLES] * INVERSE(0.1), cached_gases[/datum/gas/carbon_dioxide][MOLES] * INVERSE(0.3))
+ var/minimal_mole_factor = min(plasma_moles * INVERSE(0.6), bz_moles * INVERSE(0.1), carbon_dioxide_moles * INVERSE(0.3))
var/equation_first_part = NUM_E ** (-(((temperature - 800) / 200) ** 2))
var/equation_second_part = 3 / (1 + NUM_E ** (-0.001 * (temperature - 6000)))
var/heat_factor = equation_first_part + equation_second_part
- var/freon_formed = min(heat_factor * minimal_mole_factor * 0.05, cached_gases[/datum/gas/plasma][MOLES] * INVERSE(0.6), cached_gases[/datum/gas/carbon_dioxide][MOLES] * INVERSE(0.3), cached_gases[/datum/gas/bz][MOLES] * INVERSE(0.1))
- if (freon_formed <= 0 || (cached_gases[/datum/gas/plasma][MOLES] - freon_formed * 0.6 < 0 ) || (cached_gases[/datum/gas/carbon_dioxide][MOLES] - freon_formed * 0.3 < 0) || (cached_gases[/datum/gas/bz][MOLES] - freon_formed * 0.1 < 0)) //Shouldn't produce gas from nothing.
+ var/freon_formed = min(heat_factor * minimal_mole_factor * 0.05, plasma_moles * INVERSE(0.6), carbon_dioxide_moles * INVERSE(0.3), bz_moles * INVERSE(0.1))
+ if (freon_formed <= 0 || (plasma_moles - freon_formed * 0.6 < 0 ) || (carbon_dioxide_moles - freon_formed * 0.3 < 0) || (bz_moles - freon_formed * 0.1 < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- ASSERT_GAS(/datum/gas/freon, air)
- cached_gases[/datum/gas/plasma][MOLES] -= freon_formed * 0.6
- cached_gases[/datum/gas/carbon_dioxide][MOLES] -= freon_formed * 0.3
- cached_gases[/datum/gas/bz][MOLES] -= freon_formed * 0.1
- cached_gases[/datum/gas/freon][MOLES] += freon_formed
+ cached_moles[/datum/gas/plasma] -= freon_formed * 0.6
+ cached_moles[/datum/gas/carbon_dioxide] -= freon_formed * 0.3
+ cached_moles[/datum/gas/bz] -= freon_formed * 0.1
+ cached_moles[/datum/gas/freon] += freon_formed
SET_REACTION_RESULTS(freon_formed)
@@ -763,13 +783,13 @@
* Due to its high mass, hyper-noblium uses large amounts of nitrogen and tritium.
* BZ can be used as a catalyst to make it less exothermic.
*/
-/datum/gas_reaction/nobliumformation
+/datum/gas_reaction/standard/nobliumformation
priority_group = PRIORITY_FORMATION
name = "Hyper-Noblium Condensation"
id = "nobformation"
- desc = "Production of hyper-noblium from nitrogen and tritium under very low temperatures. Extremely energetic."
+ desc = "Production of Hyper-Noblium from Nitrogen and Tritium under very low temperatures. Extremely energetic."
-/datum/gas_reaction/nobliumformation/init_reqs()
+/datum/gas_reaction/standard/nobliumformation/init_reqs()
requirements = list(
/datum/gas/nitrogen = 10,
/datum/gas/tritium = 5,
@@ -777,24 +797,30 @@
"MAX_TEMP" = NOBLIUM_FORMATION_MAX_TEMP,
)
-/datum/gas_reaction/nobliumformation/react(datum/gas_mixture/air)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/nobliumformation/react(datum/gas_mixture/air)
+ . = NO_REACTION
+ var/list/cached_moles = air.moles
+ var/nitrogen_moles = cached_moles[/datum/gas/nitrogen]
+ var/tritium_moles = cached_moles[/datum/gas/tritium]
/// List of gases we will assert, and possibly garbage collect.
var/list/asserted_gases = list(/datum/gas/hypernoblium, /datum/gas/bz)
air.assert_gases(arglist(asserted_gases))
- var/reduction_factor = clamp(cached_gases[/datum/gas/tritium][MOLES] / (cached_gases[/datum/gas/tritium][MOLES] + cached_gases[/datum/gas/bz][MOLES]), 0.001 , 1) //reduces trit consumption in presence of bz upward to 0.1% reduction
- var/nob_formed = min((cached_gases[/datum/gas/nitrogen][MOLES] + cached_gases[/datum/gas/tritium][MOLES]) * 0.01, cached_gases[/datum/gas/tritium][MOLES] * INVERSE(5 * reduction_factor), cached_gases[/datum/gas/nitrogen][MOLES] * INVERSE(10))
+ var/bz_moles = cached_moles[/datum/gas/bz]
+ var/reduction_factor = clamp(tritium_moles / (tritium_moles + bz_moles), 0.001 , 1) //reduces trit consumption in presence of bz upward to 0.1% reduction
+ var/nob_formed = min((nitrogen_moles + tritium_moles) * 0.01, tritium_moles * INVERSE(5 * reduction_factor), nitrogen_moles * INVERSE(10))
- if (nob_formed <= 0 || (cached_gases[/datum/gas/tritium][MOLES] - 5 * nob_formed * reduction_factor < 0) || (cached_gases[/datum/gas/nitrogen][MOLES] - 10 * nob_formed < 0))
- air.garbage_collect(arglist(asserted_gases))
- return NO_REACTION
+ //calling QUANTIZE on results to round very small floating point values.
+ if (QUANTIZE(nob_formed) <= 0 || (QUANTIZE(tritium_moles - 5 * nob_formed * reduction_factor) < 0) || (QUANTIZE(nitrogen_moles - 10 * nob_formed) < 0))
+ air.garbage_collect()
+ return
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/tritium][MOLES] -= 5 * nob_formed * reduction_factor
- cached_gases[/datum/gas/nitrogen][MOLES] -= 10 * nob_formed
- cached_gases[/datum/gas/hypernoblium][MOLES] += nob_formed // I'm not going to nitpick, but N20H10 feels like it should be an explosive more than anything.
+ cached_moles[/datum/gas/tritium] -= 5 * nob_formed * reduction_factor
+ cached_moles[/datum/gas/nitrogen] -= 10 * nob_formed
+ cached_moles[/datum/gas/hypernoblium] += nob_formed // I'm not going to nitpick, but N20H10 feels like it should be an explosive more than anything.
+
SET_REACTION_RESULTS(nob_formed)
- var/energy_released = nob_formed * (NOBLIUM_FORMATION_ENERGY / (max(cached_gases[/datum/gas/bz][MOLES], 1)))
+ var/energy_released = nob_formed * NOBLIUM_FORMATION_ENERGY / max(bz_moles, 1)
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((air.temperature * old_heat_capacity + energy_released) / new_heat_capacity), TCMB)
@@ -810,35 +836,37 @@
* Produces carbon dioxide.
* Endothermic.
*/
-/datum/gas_reaction/halon_o2removal
+/datum/gas_reaction/standard/halon_o2removal
priority_group = PRIORITY_PRE_FORMATION
name = "Halon Oxygen Absorption"
id = "halon_o2removal"
- desc = "Halon interaction with oxygen that can be used to snuff fires out."
+ desc = "Halon interaction with Oxygen that can be used to snuff fires out."
-/datum/gas_reaction/halon_o2removal/init_reqs()
+/datum/gas_reaction/standard/halon_o2removal/init_reqs()
requirements = list(
/datum/gas/halon = MINIMUM_MOLE_COUNT,
/datum/gas/oxygen = MINIMUM_MOLE_COUNT,
"MIN_TEMP" = FIRE_MINIMUM_TEMPERATURE_TO_EXIST,
)
-/datum/gas_reaction/halon_o2removal/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/halon_o2removal/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
+ var/list/cached_moles = air.moles
+ var/halon_moles = cached_moles[/datum/gas/halon]
+ var/oxygen_moles = cached_moles[/datum/gas/oxygen]
var/temperature = air.temperature
- var/heat_efficency = min(temperature / ( FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 10), cached_gases[/datum/gas/halon][MOLES], cached_gases[/datum/gas/oxygen][MOLES] * INVERSE(20))
- if (heat_efficency <= 0 || (cached_gases[/datum/gas/halon][MOLES] - heat_efficency < 0 ) || (cached_gases[/datum/gas/oxygen][MOLES] - heat_efficency * 20 < 0)) //Shouldn't produce gas from nothing.
- return NO_REACTION
+ var/heat_efficiency = min(temperature / HALON_COMBUSTION_TEMPERATURE_SCALE, halon_moles, oxygen_moles * INVERSE(20))
+ if (heat_efficiency <= 0 || (halon_moles - heat_efficiency < 0 ) || (oxygen_moles - heat_efficiency * 20 < 0)) //Shouldn't produce gas from nothing.
+ return
var/old_heat_capacity = air.heat_capacity()
- ASSERT_GAS(/datum/gas/carbon_dioxide, air)
- cached_gases[/datum/gas/halon][MOLES] -= heat_efficency
- cached_gases[/datum/gas/oxygen][MOLES] -= heat_efficency * 20
- cached_gases[/datum/gas/carbon_dioxide][MOLES] += heat_efficency * 5
+ cached_moles[/datum/gas/halon] -= heat_efficiency
+ cached_moles[/datum/gas/oxygen] -= heat_efficiency * 20
+ cached_moles[/datum/gas/pluoxium] += heat_efficiency * 2.5
- SET_REACTION_RESULTS(heat_efficency * 5)
- var/energy_used = heat_efficency * HALON_COMBUSTION_ENERGY
+ SET_REACTION_RESULTS(heat_efficiency * 5)
+ var/energy_used = heat_efficiency * HALON_COMBUSTION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature * old_heat_capacity - energy_used) / new_heat_capacity), TCMB)
@@ -852,13 +880,13 @@
*
* Exothermic
*/
-/datum/gas_reaction/healium_formation
+/datum/gas_reaction/standard/healium_formation
priority_group = PRIORITY_FORMATION
name = "Healium Formation"
id = "healium_formation"
- desc = "Production of healium using BZ and freon."
+ desc = "Production of Healium using BZ and Freon."
-/datum/gas_reaction/healium_formation/init_reqs()
+/datum/gas_reaction/standard/healium_formation/init_reqs()
requirements = list(
/datum/gas/bz = MINIMUM_MOLE_COUNT,
/datum/gas/freon = MINIMUM_MOLE_COUNT,
@@ -866,21 +894,22 @@
"MAX_TEMP" = HEALIUM_FORMATION_MAX_TEMP,
)
-/datum/gas_reaction/healium_formation/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/healium_formation/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles
+ var/bz_moles = cached_moles[/datum/gas/bz]
+ var/freon_moles = cached_moles[/datum/gas/freon]
var/temperature = air.temperature
- var/heat_efficency = min(temperature * 0.3, cached_gases[/datum/gas/freon][MOLES] * INVERSE(2.75), cached_gases[/datum/gas/bz][MOLES] * INVERSE(0.25))
- if (heat_efficency <= 0 || (cached_gases[/datum/gas/freon][MOLES] - heat_efficency * 2.75 < 0 ) || (cached_gases[/datum/gas/bz][MOLES] - heat_efficency * 0.25 < 0)) //Shouldn't produce gas from nothing.
+ var/heat_efficiency = min(temperature * 0.3, freon_moles * INVERSE(2.75), bz_moles * INVERSE(0.25))
+ if (heat_efficiency <= 0 || (freon_moles - heat_efficiency * 2.75 < 0 ) || (bz_moles - heat_efficiency * 0.25 < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- ASSERT_GAS(/datum/gas/healium, air)
- cached_gases[/datum/gas/freon][MOLES] -= heat_efficency * 2.75
- cached_gases[/datum/gas/bz][MOLES] -= heat_efficency * 0.25
- cached_gases[/datum/gas/healium][MOLES] += heat_efficency * 3
+ cached_moles[/datum/gas/freon] -= heat_efficiency * 2.75
+ cached_moles[/datum/gas/bz] -= heat_efficiency * 0.25
+ cached_moles[/datum/gas/healium] += heat_efficiency * 3
- SET_REACTION_RESULTS(heat_efficency * 3)
- var/energy_released = heat_efficency * HEALIUM_FORMATION_ENERGY
+ SET_REACTION_RESULTS(heat_efficiency * 3)
+ var/energy_released = heat_efficiency * HEALIUM_FORMATION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature * old_heat_capacity + energy_released) / new_heat_capacity), TCMB)
@@ -892,13 +921,13 @@
* Exothermic.
* Requires Hypernoblium.
*/
-/datum/gas_reaction/zauker_formation
+/datum/gas_reaction/standard/zauker_formation
priority_group = PRIORITY_FORMATION
name = "Zauker Formation"
id = "zauker_formation"
- desc = "Production of zauker using hyper-noblium and nitrium under very high temperatures."
+ desc = "Production of Zauker using Hyper-Noblium and Nitrium under very high temperatures."
-/datum/gas_reaction/zauker_formation/init_reqs()
+/datum/gas_reaction/standard/zauker_formation/init_reqs()
requirements = list(
/datum/gas/hypernoblium = MINIMUM_MOLE_COUNT,
/datum/gas/nitrium = MINIMUM_MOLE_COUNT,
@@ -906,22 +935,23 @@
"MAX_TEMP" = ZAUKER_FORMATION_MAX_TEMPERATURE,
)
-/datum/gas_reaction/zauker_formation/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/zauker_formation/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles
+ var/hypernoblium_moles = cached_moles[/datum/gas/hypernoblium]
+ var/nitrium_moles = cached_moles[/datum/gas/nitrium]
var/temperature = air.temperature
- var/heat_efficency = min(temperature * ZAUKER_FORMATION_TEMPERATURE_SCALE, cached_gases[/datum/gas/hypernoblium][MOLES] * INVERSE(0.01), cached_gases[/datum/gas/nitrium][MOLES] * INVERSE(0.5))
- if (heat_efficency <= 0 || (cached_gases[/datum/gas/hypernoblium][MOLES] - heat_efficency * 0.01 < 0 ) || (cached_gases[/datum/gas/nitrium][MOLES] - heat_efficency * 0.5 < 0)) //Shouldn't produce gas from nothing.
+ var/heat_efficiency = min(temperature * ZAUKER_FORMATION_TEMPERATURE_SCALE, hypernoblium_moles * INVERSE(0.01), nitrium_moles * INVERSE(0.5))
+ if (heat_efficiency <= 0 || (hypernoblium_moles - heat_efficiency * 0.01 < 0 ) || (nitrium_moles - heat_efficiency * 0.5 < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- ASSERT_GAS(/datum/gas/zauker, air)
- cached_gases[/datum/gas/hypernoblium][MOLES] -= heat_efficency * 0.01
- cached_gases[/datum/gas/nitrium][MOLES] -= heat_efficency * 0.5
- cached_gases[/datum/gas/zauker][MOLES] += heat_efficency * 0.5
+ cached_moles[/datum/gas/hypernoblium] -= heat_efficiency * 0.01
+ cached_moles[/datum/gas/nitrium] -= heat_efficiency * 0.5
+ cached_moles[/datum/gas/zauker] += heat_efficiency * 0.5
- SET_REACTION_RESULTS(heat_efficency * 0.5)
- var/energy_used = heat_efficency * ZAUKER_FORMATION_ENERGY
+ SET_REACTION_RESULTS(heat_efficiency * 0.5)
+ var/energy_used = heat_efficiency * ZAUKER_FORMATION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature * old_heat_capacity - energy_used) / new_heat_capacity), TCMB)
@@ -934,30 +964,30 @@
* Occurs in the presence of nitrogen to prevent zauker floods.
* Exothermic.
*/
-/datum/gas_reaction/zauker_decomp
+/datum/gas_reaction/standard/zauker_decomp
priority_group = PRIORITY_POST_FORMATION
name = "Zauker Decomposition"
id = "zauker_decomp"
- desc = "Decomposition of zauker when exposed to nitrogen."
+ desc = "Decomposition of Zauker when exposed to Nitrogen."
-/datum/gas_reaction/zauker_decomp/init_reqs()
+/datum/gas_reaction/standard/zauker_decomp/init_reqs()
requirements = list(
/datum/gas/nitrogen = MINIMUM_MOLE_COUNT,
/datum/gas/zauker = MINIMUM_MOLE_COUNT,
)
-/datum/gas_reaction/zauker_decomp/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases //this speeds things up because accessing datum vars is slow
- var/burned_fuel = min(ZAUKER_DECOMPOSITION_MAX_RATE, cached_gases[/datum/gas/nitrogen][MOLES], cached_gases[/datum/gas/zauker][MOLES])
- if (burned_fuel <= 0 || cached_gases[/datum/gas/zauker][MOLES] - burned_fuel < 0)
+/datum/gas_reaction/standard/zauker_decomp/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles //this speeds things up because accessing datum vars is slow
+ var/nitrogen_moles = cached_moles[/datum/gas/nitrogen]
+ var/zauker_moles = cached_moles[/datum/gas/zauker]
+ var/burned_fuel = min(ZAUKER_DECOMPOSITION_MAX_RATE, nitrogen_moles, zauker_moles)
+ if (burned_fuel <= 0 || zauker_moles - burned_fuel < 0)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/zauker][MOLES] -= burned_fuel
- ASSERT_GAS(/datum/gas/oxygen, air)
- cached_gases[/datum/gas/oxygen][MOLES] += burned_fuel * 0.3
- ASSERT_GAS(/datum/gas/nitrogen, air)
- cached_gases[/datum/gas/nitrogen][MOLES] += burned_fuel * 0.7
+ cached_moles[/datum/gas/zauker] -= burned_fuel
+ cached_moles[/datum/gas/oxygen] += burned_fuel * 0.3
+ cached_moles[/datum/gas/nitrogen] += burned_fuel * 0.7
SET_REACTION_RESULTS(burned_fuel)
var/energy_released = ZAUKER_DECOMPOSITION_ENERGY * burned_fuel
@@ -974,13 +1004,13 @@
*
* Exothermic.
*/
-/datum/gas_reaction/proto_nitrate_formation
+/datum/gas_reaction/standard/proto_nitrate_formation
priority_group = PRIORITY_FORMATION
- name = "Proto Nitrate Formation"
+ name = "Proto-Nitrate Formation"
id = "proto_nitrate_formation"
- desc = "Production of proto-nitrate from pluoxium and hydrogen under high temperatures."
+ desc = "Production of Proto-Nitrate from Pluoxium and Hydrogen under high temperatures."
-/datum/gas_reaction/proto_nitrate_formation/init_reqs()
+/datum/gas_reaction/standard/proto_nitrate_formation/init_reqs()
requirements = list(
/datum/gas/pluoxium = MINIMUM_MOLE_COUNT,
/datum/gas/hydrogen = MINIMUM_MOLE_COUNT,
@@ -988,22 +1018,23 @@
"MAX_TEMP" = PN_FORMATION_MAX_TEMPERATURE,
)
-/datum/gas_reaction/proto_nitrate_formation/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/proto_nitrate_formation/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles
+ var/pluoxium_moles = cached_moles[/datum/gas/pluoxium]
+ var/hydrogen_moles = cached_moles[/datum/gas/hydrogen]
var/temperature = air.temperature
- var/heat_efficency = min(temperature * 0.005, cached_gases[/datum/gas/pluoxium][MOLES] * INVERSE(0.2), cached_gases[/datum/gas/hydrogen][MOLES] * INVERSE(2))
- if (heat_efficency <= 0 || (cached_gases[/datum/gas/pluoxium][MOLES] - heat_efficency * 0.2 < 0 ) || (cached_gases[/datum/gas/hydrogen][MOLES] - heat_efficency * 2 < 0)) //Shouldn't produce gas from nothing.
+ var/heat_efficiency = min(temperature * 0.005, pluoxium_moles * INVERSE(0.2), hydrogen_moles * INVERSE(2))
+ if (heat_efficiency <= 0 || (pluoxium_moles - heat_efficiency * 0.2 < 0 ) || (hydrogen_moles - heat_efficiency * 2 < 0)) //Shouldn't produce gas from nothing.
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- ASSERT_GAS(/datum/gas/proto_nitrate, air)
- cached_gases[/datum/gas/hydrogen][MOLES] -= heat_efficency * 2
- cached_gases[/datum/gas/pluoxium][MOLES] -= heat_efficency * 0.2
- cached_gases[/datum/gas/proto_nitrate][MOLES] += heat_efficency * 2.2
+ cached_moles[/datum/gas/hydrogen] -= heat_efficiency * 2
+ cached_moles[/datum/gas/pluoxium] -= heat_efficiency * 0.2
+ cached_moles[/datum/gas/proto_nitrate] += heat_efficiency * 2.2
- SET_REACTION_RESULTS(heat_efficency * 2.2)
- var/energy_released = heat_efficency * PN_FORMATION_ENERGY
+ SET_REACTION_RESULTS(heat_efficiency * 2.2)
+ var/energy_released = heat_efficiency * PN_FORMATION_ENERGY
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max(((temperature * old_heat_capacity + energy_released) / new_heat_capacity), TCMB)
@@ -1015,27 +1046,29 @@
* Converts hydrogen into proto-nitrate.
* Endothermic.
*/
-/datum/gas_reaction/proto_nitrate_hydrogen_response
+/datum/gas_reaction/standard/proto_nitrate_hydrogen_response
priority_group = PRIORITY_PRE_FORMATION
- name = "Proto Nitrate Hydrogen Response"
+ name = "Proto-Nitrate Hydrogen Response"
id = "proto_nitrate_hydrogen_response"
- desc = "Conversion of hydrogen into proto nitrate."
+ desc = "Conversion of Hydrogen into Proto-Nitrate."
-/datum/gas_reaction/proto_nitrate_hydrogen_response/init_reqs()
+/datum/gas_reaction/standard/proto_nitrate_hydrogen_response/init_reqs()
requirements = list(
/datum/gas/proto_nitrate = MINIMUM_MOLE_COUNT,
/datum/gas/hydrogen = PN_HYDROGEN_CONVERSION_THRESHOLD,
)
-/datum/gas_reaction/proto_nitrate_hydrogen_response/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
- var/produced_amount = min(PN_HYDROGEN_CONVERSION_MAX_RATE, cached_gases[/datum/gas/hydrogen][MOLES], cached_gases[/datum/gas/proto_nitrate][MOLES])
- if (produced_amount <= 0 || cached_gases[/datum/gas/hydrogen][MOLES] - produced_amount < 0)
+/datum/gas_reaction/standard/proto_nitrate_hydrogen_response/react(datum/gas_mixture/air, datum/holder)
+ var/list/cached_moles = air.moles
+ var/proto_nitrate_moles = cached_moles[/datum/gas/proto_nitrate]
+ var/hydrogen_moles = cached_moles[/datum/gas/hydrogen]
+ var/produced_amount = min(PN_HYDROGEN_CONVERSION_MAX_RATE, hydrogen_moles, proto_nitrate_moles)
+ if (produced_amount <= 0 || hydrogen_moles - produced_amount < 0)
return NO_REACTION
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/hydrogen][MOLES] -= produced_amount
- cached_gases[/datum/gas/proto_nitrate][MOLES] += produced_amount * 0.5
+ cached_moles[/datum/gas/hydrogen] -= produced_amount
+ cached_moles[/datum/gas/proto_nitrate] += produced_amount * 0.5
SET_REACTION_RESULTS(produced_amount * 0.5)
var/energy_used = produced_amount * PN_HYDROGEN_CONVERSION_ENERGY
@@ -1051,13 +1084,13 @@
* Releases radiation.
* Exothermic.
*/
-/datum/gas_reaction/proto_nitrate_tritium_response
+/datum/gas_reaction/standard/proto_nitrate_tritium_response
priority_group = PRIORITY_PRE_FORMATION
- name = "Proto Nitrate Tritium Response"
+ name = "Proto-Nitrate Tritium Response"
id = "proto_nitrate_tritium_response"
- desc = "Conversion of tritium into hydrogen that consumes a small amount of proto-nitrate."
+ desc = "Conversion of Tritium into Hydrogen that consumes a small amount of Proto-Nitrate."
-/datum/gas_reaction/proto_nitrate_tritium_response/init_reqs()
+/datum/gas_reaction/standard/proto_nitrate_tritium_response/init_reqs()
requirements = list(
/datum/gas/proto_nitrate = MINIMUM_MOLE_COUNT,
/datum/gas/tritium = MINIMUM_MOLE_COUNT,
@@ -1065,18 +1098,20 @@
"MAX_TEMP" = PN_TRITIUM_CONVERSION_MAX_TEMP,
)
-/datum/gas_reaction/proto_nitrate_tritium_response/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/proto_nitrate_tritium_response/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
+ var/list/cached_moles = air.moles
+ var/proto_nitrate_moles = cached_moles[/datum/gas/proto_nitrate]
+ var/tritium_moles = cached_moles[/datum/gas/tritium]
var/temperature = air.temperature
- var/produced_amount = min(air.temperature / 34 * (cached_gases[/datum/gas/tritium][MOLES] * cached_gases[/datum/gas/proto_nitrate][MOLES]) / (cached_gases[/datum/gas/tritium][MOLES] + 10 * cached_gases[/datum/gas/proto_nitrate][MOLES]), cached_gases[/datum/gas/tritium][MOLES], cached_gases[/datum/gas/proto_nitrate][MOLES] * INVERSE(0.01))
- if(cached_gases[/datum/gas/tritium][MOLES] - produced_amount < 0 || cached_gases[/datum/gas/proto_nitrate][MOLES] - produced_amount * 0.01 < 0)
- return NO_REACTION
+ var/produced_amount = min(temperature / 34 * (tritium_moles * proto_nitrate_moles) / (tritium_moles + 10 * proto_nitrate_moles), tritium_moles, proto_nitrate_moles * INVERSE(0.01))
+ if(tritium_moles - produced_amount < 0 || proto_nitrate_moles - produced_amount * 0.01 < 0)
+ return
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/proto_nitrate][MOLES] -= produced_amount * 0.01
- cached_gases[/datum/gas/tritium][MOLES] -= produced_amount
- ASSERT_GAS(/datum/gas/hydrogen, air)
- cached_gases[/datum/gas/hydrogen][MOLES] += produced_amount
+ cached_moles[/datum/gas/proto_nitrate] -= produced_amount * 0.01
+ cached_moles[/datum/gas/tritium] -= produced_amount
+ cached_moles[/datum/gas/hydrogen] += produced_amount
SET_REACTION_RESULTS(produced_amount)
var/turf/open/location
@@ -1100,13 +1135,13 @@
*
* Breaks BZ down into nitrogen, helium, and plasma in the presence of proto-nitrate.
*/
-/datum/gas_reaction/proto_nitrate_bz_response
+/datum/gas_reaction/standard/proto_nitrate_bz_response
priority_group = PRIORITY_PRE_FORMATION
- name = "Proto Nitrate BZ Response"
+ name = "Proto-Nitrate BZ Response"
id = "proto_nitrate_bz_response"
- desc = "Breakdown of BZ into nitrogen, helium, and plasma by proto-nitrate under low temperatures."
+ desc = "Breakdown of BZ into Nitrogen, Helium, and Plasma by Proto-Nitrate under low temperatures."
-/datum/gas_reaction/proto_nitrate_bz_response/init_reqs()
+/datum/gas_reaction/standard/proto_nitrate_bz_response/init_reqs()
requirements = list(
/datum/gas/proto_nitrate = MINIMUM_MOLE_COUNT,
/datum/gas/bz = MINIMUM_MOLE_COUNT,
@@ -1114,21 +1149,21 @@
"MAX_TEMP" = PN_BZASE_MAX_TEMP,
)
-/datum/gas_reaction/proto_nitrate_bz_response/react(datum/gas_mixture/air, datum/holder)
- var/list/cached_gases = air.gases
+/datum/gas_reaction/standard/proto_nitrate_bz_response/react(datum/gas_mixture/air, datum/holder)
+ . = NO_REACTION
+ var/list/cached_moles = air.moles
+ var/proto_nitrate_moles = cached_moles[/datum/gas/proto_nitrate]
+ var/bz_moles = cached_moles[/datum/gas/bz]
var/temperature = air.temperature
- var/consumed_amount = min(air.temperature / 2240 * cached_gases[/datum/gas/bz][MOLES] * cached_gases[/datum/gas/proto_nitrate][MOLES] / (cached_gases[/datum/gas/bz][MOLES] + cached_gases[/datum/gas/proto_nitrate][MOLES]), cached_gases[/datum/gas/bz][MOLES], cached_gases[/datum/gas/proto_nitrate][MOLES])
- if (consumed_amount <= 0 || cached_gases[/datum/gas/bz][MOLES] - consumed_amount < 0)
- return NO_REACTION
+ var/consumed_amount = min(temperature / 2240 * bz_moles * proto_nitrate_moles / (bz_moles + proto_nitrate_moles), bz_moles, proto_nitrate_moles)
+ if (consumed_amount <= 0 || bz_moles - consumed_amount < 0)
+ return
var/old_heat_capacity = air.heat_capacity()
- cached_gases[/datum/gas/bz][MOLES] -= consumed_amount
- ASSERT_GAS(/datum/gas/nitrogen, air)
- cached_gases[/datum/gas/nitrogen][MOLES] += consumed_amount * 0.4
- ASSERT_GAS(/datum/gas/helium, air)
- cached_gases[/datum/gas/helium][MOLES] += consumed_amount * 1.6
- ASSERT_GAS(/datum/gas/plasma, air)
- cached_gases[/datum/gas/plasma][MOLES] += consumed_amount * 0.8
+ cached_moles[/datum/gas/bz] -= consumed_amount
+ cached_moles[/datum/gas/nitrogen] += consumed_amount * 0.4
+ cached_moles[/datum/gas/helium] += consumed_amount * 1.6
+ cached_moles[/datum/gas/plasma] += consumed_amount * 0.8
SET_REACTION_RESULTS(consumed_amount)
var/turf/open/location
@@ -1149,6 +1184,6 @@
var/new_heat_capacity = air.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air.temperature = max((temperature * old_heat_capacity + energy_released) / new_heat_capacity, TCMB)
- return REACTING
+ . |= REACTING
#undef SET_REACTION_RESULTS
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index 40883eecad6e..b8bff006ff86 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -100,13 +100,14 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
tlv_collection = list()
tlv_collection["pressure"] = new /datum/tlv/pressure
tlv_collection["temperature"] = new /datum/tlv/temperature
- var/list/meta_info = GLOB.meta_gas_info // shorthand
- for(var/gas_path in meta_info)
+
+ var/list/cached_gas_info = GLOB.meta_gas_info
+ for(var/datum/gas/gas_path as anything in cached_gas_info[META_GAS_ID])
if(ispath(gas_path, /datum/gas/oxygen))
tlv_collection[gas_path] = new /datum/tlv/oxygen
else if(ispath(gas_path, /datum/gas/carbon_dioxide))
tlv_collection[gas_path] = new /datum/tlv/carbon_dioxide
- else if(meta_info[gas_path][META_GAS_DANGER])
+ else if(cached_gas_info[META_GAS_DANGER][gas_path])
tlv_collection[gas_path] = new /datum/tlv/dangerous
else
tlv_collection[gas_path] = new /datum/tlv/no_checks
@@ -262,11 +263,10 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
"danger" = tlv_collection["temperature"].check_value(temp),
))
if(total_moles)
- for(var/gas_path in environment.gases)
- var/moles = environment.gases[gas_path][MOLES]
+ for(var/gas_path, moles in environment.moles)
var/portion = moles / total_moles
data["envData"] += list(list(
- "name" = GLOB.meta_gas_info[gas_path][META_GAS_NAME],
+ "name" = GLOB.meta_gas_info[META_GAS_NAME][gas_path],
"value" = "[round(moles, 0.01)] moles / [round(100 * portion, 0.01)] % / [round(portion * pressure, 0.01)] kPa",
"danger" = tlv_collection[gas_path].check_value(portion * pressure),
))
@@ -282,7 +282,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
singular_tlv["name"] = "Temperature"
singular_tlv["unit"] = "K"
else
- singular_tlv["name"] = GLOB.meta_gas_info[threshold][META_GAS_NAME]
+ singular_tlv["name"] = GLOB.meta_gas_info[META_GAS_NAME][threshold]
singular_tlv["unit"] = "kPa"
singular_tlv["id"] = threshold
singular_tlv["warning_min"] = tlv.warning_min
@@ -312,9 +312,9 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
data["scrubbers"] = list()
for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/scrubber as anything in my_area.air_scrubbers)
var/list/filter_types = list()
- for (var/path in GLOB.meta_gas_info)
- var/list/gas = GLOB.meta_gas_info[path]
- filter_types += list(list("gas_id" = gas[META_GAS_ID], "gas_name" = gas[META_GAS_NAME], "enabled" = (path in scrubber.filter_types)))
+ var/cached_gas_info = GLOB.meta_gas_info
+ for (var/path in cached_gas_info[META_GAS_ID])
+ filter_types += list(list("gas_id" = cached_gas_info[META_GAS_ID][path], "gas_name" = cached_gas_info[META_GAS_NAME][path], "enabled" = (path in scrubber.filter_types)))
data["scrubbers"] += list(list(
"refID" = REF(scrubber),
"long_name" = sanitize(scrubber.name),
@@ -561,8 +561,9 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
danger_level = max(danger_level, tlv_collection["pressure"].check_value(pressure), tlv_collection["temperature"].check_value(temp))
if(total_moles)
- for(var/gas_path in GLOB.meta_gas_info)
- var/moles = environment.gases[gas_path]?[MOLES] || 0
+ var/list/cached_gas_info = GLOB.meta_gas_info
+ for(var/datum/gas/gas_path as anything in cached_gas_info[META_GAS_ID])
+ var/moles = environment.moles[gas_path] || 0
danger_level = max(danger_level, tlv_collection[gas_path].check_value(pressure * moles / total_moles))
if(danger_level)
@@ -674,7 +675,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/airalarm, 27)
tlv_collection["temperature"] = new /datum/tlv/no_checks
tlv_collection["pressure"] = new /datum/tlv/no_checks
- for(var/gas_path in GLOB.meta_gas_info)
+ for(var/gas_path in GLOB.meta_gas_info[META_GAS_ID])
tlv_collection[gas_path] = new /datum/tlv/no_checks
///Used for air alarm link helper, which connects air alarm to a sensor with corresponding chamber_id
diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm
index 04c5f0c4bfed..286e416b501d 100644
--- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm
@@ -164,8 +164,9 @@
"Temperature" = "temperature"
)
- for(var/gas_id in GLOB.meta_gas_info)
- component_options[GLOB.meta_gas_info[gas_id][META_GAS_NAME]] = gas_id2path(gas_id)
+ var/cached_gas_info = GLOB.meta_gas_info
+ for(var/gas_id in cached_gas_info[META_GAS_ID])
+ component_options[cached_gas_info[META_GAS_NAME][gas_id]] = gas_id2path(gas_id)
air_alarm_options = add_option_port("Air Alarm Options", component_options)
options_map = component_options
@@ -230,7 +231,7 @@
pressure.set_output(round(environment.return_pressure()))
temperature.set_output(round(environment.temperature))
if(ispath(options_map[current_option]))
- gas_amount.set_output(round(environment.gases[options_map[current_option]][MOLES]))
+ gas_amount.set_output(round(environment.moles[options_map[current_option]]))
update_received.set_output(COMPONENT_SIGNAL)
@@ -351,8 +352,9 @@
. = ..()
var/static/list/meta_data = list()
if(length(meta_data) == 0)
- for(var/typepath as anything in GLOB.meta_gas_info)
- meta_data += GLOB.meta_gas_info[typepath][META_GAS_ID]
+ var/cached_gas_info = GLOB.meta_gas_info
+ for(var/typepath in cached_gas_info[META_GAS_ID])
+ meta_data += cached_gas_info[META_GAS_ID][typepath]
. += create_table_notices(meta_data, column_name = "Gas", column_name_plural = "Gases")
/obj/item/circuit_component/air_alarm_scrubbers/proc/set_gas_to_filter(datum/port/input/port)
diff --git a/code/modules/atmospherics/machinery/bluespace_vendor.dm b/code/modules/atmospherics/machinery/bluespace_vendor.dm
deleted file mode 100644
index b301eb891293..000000000000
--- a/code/modules/atmospherics/machinery/bluespace_vendor.dm
+++ /dev/null
@@ -1,285 +0,0 @@
-/obj/item/wallframe/bluespace_vendor_mount
- name = "bluespace vendor wall mount"
- desc = "Used for placing bluespace vendors."
- icon = 'icons/obj/machines/atmospherics/bluespace_gas_selling.dmi'
- icon_state = "bluespace_vendor_open"
- result_path = /obj/machinery/bluespace_vendor/built
- pixel_shift = 30
-
-///Defines for the mode of the vendor
-#define BS_MODE_OFF 1
-#define BS_MODE_IDLE 2
-#define BS_MODE_PUMPING 3
-#define BS_MODE_OPEN 4
-
-/obj/machinery/bluespace_vendor
- icon = 'icons/obj/machines/atmospherics/bluespace_gas_selling.dmi'
- icon_state = "bluespace_vendor_off"
- base_icon_state = "bluespace_vendor"
- name = "Bluespace Gas Vendor"
- desc = "Sells gas tanks with custom mixes for all the family!"
-
- max_integrity = 300
- armor_type = /datum/armor/machinery_bluespace_vendor
- layer = OBJ_LAYER
-
- ///The bluespace sender that this vendor is connected to
- var/obj/machinery/atmospherics/components/unary/bluespace_sender/connected_machine
- ///Amount of usable tanks inside the machine
- var/empty_tanks = 10
- ///Reference to the current in use tank to be filled
- var/obj/item/tank/internals/generic/internal_tank
- ///Path of the gas selected from the UI to be pumped inside the tanks
- var/selected_gas
- ///Is the vendor trying to move gases from the network to the tanks?
- var/pumping = FALSE
- ///Has the user prepared a tank to be filled with gases?
- var/inserted_tank = FALSE
- ///Amount of the tank already filled with gas (from 0 to 100)
- var/tank_filling_amount = 0
- ///Base price of the tank
- var/tank_cost = 10
- ///Stores the current price of the gases inside the tank
- var/gas_price = 0
- ///Helper for mappers, will automatically connect to the sender (ensure to only place one sender per map)
- var/map_spawned = TRUE
- ///Current operating mode of the vendor
- var/mode = BS_MODE_OFF
-
-//The one that the players make
-/obj/machinery/bluespace_vendor/built
- map_spawned = FALSE
- mode = BS_MODE_OPEN
-
-MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
-
-/datum/armor/machinery_bluespace_vendor
- energy = 100
- fire = 80
- acid = 30
-
-/obj/machinery/bluespace_vendor/Initialize(mapload, ndir, nbuild)
- . = ..()
-
- if(nbuild)
- set_panel_open(TRUE)
-
- update_appearance()
-
- AddComponent(/datum/component/payment, tank_cost, SSeconomy.get_dep_account(ACCOUNT_ENG), PAYMENT_ANGRY)
- find_and_hang_on_wall( FALSE)
-
-/obj/machinery/bluespace_vendor/post_machine_initialize()
- . = ..()
- if(!map_spawned)
- return
- for(var/obj/machinery/atmospherics/components/unary/bluespace_sender/sender as anything in GLOB.bluespace_senders)
- register_machine(sender)
-
-/obj/machinery/bluespace_vendor/Destroy()
- unregister_machine()
- return ..()
-
-/obj/machinery/bluespace_vendor/update_icon_state()
- switch(mode)
- if(BS_MODE_OFF)
- icon_state = "[base_icon_state]_off"
- if(BS_MODE_IDLE)
- icon_state = "[base_icon_state]_idle"
- if(BS_MODE_PUMPING)
- icon_state = "[base_icon_state]_pumping"
- if(BS_MODE_OPEN)
- icon_state = "[base_icon_state]_open"
- return ..()
-
-/obj/machinery/bluespace_vendor/Exited(atom/movable/gone, direction)
- if(gone == internal_tank)
- internal_tank = null
- return ..()
-
-/obj/machinery/bluespace_vendor/process()
- if(mode == BS_MODE_OPEN)
- return
- if(!selected_gas)
- return
- var/gas_path = gas_id2path(selected_gas)
-
- if(!connected_machine.bluespace_network.gases[gas_path])
- pumping = FALSE
- selected_gas = null
- mode = BS_MODE_IDLE
- update_appearance()
- return
-
- connected_machine.bluespace_network.pump_gas_to(internal_tank.return_air(), (tank_filling_amount * 0.01) * 10 * ONE_ATMOSPHERE, gas_path)
-
-/obj/machinery/bluespace_vendor/multitool_act(mob/living/user, obj/item/multitool/multitool)
- if(!istype(multitool))
- return
- if(!istype(multitool.buffer, /obj/machinery/atmospherics/components/unary/bluespace_sender))
- to_chat(user, span_notice("Wrong machine type in [multitool] buffer..."))
- return
- if(connected_machine)
- to_chat(user, span_notice("Changing [src] bluespace network..."))
- if(!do_after(user, 0.2 SECONDS, src))
- return
- playsound(get_turf(user), 'sound/machines/click.ogg', 10, TRUE)
- register_machine(multitool.buffer)
- to_chat(user, span_notice("You link [src] to the console in [multitool]'s buffer."))
- return TRUE
-
-/obj/machinery/bluespace_vendor/attackby(obj/item/item, mob/living/user)
- if(!pumping && default_deconstruction_screwdriver(user, "[base_icon_state]_open", "[base_icon_state]_off", item))
- check_mode()
- return
- if(default_deconstruction_crowbar(item, FALSE, custom_deconstruct = TRUE))
- new/obj/item/wallframe/bluespace_vendor_mount(user.loc)
- qdel(src)
- return
-
- if(istype(item, /obj/item/stack/sheet/iron))
- var/obj/item/stack/sheet/iron/iron = item
- if (iron.use(1))
- empty_tanks++
- return TRUE
- return ..()
-
-/obj/machinery/bluespace_vendor/examine(mob/user)
- . = ..()
- if(empty_tanks > 1)
- . += span_notice("There are currently [empty_tanks] empty tanks available, more can be made by inserting iron sheets in the machine.")
- else if(empty_tanks == 1)
- . += span_notice("There is only one empty tank available, please refill the machine by using iron sheets.")
- else
- . += span_notice("There is no available tank, please refill the machine by using iron sheets.")
-
-///Check what is the current operating mode
-/obj/machinery/bluespace_vendor/proc/check_mode()
- if(panel_open)
- mode = BS_MODE_OPEN
- else if(connected_machine)
- mode = BS_MODE_IDLE
- else
- mode = BS_MODE_OFF
- update_appearance()
-
-///Register the sender as the connected_machine
-/obj/machinery/bluespace_vendor/proc/register_machine(machine)
- connected_machine = machine
- LAZYADD(connected_machine.vendors, src)
- RegisterSignal(connected_machine, COMSIG_QDELETING, PROC_REF(unregister_machine))
- mode = BS_MODE_IDLE
- update_appearance()
-
-///Unregister the connected_machine (either when qdel this or the sender)
-/obj/machinery/bluespace_vendor/proc/unregister_machine()
- SIGNAL_HANDLER
- if(connected_machine)
- UnregisterSignal(connected_machine, COMSIG_QDELETING)
- LAZYREMOVE(connected_machine.vendors, src)
- connected_machine = null
- mode = BS_MODE_OFF
- update_appearance()
-
-///Check the price of the current tank, if the user doesn't have the money the gas will be merged back into the network
-/obj/machinery/bluespace_vendor/proc/check_price(mob/user)
- var/temp_price = 0
- var/datum/gas_mixture/working_mix = internal_tank.return_air()
- var/list/gases = working_mix.gases
- for(var/gas_id in gases)
- temp_price += gases[gas_id][MOLES] * connected_machine.base_prices[gas_id]
- gas_price = temp_price
-
- if(attempt_charge(src, user, gas_price) & COMPONENT_OBJ_CANCEL_CHARGE)
- var/datum/gas_mixture/remove = working_mix.remove_ratio(1)
- connected_machine.bluespace_network.merge(remove)
- return
- connected_machine.credits_gained += gas_price + tank_cost
-
- if(internal_tank && Adjacent(user)) //proper capitalysm take money before goods
- inserted_tank = FALSE
- user.put_in_hands(internal_tank)
-
-/obj/machinery/bluespace_vendor/ui_interact(mob/user, datum/tgui/ui)
- if(!connected_machine || mode == BS_MODE_OPEN)
- return
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "BluespaceVendor", name)
- ui.open()
-
-/obj/machinery/bluespace_vendor/ui_data(mob/user)
- var/list/data = list()
- var/list/bluespace_gasdata = list()
- if(connected_machine.bluespace_network.total_moles())
- for(var/gas_id in connected_machine.bluespace_network.gases)
- bluespace_gasdata.Add(list(list(
- "name" = connected_machine.bluespace_network.gases[gas_id][GAS_META][META_GAS_NAME],
- "id" = connected_machine.bluespace_network.gases[gas_id][GAS_META][META_GAS_ID],
- "amount" = round(connected_machine.bluespace_network.gases[gas_id][MOLES], 0.01),
- "price" = connected_machine.base_prices[gas_id],
- )))
- else
- for(var/gas_id in connected_machine.bluespace_network.gases)
- bluespace_gasdata.Add(list(list(
- "name" = connected_machine.bluespace_network.gases[gas_id][GAS_META][META_GAS_NAME],
- "id" = "",
- "amount" = 0,
- "price" = 0,
- )))
- data["bluespace_network_gases"] = bluespace_gasdata
- data["pumping"] = pumping
- data["tank_filling_amount"] = tank_filling_amount
- data["selected_gas"] = selected_gas
- data["tank_amount"] = empty_tanks
- data["inserted_tank"] = inserted_tank
- var/total_tank_pressure
- if(internal_tank)
- var/datum/gas_mixture/working_mix = internal_tank.return_air()
- total_tank_pressure = working_mix.return_pressure()
- else
- total_tank_pressure = 0
- data["tank_full"] = total_tank_pressure
- return data
-
-/obj/machinery/bluespace_vendor/ui_act(action, params)
- . = ..()
- if(.)
- return
-
- if(mode == BS_MODE_OPEN)
- return
-
- switch(action)
- if("start_pumping")
- if(inserted_tank && !pumping)
- pumping = TRUE
- selected_gas = params["gas_id"]
- mode = BS_MODE_PUMPING
- update_appearance()
- . = TRUE
- if("stop_pumping")
- if(inserted_tank && pumping)
- pumping = FALSE
- selected_gas = null
- mode = BS_MODE_IDLE
- update_appearance()
- . = TRUE
- if("pumping_rate")
- tank_filling_amount = clamp(params["rate"], 0, 100)
- . = TRUE
- if("tank_prepare")
- if(empty_tanks && !inserted_tank)
- inserted_tank = TRUE
- internal_tank = new(src)
- empty_tanks = max(empty_tanks - 1, 0)
- . = TRUE
- if("tank_expel")
- if(inserted_tank && !pumping)
- check_price(usr)
- . = TRUE
-
-#undef BS_MODE_OFF
-#undef BS_MODE_IDLE
-#undef BS_MODE_PUMPING
-#undef BS_MODE_OPEN
diff --git a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm
index c4bcfcdcc88c..c8ae16b971e0 100644
--- a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm
+++ b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm
@@ -7,8 +7,8 @@
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN
icon = 'icons/obj/pipes_n_cables/atmos.dmi'
icon_state = "electrolyzer-off"
- name = "space electrolyzer"
- desc = "Thanks to the fast and dynamic response of our electrolyzers, on-site hydrogen production is guaranteed. Warranty void if used by clowns"
+ name = "electrolyzer"
+ desc = "A portable electrolyzer, allowing for on-site production of Hydrogen. Warranty void if used by clowns."
max_integrity = 250
armor_type = /datum/armor/machinery_electrolyzer
circuit = /obj/item/circuitboard/machine/electrolyzer
@@ -71,9 +71,9 @@
. += "The charge meter reads [cell ? round(cell.percent(), 1) : 0]%."
else
. += "There is no power cell installed."
- if(in_range(user, src) || isobserver(user))
+ if(in_range(user, src) && !isobserver(user))
. += span_notice("Alt-click to toggle [on ? "off" : "on"].")
- . += span_notice("Anchor to drain power from APC instead of cell")
+ . += span_notice("Anchor it to drain power from the area's APC instead its internal power cell.")
. += span_notice("It will drain power from the [anchored ? "area's APC" : "internal power cell"].")
@@ -131,7 +131,7 @@
/obj/machinery/electrolyzer/proc/call_reactions(datum/gas_mixture/env)
for(var/reaction in GLOB.electrolyzer_reactions)
- var/datum/electrolyzer_reaction/current_reaction = GLOB.electrolyzer_reactions[reaction]
+ var/datum/gas_reaction/electrolyzer/current_reaction = GLOB.electrolyzer_reactions[reaction]
if(!current_reaction.reaction_check(env))
continue
diff --git a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer_reactions.dm b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer_reactions.dm
index c393436d5a77..70d57dffbec0 100644
--- a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer_reactions.dm
+++ b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer_reactions.dm
@@ -5,114 +5,126 @@ GLOBAL_LIST_INIT(electrolyzer_reactions, electrolyzer_reactions_list())
*/
/proc/electrolyzer_reactions_list()
var/list/built_reaction_list = list()
- for(var/reaction_path in subtypesof(/datum/electrolyzer_reaction))
- var/datum/electrolyzer_reaction/reaction = new reaction_path()
+ for(var/reaction_path in subtypesof(/datum/gas_reaction/electrolyzer))
+ var/datum/gas_reaction/electrolyzer/reaction = new reaction_path()
built_reaction_list[reaction.id] = reaction
return built_reaction_list
-/datum/electrolyzer_reaction
- var/list/requirements
- var/name = "reaction"
- var/id = "r"
- var/desc = ""
- var/list/factor
+/datum/gas_reaction/electrolyzer
+ abstract_type = /datum/gas_reaction/electrolyzer
-/datum/electrolyzer_reaction/proc/react(turf/location, datum/gas_mixture/air_mixture, working_power)
+/datum/gas_reaction/electrolyzer/New()
+ . = ..()
+ factor ||= list()
+ factor["Location"] ||= "Can only happen on tiles with an active Electrolyzer."
+
+/**
+ * Electrolyzer reaction.
+ * Args:
+ * * air_mixture: The gas_mixture receiving the electrolysis.
+ * * working_power: How much energy to put into the electrolysis, in electrolyzer units. A value of 1 is what a tier 1 electrolyzer would put in.
+ * * electrolyzer_args: Additional arguments for alternative methods of electrolysis.
+ */
+/datum/gas_reaction/electrolyzer/proc/react(turf/location, datum/gas_mixture/air_mixture, working_power)
return
-/datum/electrolyzer_reaction/proc/reaction_check(datum/gas_mixture/air_mixture)
+/**
+ * Checks whether the requirements are met for a reaction.
+ * Args:
+ * * air_mixture: The air mixture to check the requirements for.
+ * * electrolyzer_args: Additional arguments for alternative methods of electrolysis.
+ */
+/datum/gas_reaction/electrolyzer/proc/reaction_check(datum/gas_mixture/air_mixture)
var/temp = air_mixture.temperature
- var/list/cached_gases = air_mixture.gases
+ var/list/cached_moles = air_mixture.moles
if((requirements["MIN_TEMP"] && temp < requirements["MIN_TEMP"]) || (requirements["MAX_TEMP"] && temp > requirements["MAX_TEMP"]))
return FALSE
for(var/id in requirements)
if (id == "MIN_TEMP" || id == "MAX_TEMP")
continue
- if(!cached_gases[id] || cached_gases[id][MOLES] < requirements[id])
+ if(cached_moles[id] < requirements[id])
return FALSE
return TRUE
-/datum/electrolyzer_reaction/h2o_conversion
+/datum/gas_reaction/electrolyzer/h2o_conversion
name = "H2O Conversion"
id = "h2o_conversion"
- desc = "Conversion of H2o into O2 and H2"
+ desc = "Conversion of H2O into H2 and O2."
requirements = list(
/datum/gas/water_vapor = MINIMUM_MOLE_COUNT
)
factor = list(
- /datum/gas/water_vapor = "2 moles of H2O get consumed",
- /datum/gas/oxygen = "1 mole of O2 gets produced",
- /datum/gas/hydrogen = "2 moles of H2 get produced",
- "Location" = "Can only happen on turfs with an active Electrolyzer.",
+ /datum/gas/water_vapor = "2 moles of H2O is consumed.",
+ /datum/gas/oxygen = "1 mole of O2 is produced.",
+ /datum/gas/hydrogen = "2 moles of H2 is produced.",
)
-/datum/electrolyzer_reaction/h2o_conversion/react(turf/location, datum/gas_mixture/air_mixture, working_power)
+/datum/gas_reaction/electrolyzer/h2o_conversion/react(turf/location, datum/gas_mixture/air_mixture, working_power)
var/old_heat_capacity = air_mixture.heat_capacity()
- air_mixture.assert_gases(/datum/gas/water_vapor, /datum/gas/oxygen, /datum/gas/hydrogen)
- var/proportion = min(air_mixture.gases[/datum/gas/water_vapor][MOLES] * INVERSE(2), (2.5 * (working_power ** 2)))
- air_mixture.gases[/datum/gas/water_vapor][MOLES] -= proportion * 2
- air_mixture.gases[/datum/gas/oxygen][MOLES] += proportion
- air_mixture.gases[/datum/gas/hydrogen][MOLES] += proportion * 2
+
+ var/proportion = min(air_mixture.moles[/datum/gas/water_vapor] * INVERSE(2), (2.5 * (working_power ** 2)))
+ air_mixture.adjust_gas(/datum/gas/water_vapor, -proportion * 2)
+ air_mixture.adjust_gas(/datum/gas/oxygen, proportion)
+ air_mixture.adjust_gas(/datum/gas/hydrogen, proportion * 2)
var/new_heat_capacity = air_mixture.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air_mixture.temperature = max(air_mixture.temperature * old_heat_capacity / new_heat_capacity, TCMB)
-/datum/electrolyzer_reaction/nob_conversion
- name = "Hypernob conversion"
+/datum/gas_reaction/electrolyzer/nob_conversion
+ name = "Hyper-Noblium Conversion"
id = "nob_conversion"
- desc = "Conversion of Hypernoblium into Antinoblium"
+ desc = "Conversion of Hyper-Noblium into Anti-Noblium."
requirements = list(
/datum/gas/hypernoblium = MINIMUM_MOLE_COUNT,
"MAX_TEMP" = 150
)
factor = list(
- /datum/gas/hypernoblium = "1 mole of Hypernoblium gets consumed",
- /datum/gas/antinoblium = "0.5 moles of Antinoblium get produced",
+ /datum/gas/hypernoblium = "1 mole of Hyper-Noblium is consumed.",
+ /datum/gas/antinoblium = "1 mole of Anti-Noblium is produced.",
"Temperature" = "Can only occur under 150 kelvin.",
"Location" = "Can only happen on turfs with an active Electrolyzer.",
)
-/datum/electrolyzer_reaction/nob_conversion/react(turf/location, datum/gas_mixture/air_mixture, working_power)
+/datum/gas_reaction/electrolyzer/nob_conversion/react(turf/location, datum/gas_mixture/air_mixture, working_power)
var/old_heat_capacity = air_mixture.heat_capacity()
air_mixture.assert_gases(/datum/gas/hypernoblium, /datum/gas/antinoblium)
- var/proportion = min(air_mixture.gases[/datum/gas/hypernoblium][MOLES], (1.5 * (working_power ** 2)))
- air_mixture.gases[/datum/gas/hypernoblium][MOLES] -= proportion
- air_mixture.gases[/datum/gas/antinoblium][MOLES] += proportion * 0.5
+ var/proportion = min(air_mixture.moles[/datum/gas/hypernoblium], (1.5 * (working_power ** 2)))
+
+ air_mixture.convert_gas(/datum/gas/hypernoblium, /datum/gas/antinoblium, proportion)
+
var/new_heat_capacity = air_mixture.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
air_mixture.temperature = max(air_mixture.temperature * old_heat_capacity / new_heat_capacity, TCMB)
-/datum/electrolyzer_reaction/halon_generation
- name = "Halon generation"
+/datum/gas_reaction/electrolyzer/halon_generation
+ name = "Halon Generation"
id = "halon_generation"
- desc = "Production of halon from CO2 and N2O"
+ desc = "Production of halon from the electrolysis of BZ."
requirements = list(
- /datum/gas/carbon_dioxide = MINIMUM_MOLE_COUNT,
- /datum/gas/nitrous_oxide = MINIMUM_MOLE_COUNT,
- "MAX_TEMP" = 230
+ /datum/gas/bz = MINIMUM_MOLE_COUNT,
)
factor = list(
- /datum/gas/carbon_dioxide = "2 moles of CO2 get consumed",
- /datum/gas/nitrous_oxide = "1 mole of N2O gets consumed",
- /datum/gas/halon = "1 mole of Halon gets produced",
- "Energy" = "300 joules of energy is released per mole",
- "Temperature" = "Can only occur under 230 kelvin.",
- "Location" = "Can only happen on turfs with an active Electrolyzer.",
+ /datum/gas/bz = "All moles of BZ are consumed.",
+ /datum/gas/oxygen = "0.2 moles of oxygen is produced per mole of BZ consumed.",
+ /datum/gas/halon = "2 moles of Halon is produced per mole of BZ consumed.",
+ "Energy" = "91.2321 kJ of thermal energy is released per mole of BZ consumed.",
+ "Temperature" = "Reaction efficiency is proportional to temperature.",
)
-/datum/electrolyzer_reaction/halon_generation/react(turf/location, datum/gas_mixture/air_mixture, working_power)
-
+/datum/gas_reaction/electrolyzer/halon_generation/react(datum/gas_mixture/air_mixture, working_power, list/electrolyzer_args = list())
var/old_heat_capacity = air_mixture.heat_capacity()
- air_mixture.assert_gases(/datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/halon)
- var/pressure = air_mixture.return_pressure()
- var/reaction_efficency = min(1 / ((pressure / (0.5 * ONE_ATMOSPHERE)) * (max(air_mixture.gases[/datum/gas/carbon_dioxide][MOLES] / air_mixture.gases[/datum/gas/nitrous_oxide][MOLES], 1))), air_mixture.gases[/datum/gas/nitrous_oxide][MOLES], air_mixture.gases[/datum/gas/carbon_dioxide][MOLES] * INVERSE(2))
- air_mixture.gases[/datum/gas/carbon_dioxide][MOLES] -= reaction_efficency * 2
- air_mixture.gases[/datum/gas/nitrous_oxide][MOLES] -= reaction_efficency
- air_mixture.gases[/datum/gas/halon][MOLES] += reaction_efficency
+ air_mixture.assert_gases(/datum/gas/bz, /datum/gas/oxygen, /datum/gas/halon)
+ var/bz_moles = air_mixture.moles[/datum/gas/bz]
+ var/reaction_efficency = min(bz_moles * (1 - NUM_E ** (-0.5 * air_mixture.temperature * working_power / FIRE_MINIMUM_TEMPERATURE_TO_EXIST)), bz_moles)
+
+ air_mixture.adjust_gas(/datum/gas/bz, -reaction_efficency)
+ air_mixture.adjust_gas(/datum/gas/oxygen, reaction_efficency * 0.2)
+ air_mixture.adjust_gas(/datum/gas/halon, reaction_efficency * 2)
+
var/energy_used = reaction_efficency * HALON_FORMATION_ENERGY
var/new_heat_capacity = air_mixture.heat_capacity()
if(new_heat_capacity > MINIMUM_HEAT_CAPACITY)
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_core.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_core.dm
index 55111e731fc2..3c6a3e8bd4c8 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_core.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_core.dm
@@ -2,7 +2,7 @@
* This section contain the hfr core with all the variables and the Initialize() and Destroy() procs
*/
/obj/machinery/atmospherics/components/unary/hypertorus/core
- name = "HFR core"
+ name = "\improper HFR core"
desc = "This is the Hypertorus Fusion Reactor core, an advanced piece of technology to finely tune the reaction inside of the machine. It has I/O for cooling gases."
icon = 'icons/obj/machines/atmospherics/hypertorus.dmi'
icon_state = "core_off"
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_fuel_datums.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_fuel_datums.dm
index c7a9545a7d25..39d8ce76cd27 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_fuel_datums.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_fuel_datums.dm
@@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(hfr_fuels_list, hfr_fuels_create_list())
/datum/hfr_fuel/hypernob_hydrogen_fuel
id = "hypernob_hydrogen_fuel"
- name = "Hypernoblium + Hydrogen fuel"
+ name = "Hyper-Noblium + Hydrogen fuel"
negative_temperature_multiplier = 0.2
positive_temperature_multiplier = 2.2
energy_concentration_multiplier = 0.2
@@ -113,7 +113,7 @@ GLOBAL_LIST_INIT(hfr_fuels_list, hfr_fuels_create_list())
/datum/hfr_fuel/hypernob_trit_fuel
id = "hypernob_trit_fuel"
- name = "Hypernoblium + Tritium fuel"
+ name = "Hyper-Noblium + Tritium fuel"
negative_temperature_multiplier = 0.1
positive_temperature_multiplier = 2.5
energy_concentration_multiplier = 0.1
@@ -127,7 +127,7 @@ GLOBAL_LIST_INIT(hfr_fuels_list, hfr_fuels_create_list())
/datum/hfr_fuel/hypernob_antinob_fuel
id = "hypernob_antinob_fuel"
- name = "Hypernoblium + Antinoblium fuel"
+ name = "Hyper-Noblium + Anti-Noblium fuel"
negative_temperature_multiplier = 0.01
positive_temperature_multiplier = 3.5
energy_concentration_multiplier = 2
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm
index 43a1ccf44026..ca7bd39eecd5 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm
@@ -82,7 +82,7 @@
negative_temperature_multiplier = selected_fuel.negative_temperature_multiplier
for(var/gas_id in selected_fuel.requirements | selected_fuel.primary_products)
- var/amount = internal_fusion.gases[gas_id][MOLES]
+ var/amount = internal_fusion.moles[gas_id]
fuel_list[gas_id] = amount
scaled_fuel_list[gas_id] = max((amount - FUSION_MOLE_THRESHOLD) / scale_factor, 0)
@@ -90,8 +90,7 @@
var/list/moderator_list = list()
/// Scaled down moles of gases, no less than 0
var/list/scaled_moderator_list = list()
- for(var/gas_id in moderator_internal.gases)
- var/amount = moderator_internal.gases[gas_id][MOLES]
+ for(var/gas_id, amount in moderator_internal.moles)
moderator_list[gas_id] = amount
scaled_moderator_list[gas_id] = max((amount - FUSION_MOLE_THRESHOLD) / scale_factor, 0)
@@ -102,11 +101,9 @@
//The size of the phase space hypertorus
var/toroidal_size = (2 * PI) + TORADIANS(arctan((volume - TOROID_VOLUME_BREAKEVEN) / TOROID_VOLUME_BREAKEVEN))
//Calculation of the gas power, only for theoretical instability calculations
- var/gas_power = 0
- for (var/gas_id in internal_fusion.gases)
- gas_power += (internal_fusion.gases[gas_id][GAS_META][META_GAS_FUSION_POWER] * internal_fusion.gases[gas_id][MOLES])
- for (var/gas_id in moderator_internal.gases)
- gas_power += (moderator_internal.gases[gas_id][GAS_META][META_GAS_FUSION_POWER] * moderator_internal.gases[gas_id][MOLES] * 0.75)
+ var/list/cached_fusion_power = GAS_META[META_GAS_FUSION_POWER]
+ var/gas_power = values_dot(cached_fusion_power, internal_fusion.moles)
+ gas_power += 0.75 * values_dot(cached_fusion_power, moderator_internal.moles)
instability = MODULUS((gas_power * INSTABILITY_GAS_POWER_FACTOR)**2, toroidal_size) + (current_damper * 0.01) - iron_content * 0.05
//Effective reaction instability (determines if the energy is used/released)
@@ -246,35 +243,37 @@
var/scaled_production = production_amount * selected_fuel.gas_production_multiplier
for(var/gas_id in fuel.requirements)
- internal_fusion.gases[gas_id][MOLES] -= min(fuel_list[gas_id], fuel_consumption)
+ internal_fusion.adjust_gas(gas_id, -min(fuel_list[gas_id], fuel_consumption))
for(var/gas_id in fuel.primary_products)
- internal_fusion.gases[gas_id][MOLES] += fuel_consumption * 0.5
+ internal_fusion.adjust_gas(gas_id, fuel_consumption * 0.5)
// Each recipe provides a tier list of six output gases.
// Which gases are produced depend on what the fusion level is.
var/list/tier = fuel.secondary_products
switch(power_level)
if(1)
- moderator_internal.gases[tier[1]][MOLES] += scaled_production * 0.95
- moderator_internal.gases[tier[2]][MOLES] += scaled_production * 0.75
+ moderator_internal.adjust_gas(tier[1], scaled_production * 0.95)
+ moderator_internal.adjust_gas(tier[2], scaled_production * 0.75)
if(2)
- moderator_internal.gases[tier[1]][MOLES] += scaled_production * 1.65
- moderator_internal.gases[tier[2]][MOLES] += scaled_production
+ moderator_internal.adjust_gas(tier[1], scaled_production * 1.65)
+ moderator_internal.adjust_gas(tier[2], scaled_production)
if(moderator_list[/datum/gas/plasma] > 50)
- moderator_internal.gases[tier[3]][MOLES] += scaled_production * 1.15
+ moderator_internal.adjust_gas(tier[3], scaled_production * 1.15)
if(3)
- moderator_internal.gases[tier[2]][MOLES] += scaled_production * 0.5
- moderator_internal.gases[tier[3]][MOLES] += scaled_production * 0.45
+ moderator_internal.adjust_gas(tier[2], scaled_production * 0.5)
+ moderator_internal.adjust_gas(tier[3], scaled_production * 0.45)
if(4)
- moderator_internal.gases[tier[3]][MOLES] += scaled_production * 1.65
- moderator_internal.gases[tier[4]][MOLES] += scaled_production * 1.25
+ moderator_internal.adjust_gas(tier[3], scaled_production * 1.65)
+ moderator_internal.adjust_gas(tier[4], scaled_production * 1.25)
+ if(moderator_list[/datum/gas/plasma] > 50)
+ moderator_internal.adjust_gas(tier[5], scaled_production * 1.15)
if(5)
- moderator_internal.gases[tier[4]][MOLES] += scaled_production * 0.65
- moderator_internal.gases[tier[5]][MOLES] += scaled_production
- moderator_internal.gases[tier[6]][MOLES] += scaled_production * 0.75
+ moderator_internal.adjust_gas(tier[4], scaled_production * 0.65)
+ moderator_internal.adjust_gas(tier[5], scaled_production)
+ moderator_internal.adjust_gas(tier[6], scaled_production * 0.75)
if(6)
- moderator_internal.gases[tier[5]][MOLES] += scaled_production * 0.35
- moderator_internal.gases[tier[6]][MOLES] += scaled_production
+ moderator_internal.adjust_gas(tier[5], scaled_production * 0.35)
+ moderator_internal.adjust_gas(tier[6], scaled_production)
/**
* Perform common fusion actions:
@@ -287,94 +286,81 @@
switch(power_level)
if(1)
if(moderator_list[/datum/gas/plasma] > 100)
- internal_output.assert_gases(/datum/gas/nitrous_oxide)
- internal_output.gases[/datum/gas/nitrous_oxide] += scaled_production * 0.5
- moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 0.85)
+ internal_output.adjust_gas(/datum/gas/nitrous_oxide, scaled_production * 0.5)
+ moderator_internal.adjust_gas(/datum/gas/plasma, min(moderator_internal.moles[/datum/gas/plasma], scaled_production * 0.85))
if(moderator_list[/datum/gas/bz] > 150)
- internal_output.assert_gases(/datum/gas/halon)
- internal_output.gases[/datum/gas/halon][MOLES] += scaled_production * 0.55
- moderator_internal.gases[/datum/gas/bz][MOLES] -= min(moderator_internal.gases[/datum/gas/bz][MOLES], scaled_production * 0.95)
+ internal_output.adjust_gas(/datum/gas/halon, scaled_production * 0.55)
+ moderator_internal.adjust_gas(/datum/gas/bz, min(moderator_internal.moles[/datum/gas/bz], scaled_production * 0.95))
if(2)
if(moderator_list[/datum/gas/plasma] > 50)
- internal_output.assert_gases(/datum/gas/bz)
- internal_output.gases[/datum/gas/bz][MOLES] += scaled_production * 1.8
- moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 1.75)
+ internal_output.adjust_gas(/datum/gas/bz, scaled_production * 1.8)
+ moderator_internal.adjust_gas(/datum/gas/plasma, min(moderator_internal.moles[/datum/gas/plasma], scaled_production * 1.75))
if(moderator_list[/datum/gas/proto_nitrate] > 20)
radiation *= 1.55
heat_output *= 1.025
- internal_output.assert_gases(/datum/gas/nitrium)
- internal_output.gases[/datum/gas/nitrium][MOLES] += scaled_production * 1.05
- moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 1.35)
+ internal_output.adjust_gas(/datum/gas/nitrium, scaled_production * 1.05)
+ moderator_internal.adjust_gas(/datum/gas/proto_nitrate, min(moderator_internal.moles[/datum/gas/proto_nitrate], scaled_production * 1.35))
if(3, 4)
if(moderator_list[/datum/gas/plasma] > 10)
- internal_output.assert_gases(/datum/gas/freon, /datum/gas/nitrium)
- internal_output.gases[/datum/gas/freon][MOLES] += scaled_production * 0.15
- internal_output.gases[/datum/gas/nitrium][MOLES] += scaled_production * 1.05
- moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 0.45)
+ var/list/new_gases = list(/datum/gas/freon = scaled_production * 0.15, /datum/gas/nitrium = scaled_production * 1.05)
+ internal_output.adjust_multiple_gases(new_gases)
+ moderator_internal.adjust_gas(/datum/gas/plasma, min(moderator_internal.moles[/datum/gas/plasma], scaled_production * 0.45))
if(moderator_list[/datum/gas/freon] > 50)
heat_output *= 0.9
radiation *= 0.8
if(moderator_list[/datum/gas/proto_nitrate]> 15)
- internal_output.assert_gases(/datum/gas/nitrium, /datum/gas/halon)
- internal_output.gases[/datum/gas/nitrium][MOLES] += scaled_production * 1.25
- internal_output.gases[/datum/gas/halon][MOLES] += scaled_production * 1.15
- moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 1.55)
+ var/list/new_gases = list(/datum/gas/nitrium = scaled_production * 1.25, /datum/gas/halon = scaled_production * 1.15)
+ internal_output.adjust_multiple_gases(new_gases)
+ moderator_internal.adjust_gas(/datum/gas/proto_nitrate, min(moderator_internal.moles[/datum/gas/proto_nitrate], scaled_production * 1.55))
radiation *= 1.95
heat_output *= 1.25
if(moderator_list[/datum/gas/bz] > 100)
- internal_output.assert_gases(/datum/gas/healium, /datum/gas/proto_nitrate)
- internal_output.gases[/datum/gas/proto_nitrate][MOLES] += scaled_production * 1.5
- internal_output.gases[/datum/gas/healium][MOLES] += scaled_production * 1.5
+ var/list/new_gases = list(/datum/gas/healium = scaled_production * 1.5, /datum/gas/proto_nitrate = scaled_production * 1.5)
+ internal_output.adjust_multiple_gases(new_gases)
visible_hallucination_pulse(src, HALLUCINATION_HFR(heat_output), 100 SECONDS * power_level * seconds_per_tick)
if(5)
if(moderator_list[/datum/gas/plasma] > 15)
- internal_output.assert_gases(/datum/gas/freon)
- internal_output.gases[/datum/gas/freon][MOLES] += scaled_production *0.25
- moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 1.45)
+ internal_output.adjust_gas(/datum/gas/freon, scaled_production *0.25)
+ moderator_internal.adjust_gas(/datum/gas/plasma,min(moderator_internal.moles[/datum/gas/plasma], scaled_production * 1.45))
if(moderator_list[/datum/gas/freon] > 500)
heat_output *= 0.5
radiation *= 0.2
if(moderator_list[/datum/gas/proto_nitrate] > 50)
- internal_output.assert_gases(/datum/gas/nitrium, /datum/gas/pluoxium)
- internal_output.gases[/datum/gas/nitrium][MOLES] += scaled_production * 1.95
- internal_output.gases[/datum/gas/pluoxium][MOLES] += scaled_production
- moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 1.35)
+ var/list/new_gases = list(/datum/gas/nitrium = scaled_production * 1.95, /datum/gas/pluoxium = scaled_production)
+ internal_output.adjust_multiple_gases(new_gases)
+ moderator_internal.adjust_gas(/datum/gas/proto_nitrate, min(moderator_internal.moles[/datum/gas/proto_nitrate], scaled_production * 1.35))
radiation *= 1.95
heat_output *= 1.25
if(moderator_list[/datum/gas/bz] > 100)
- internal_output.assert_gases(/datum/gas/healium, /datum/gas/freon)
- internal_output.gases[/datum/gas/healium][MOLES] += scaled_production
+ var/list/new_gases = list(/datum/gas/healium = scaled_production, /datum/gas/freon = scaled_production * 1.15)
+ internal_output.adjust_multiple_gases(new_gases)
visible_hallucination_pulse(src, HALLUCINATION_HFR(heat_output), 100 SECONDS * power_level * seconds_per_tick)
- internal_output.gases[/datum/gas/freon][MOLES] += scaled_production * 1.15
if(moderator_list[/datum/gas/healium] > 100)
if(critical_threshold_proximity > 400)
critical_threshold_proximity = max(critical_threshold_proximity - (moderator_list[/datum/gas/healium] / 100 * seconds_per_tick ), 0)
- moderator_internal.gases[/datum/gas/healium][MOLES] -= min(moderator_internal.gases[/datum/gas/healium][MOLES], scaled_production * 20)
+ moderator_internal.adjust_gas(/datum/gas/healium, -min(moderator_internal.moles[/datum/gas/healium], scaled_production * 20))
if(moderator_internal.temperature < 1e7 || (moderator_list[/datum/gas/plasma] > 100 && moderator_list[/datum/gas/bz] > 50))
- internal_output.assert_gases(/datum/gas/antinoblium)
- internal_output.gases[/datum/gas/antinoblium][MOLES] += dirty_production_rate * 0.9 / 0.065 * seconds_per_tick
+ internal_output.adjust_gas(/datum/gas/antinoblium, dirty_production_rate * 0.9 / 0.065 * seconds_per_tick)
if(6)
internal_output.assert_gases(/datum/gas/antinoblium)
if(moderator_list[/datum/gas/plasma] > 30)
- internal_output.assert_gases(/datum/gas/bz)
- internal_output.gases[/datum/gas/bz][MOLES] += scaled_production * 1.15
- moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 1.45)
+ internal_output.adjust_gas(/datum/gas/bz, scaled_production * 1.15)
+ moderator_internal.adjust_gas(/datum/gas/plasma, -min(moderator_internal.moles[/datum/gas/plasma], scaled_production * 1.45))
if(moderator_list[/datum/gas/proto_nitrate])
- internal_output.assert_gases(/datum/gas/zauker, /datum/gas/nitrium)
- internal_output.gases[/datum/gas/zauker][MOLES] += scaled_production * 5.35
- internal_output.gases[/datum/gas/nitrium][MOLES] += scaled_production * 2.15
- moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 3.35)
+ var/list/new_gases = list(/datum/gas/zauker = scaled_production * 5.35, /datum/gas/nitrium = scaled_production * 2.15)
+ internal_output.adjust_multiple_gases(new_gases)
+ moderator_internal.adjust_gas(/datum/gas/proto_nitrate, -min(moderator_internal.moles[/datum/gas/proto_nitrate], scaled_production * 3.35))
radiation *= 2
heat_output *= 2.25
if(moderator_list[/datum/gas/bz])
visible_hallucination_pulse(src, HALLUCINATION_HFR(heat_output), 100 SECONDS * power_level * seconds_per_tick)
- internal_output.gases[/datum/gas/antinoblium][MOLES] += clamp(dirty_production_rate / 0.045, 0, 10) * seconds_per_tick
+ internal_output.adjust_gas(/datum/gas/antinoblium, clamp(dirty_production_rate / 0.045, 0, 10) * seconds_per_tick)
if(moderator_list[/datum/gas/healium] > 100)
if(critical_threshold_proximity > 400)
critical_threshold_proximity = max(critical_threshold_proximity - (moderator_list[/datum/gas/healium] / 100 * seconds_per_tick ), 0)
- moderator_internal.gases[/datum/gas/healium][MOLES] -= min(moderator_internal.gases[/datum/gas/healium][MOLES], scaled_production * 20)
- internal_fusion.gases[/datum/gas/antinoblium][MOLES] += dirty_production_rate * 0.01 / 0.095 * seconds_per_tick
+ moderator_internal.adjust_gas(/datum/gas/healium, -min(moderator_internal.moles[/datum/gas/healium], scaled_production * 20))
+ internal_fusion.adjust_gas(/datum/gas/antinoblium, dirty_production_rate * 0.01 / 0.095 * seconds_per_tick)
//Modifies the internal_fusion temperature with the amount of heat output
var/temperature_modifier = selected_fuel.temperature_change_multiplier
@@ -403,7 +389,7 @@
var/max_iron_removable = IRON_OXYGEN_HEAL_PER_SECOND
var/iron_removed = min(max_iron_removable * seconds_per_tick, iron_content)
iron_content -= iron_removed
- moderator_internal.gases[/datum/gas/oxygen][MOLES] -= iron_removed * OXYGEN_MOLES_CONSUMED_PER_IRON_HEAL
+ moderator_internal.adjust_gas(/datum/gas/oxygen, -iron_removed * OXYGEN_MOLES_CONSUMED_PER_IRON_HEAL)
check_gravity_pulse(seconds_per_tick)
@@ -511,7 +497,7 @@
if(!waste_remove)
return
var/filtering_amount = moderator_scrubbing.len
- for(var/gas in moderator_internal.gases & moderator_scrubbing)
+ for(var/gas in moderator_internal.moles & moderator_scrubbing)
var/datum/gas_mixture/removed = moderator_internal.remove_specific(gas, (moderator_filtering_rate / filtering_amount) * seconds_per_tick)
if(removed)
linked_output.airs[1].merge(removed)
@@ -519,8 +505,8 @@
if (selected_fuel)
var/datum/gas_mixture/internal_remove
for(var/gas_id in selected_fuel.primary_products)
- if(internal_fusion.gases[gas_id][MOLES] > 0)
- internal_remove = internal_fusion.remove_specific(gas_id, internal_fusion.gases[gas_id][MOLES] * (1 - (1 - 0.25) ** seconds_per_tick))
+ if(internal_fusion.moles[gas_id] > 0)
+ internal_remove = internal_fusion.remove_specific(gas_id, internal_fusion.moles[gas_id] * (1 - (1 - 0.25) ** seconds_per_tick))
linked_output.airs[1].merge(internal_remove)
internal_fusion.garbage_collect()
moderator_internal.garbage_collect()
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm
index 0eee62cdbeaf..2af79e0b6a33 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm
@@ -78,7 +78,7 @@
return
/obj/machinery/atmospherics/components/unary/hypertorus/fuel_input
- name = "HFR fuel input port"
+ name = "\improper HFR fuel input port"
desc = "Input port for the Hypertorus Fusion Reactor, designed to take in fuels with the optimal fuel mix being a 50/50 split."
icon_state = "fuel_input_off"
icon_state_open = "fuel_input_open"
@@ -87,7 +87,7 @@
circuit = /obj/item/circuitboard/machine/HFR_fuel_input
/obj/machinery/atmospherics/components/unary/hypertorus/waste_output
- name = "HFR waste output port"
+ name = "\improper HFR waste output port"
desc = "Waste port for the Hypertorus Fusion Reactor, designed to output the hot waste gases coming from the core of the machine."
icon_state = "waste_output_off"
icon_state_open = "waste_output_open"
@@ -96,7 +96,7 @@
circuit = /obj/item/circuitboard/machine/HFR_waste_output
/obj/machinery/atmospherics/components/unary/hypertorus/moderator_input
- name = "HFR moderator input port"
+ name = "\improper HFR moderator input port"
desc = "Moderator port for the Hypertorus Fusion Reactor, designed to move gases inside the machine to cool and control the flow of the reaction."
icon_state = "moderator_input_off"
icon_state_open = "moderator_input_open"
@@ -149,7 +149,7 @@
return ..()
/obj/machinery/hypertorus/interface
- name = "HFR interface"
+ name = "\improper HFR interface"
desc = "Interface for the HFR to control the flow of the reaction."
icon_state = "interface_off"
circuit = /obj/item/circuitboard/machine/HFR_interface
@@ -235,14 +235,14 @@
//Internal Fusion gases
var/list/fusion_gasdata = list()
if(connected_core.internal_fusion.total_moles())
- for(var/gas_type in connected_core.internal_fusion.gases)
+ for(var/gas_type in connected_core.internal_fusion.moles)
var/datum/gas/gas = gas_type
fusion_gasdata.Add(list(list(
"id"= initial(gas.id),
- "amount" = round(connected_core.internal_fusion.gases[gas][MOLES], 0.01),
+ "amount" = round(connected_core.internal_fusion.moles[gas], 0.01),
)))
else
- for(var/gas_type in connected_core.internal_fusion.gases)
+ for(var/gas_type in connected_core.internal_fusion.moles)
var/datum/gas/gas = gas_type
fusion_gasdata.Add(list(list(
"id"= initial(gas.id),
@@ -251,14 +251,14 @@
//Moderator gases
var/list/moderator_gasdata = list()
if(connected_core.moderator_internal.total_moles())
- for(var/gas_type in connected_core.moderator_internal.gases)
+ for(var/gas_type in connected_core.moderator_internal.moles)
var/datum/gas/gas = gas_type
moderator_gasdata.Add(list(list(
"id"= initial(gas.id),
- "amount" = round(connected_core.moderator_internal.gases[gas][MOLES], 0.01),
+ "amount" = round(connected_core.moderator_internal.moles[gas], 0.01),
)))
else
- for(var/gas_type in connected_core.moderator_internal.gases)
+ for(var/gas_type in connected_core.moderator_internal.moles)
var/datum/gas/gas = gas_type
moderator_gasdata.Add(list(list(
"id"= initial(gas.id),
@@ -304,9 +304,13 @@
data["waste_remove"] = connected_core.waste_remove
data["filter_types"] = list()
- for(var/path in GLOB.meta_gas_info)
- var/list/gas = GLOB.meta_gas_info[path]
- data["filter_types"] += list(list("gas_id" = gas[META_GAS_ID], "gas_name" = gas[META_GAS_NAME], "enabled" = (path in connected_core.moderator_scrubbing)))
+ var/cached_gas_info = GLOB.meta_gas_info
+ for(var/path in cached_gas_info[META_GAS_ID])
+ data["filter_types"] += list(list(
+ "gas_id" = cached_gas_info[META_GAS_ID][path],
+ "gas_name" = cached_gas_info[META_GAS_NAME][path],
+ "enabled" = (path in connected_core.moderator_scrubbing)
+ ))
data["cooling_volume"] = connected_core.airs[1].volume
data["mod_filtering_rate"] = connected_core.moderator_filtering_rate
@@ -391,7 +395,7 @@
. = TRUE
/obj/machinery/hypertorus/corner
- name = "HFR corner"
+ name = "\improper HFR corner"
desc = "Structural piece of the machine."
icon_state = "corner_off"
circuit = /obj/item/circuitboard/machine/HFR_corner
@@ -425,7 +429,7 @@
use more advanced guides to understando how the various gases will act as moderators."
/obj/item/hfr_box
- name = "HFR box"
+ name = "\improper HFR box"
desc = "If you see this, call the police."
icon = 'icons/obj/machines/atmospherics/hypertorus.dmi'
icon_state = "error"
@@ -435,39 +439,39 @@
var/part_path
/obj/item/hfr_box/corner
- name = "HFR box corner"
+ name = "\improper HFR box corner"
desc = "Place this as the corner of your 3x3 multiblock fusion reactor"
icon_state = "box_corner"
box_type = "corner"
part_path = /obj/machinery/hypertorus/corner
/obj/item/hfr_box/body
- name = "HFR box body"
+ name = "\improper HFR box body"
desc = "Place this on the sides of the core box of your 3x3 multiblock fusion reactor"
box_type = "body"
icon_state = "box_body"
/obj/item/hfr_box/body/fuel_input
- name = "HFR box fuel input"
+ name = "\improper HFR box fuel input"
icon_state = "box_fuel"
part_path = /obj/machinery/atmospherics/components/unary/hypertorus/fuel_input
/obj/item/hfr_box/body/moderator_input
- name = "HFR box moderator input"
+ name = "\improper HFR box moderator input"
icon_state = "box_moderator"
part_path = /obj/machinery/atmospherics/components/unary/hypertorus/moderator_input
/obj/item/hfr_box/body/waste_output
- name = "HFR box waste output"
+ name = "\improper HFR box waste output"
icon_state = "box_waste"
part_path = /obj/machinery/atmospherics/components/unary/hypertorus/waste_output
/obj/item/hfr_box/body/interface
- name = "HFR box interface"
+ name = "\improper HFR box interface"
part_path = /obj/machinery/hypertorus/interface
/obj/item/hfr_box/core
- name = "HFR box core"
+ name = "\improper HFR box core"
desc = "Activate this with a multitool to deploy the full machine after setting up the other boxes"
icon_state = "box_core"
box_type = "core"
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm
index 3726291e5af9..efa04323c459 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm
@@ -167,7 +167,7 @@
internal_fusion.assert_gas(/datum/gas/antinoblium)
- moderator_internal.assert_gases(arglist(GLOB.meta_gas_info))
+ moderator_internal.assert_gases(arglist(GLOB.meta_gas_info[META_GAS_ID]))
if (!selected_fuel)
return
@@ -241,7 +241,7 @@
return FALSE
for(var/gas_type in selected_fuel.requirements)
internal_fusion.assert_gas(gas_type)
- if(internal_fusion.gases[gas_type][MOLES] < FUSION_MOLE_THRESHOLD)
+ if(internal_fusion.moles[gas_type] < FUSION_MOLE_THRESHOLD)
return FALSE
return TRUE
@@ -261,7 +261,7 @@
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_gas_requirements()
var/datum/gas_mixture/contents = linked_input.airs[1]
for(var/gas_type in selected_fuel.requirements)
- if(!contents.gases[gas_type] || !contents.gases[gas_type][MOLES])
+ if(!contents.moles[gas_type])
return FALSE
return TRUE
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm
index 13c872199dc0..8e94efa1a943 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/atmos_machines_recipes.dm
@@ -38,7 +38,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/hypern_crystalium
id = "hyper_crystalium"
- name = "Hypernoblium Crystal"
+ name = "Hyper-Noblium Crystal"
min_temp = 3
max_temp = 250
energy_release = -250000
@@ -47,7 +47,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/metallic_hydrogen
id = "metal_h"
- name = "Metallic hydrogen"
+ name = "Metallic Hydrogen"
min_temp = 50000
max_temp = 150000
energy_release = -2500000
@@ -56,7 +56,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/healium_grenade
id = "healium_g"
- name = "Healium crystal"
+ name = "Healium Crystal"
min_temp = 200
max_temp = 400
energy_release = -2000000
@@ -65,7 +65,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/proto_nitrate_grenade
id = "proto_nitrate_g"
- name = "Proto nitrate crystal"
+ name = "Proto-Nitrate Crystal"
min_temp = 200
max_temp = 400
energy_release = 1500000
@@ -74,7 +74,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/hot_ice
id = "hot_ice"
- name = "Hot ice"
+ name = "Hot Ice"
min_temp = 15
max_temp = 35
energy_release = -3000000
@@ -83,7 +83,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/ammonia_crystal
id = "ammonia_crystal"
- name = "Ammonia crystal"
+ name = "Ammonia Crystal"
min_temp = 200
max_temp = 240
energy_release = 950000
@@ -92,7 +92,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/shard
id = "crystal_shard"
- name = "Supermatter crystal shard"
+ name = "Supermatter Crystal Shard"
min_temp = 10
max_temp = 20
energy_release = 3500000
@@ -102,7 +102,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/n2o_crystal
id = "n2o_crystal"
- name = "Nitrous oxide crystal"
+ name = "Nitrous Oxide Crystal"
min_temp = 50
max_temp = 350
energy_release = 3500000
@@ -120,7 +120,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/plasma_sheet
id = "plasma_sheet"
- name = "Plasma sheet"
+ name = "Solidified Plasma"
min_temp = 10
max_temp = 20
energy_release = 3500000
@@ -138,7 +138,7 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/zaukerite
id = "zaukerite"
- name = "Zaukerite sheet"
+ name = "Solidified Zauker"
min_temp = 5
max_temp = 20
energy_release = 2900000
@@ -147,35 +147,35 @@ GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
/datum/gas_recipe/crystallizer/fuel_pellet
id = "fuel_basic"
- name = "standard fuel pellet"
+ name = "Standard Fuel Pellet"
energy_release = -6000000
requirements = list(/datum/gas/oxygen = 50, /datum/gas/plasma = 100)
products = list(/obj/item/fuel_pellet = 1)
/datum/gas_recipe/crystallizer/fuel_pellet_advanced
id = "fuel_advanced"
- name = "advanced fuel pellet"
+ name = "Advanced Fuel Pellet"
energy_release = -6000000
requirements = list(/datum/gas/tritium = 100, /datum/gas/hydrogen = 100)
products = list(/obj/item/fuel_pellet/advanced = 1)
/datum/gas_recipe/crystallizer/fuel_pellet_exotic
id = "fuel_exotic"
- name = "exotic fuel pellet"
+ name = "Exotic Fuel Pellet"
energy_release = -6000000
requirements = list(/datum/gas/hypernoblium = 100, /datum/gas/nitrium = 100)
products = list(/obj/item/fuel_pellet/exotic = 1)
/datum/gas_recipe/crystallizer/crystal_foam
id = "crystal_foam"
- name = "Crystal foam grenade"
+ name = "Foam Crystal"
energy_release = 140000
requirements = list(/datum/gas/carbon_dioxide = 150, /datum/gas/nitrous_oxide = 100, /datum/gas/water_vapor = 25)
products = list(/obj/item/grenade/gas_crystal/crystal_foam = 1)
/datum/gas_recipe/crystallizer/crystallized_nitrium
id = "crystallized_nitrium"
- name = "Nitrium crystal"
+ name = "Nitrium Crystal"
min_temp = 10
max_temp = 25
energy_release = -45000
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
index 219d7c338ca4..0fdf7936cd2a 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
@@ -110,19 +110,19 @@
/obj/machinery/atmospherics/components/binary/crystallizer/proc/inject_gases()
var/datum/gas_mixture/contents = airs[2]
for(var/gas_type in selected_recipe.requirements)
- if(!contents.gases[gas_type] || !contents.gases[gas_type][MOLES])
+ if(!contents.moles[gas_type])
continue
- if(internal.gases[gas_type] && internal.gases[gas_type][MOLES] >= selected_recipe.requirements[gas_type] * 2)
+ if(internal.moles[gas_type] >= selected_recipe.requirements[gas_type] * 2)
continue
- internal.merge(contents.remove_specific(gas_type, contents.gases[gas_type][MOLES] * gas_input))
+ internal.merge(contents.remove_specific(gas_type, contents.moles[gas_type] * gas_input))
///Checks if the gases required are all inside
/obj/machinery/atmospherics/components/binary/crystallizer/proc/internal_check()
var/gas_check = 0
for(var/gas_type in selected_recipe.requirements)
- if(!internal.gases[gas_type] || !internal.gases[gas_type][MOLES])
+ if(!internal.moles[gas_type])
return FALSE
- if(internal.gases[gas_type][MOLES] >= selected_recipe.requirements[gas_type])
+ if(internal.moles[gas_type] >= selected_recipe.requirements[gas_type])
gas_check++
if(gas_check == selected_recipe.requirements.len)
return TRUE
@@ -194,7 +194,7 @@
for(var/gas_type in selected_recipe.requirements)
var/required_gas_moles = selected_recipe.requirements[gas_type]
var/amount_consumed = required_gas_moles + (required_gas_moles * (quality_loss * 0.01))
- if(internal.gases[gas_type][MOLES] < amount_consumed)
+ if(internal.moles[gas_type] < amount_consumed)
quality_loss = min(quality_loss + 10, 100)
internal.remove_specific(gas_type, amount_consumed)
@@ -261,18 +261,20 @@
data["selected"] = ""
var/list/internal_gas_data = list()
+ var/list/cached_gas_name = GAS_META[META_GAS_NAME]
+ var/list/cached_gas_id = GAS_META[META_GAS_ID]
if(internal.total_moles())
- for(var/gasid in internal.gases)
+ for(var/gasid, amount in internal.moles)
internal_gas_data.Add(list(list(
- "name"= internal.gases[gasid][GAS_META][META_GAS_NAME],
- "id" = internal.gases[gasid][GAS_META][META_GAS_ID],
- "amount" = round(internal.gases[gasid][MOLES], 0.01),
+ "name"= cached_gas_name[gasid],
+ "id" = cached_gas_id[gasid],
+ "amount" = round(amount, 0.01),
)))
else
- for(var/gasid in internal.gases)
+ for(var/gasid in internal.moles)
internal_gas_data.Add(list(list(
- "name"= internal.gases[gasid][GAS_META][META_GAS_NAME],
- "id" = internal.gases[gasid][GAS_META][META_GAS_ID],
+ "name"= cached_gas_name[gasid],
+ "id" = cached_gas_id[gasid],
"amount" = 0,
)))
data["internal_gas_data"] = internal_gas_data
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
index 7dfbae06e19f..389355e4890b 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
@@ -1,6 +1,6 @@
/obj/item/hypernoblium_crystal
- name = "Hypernoblium Crystal"
- desc = "Crystalized oxygen and hypernoblium stored in a bottle to pressureproof your clothes or stop reactions occuring in portable atmospheric devices."
+ name = "\improper Hyper-Noblium crystal"
+ desc = "Crystallized Oxygen and Hyper-Noblium stored in a bottle. Pressure-proofs clothing or stop reactions occurring in portable atmospheric devices."
icon = 'icons/obj/pipes_n_cables/atmos.dmi'
icon_state = "hypernoblium_crystal"
var/uses = 1
@@ -37,3 +37,15 @@
if(uses <= 0)
qdel(src)
return ITEM_INTERACT_SUCCESS
+
+/obj/item/nitrium_crystal
+ name = "\improper Nitrium crystal"
+ desc = "A strange brown crystal that emits a foul smoke when chipped."
+ icon = 'icons/obj/pipes_n_cables/atmos.dmi'
+ icon_state = "nitrium_crystal"
+ var/cloud_size = 1
+
+/obj/item/nitrium_crystal/attack_self(mob/user)
+ . = ..()
+ do_chem_smoke(cloud_size, src, get_turf(src), list(/datum/reagent/nitrium_low_metabolization = 3, /datum/reagent/nitrium_high_metabolization = 2))
+ qdel(src)
diff --git a/code/modules/atmospherics/machinery/components/tank.dm b/code/modules/atmospherics/machinery/components/tank.dm
index f95e5c18da55..db654684f994 100644
--- a/code/modules/atmospherics/machinery/components/tank.dm
+++ b/code/modules/atmospherics/machinery/components/tank.dm
@@ -141,8 +141,8 @@
var/pressure_limit = max_pressure * safety_margin
var/moles_to_add = (pressure_limit * air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- air_contents.assert_gas(gastype)
- air_contents.gases[gastype][MOLES] += moles_to_add
+
+ air_contents.adjust_gas(gastype, moles_to_add)
air_contents.archive()
/obj/machinery/atmospherics/components/tank/process_atmos()
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
index 25217de538ce..c1d282c4c6da 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
@@ -100,7 +100,7 @@
if(filtering)
var/datum/gas_mixture/filtered_out = new
- for(var/gas in removed.gases & filter_type)
+ for(var/gas in removed.moles & filter_type)
var/datum/gas_mixture/removing = removed.remove_specific_ratio(gas, 1)
if(removing)
filtered_out.merge(removing)
@@ -135,9 +135,9 @@
data["max_rate"] = round(MAX_TRANSFER_RATE)
data["filter_types"] = list()
- for(var/path in GLOB.meta_gas_info)
- var/list/gas = GLOB.meta_gas_info[path]
- data["filter_types"] += list(list("gas_id" = gas[META_GAS_ID], "enabled" = (path in filter_type)))
+ var/cached_gas_info = GLOB.meta_gas_info
+ for(var/path in cached_gas_info[META_GAS_ID])
+ data["filter_types"] += list(list("gas_id" = cached_gas_info[META_GAS_ID][path], "enabled" = (path in filter_type)))
return data
@@ -170,7 +170,7 @@
change = "added"
else
change = "removed"
- var/gas_name = GLOB.meta_gas_info[gas_id2path(params["val"])][META_GAS_NAME]
+ var/gas_name = GLOB.meta_gas_info[META_GAS_NAME][gas_id2path(params["val"])]
usr.investigate_log("[change] [gas_name] from the filter type.", INVESTIGATE_ATMOS)
. = TRUE
update_appearance()
@@ -226,57 +226,75 @@
/obj/machinery/atmospherics/components/trinary/filter/atmos //Used for atmos waste loops
on = TRUE
icon_state = "filter_on-0"
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/n2
name = "nitrogen filter"
filter_type = list(/datum/gas/nitrogen)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/o2
name = "oxygen filter"
filter_type = list(/datum/gas/oxygen)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/co2
name = "carbon dioxide filter"
filter_type = list(/datum/gas/carbon_dioxide)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/n2o
name = "nitrous oxide filter"
filter_type = list(/datum/gas/nitrous_oxide)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/plasma
name = "plasma filter"
filter_type = list(/datum/gas/plasma)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/bz
name = "bz filter"
filter_type = list(/datum/gas/bz)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/freon
name = "freon filter"
filter_type = list(/datum/gas/freon)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/halon
name = "halon filter"
filter_type = list(/datum/gas/halon)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/healium
name = "healium filter"
filter_type = list(/datum/gas/healium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/h2
name = "hydrogen filter"
filter_type = list(/datum/gas/hydrogen)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/hypernoblium
- name = "hypernoblium filter"
+ name = "hyper-noblium filter"
filter_type = list(/datum/gas/hypernoblium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/miasma
name = "miasma filter"
filter_type = list(/datum/gas/miasma)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/no2
name = "nitrium filter"
filter_type = list(/datum/gas/nitrium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/pluoxium
name = "pluoxium filter"
filter_type = list(/datum/gas/pluoxium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/proto_nitrate
name = "proto-nitrate filter"
filter_type = list(/datum/gas/proto_nitrate)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/tritium
name = "tritium filter"
filter_type = list(/datum/gas/tritium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/h2o
name = "water vapor filter"
filter_type = list(/datum/gas/water_vapor)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/zauker
name = "zauker filter"
filter_type = list(/datum/gas/zauker)
@@ -286,71 +304,91 @@
filter_type = list(/datum/gas/helium)
/obj/machinery/atmospherics/components/trinary/filter/atmos/antinoblium
- name = "antinoblium filter"
+ name = "anti-noblium filter"
filter_type = list(/datum/gas/antinoblium)
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped //This feels wrong, I know
icon_state = "filter_on-0_f"
flipped = TRUE
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/n2
name = "nitrogen filter"
filter_type = list(/datum/gas/nitrogen)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/o2
name = "oxygen filter"
filter_type = list(/datum/gas/oxygen)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/co2
name = "carbon dioxide filter"
filter_type = list(/datum/gas/carbon_dioxide)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/n2o
name = "nitrous oxide filter"
filter_type = list(/datum/gas/nitrous_oxide)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/plasma
name = "plasma filter"
filter_type = list(/datum/gas/plasma)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/bz
name = "bz filter"
filter_type = list(/datum/gas/bz)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/freon
name = "freon filter"
filter_type = list(/datum/gas/freon)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/halon
name = "halon filter"
filter_type = list(/datum/gas/halon)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/healium
name = "healium filter"
filter_type = list(/datum/gas/healium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/h2
name = "hydrogen filter"
filter_type = list(/datum/gas/hydrogen)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/hypernoblium
- name = "hypernoblium filter"
+ name = "hyper-noblium filter"
filter_type = list(/datum/gas/hypernoblium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/miasma
name = "miasma filter"
filter_type = list(/datum/gas/miasma)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/no2
name = "nitrium filter"
filter_type = list(/datum/gas/nitrium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/pluoxium
name = "pluoxium filter"
filter_type = list(/datum/gas/pluoxium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/proto_nitrate
name = "proto-nitrate filter"
filter_type = list(/datum/gas/proto_nitrate)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/tritium
name = "tritium filter"
filter_type = list(/datum/gas/tritium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/h2o
name = "water vapor filter"
filter_type = list(/datum/gas/water_vapor)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/zauker
name = "zauker filter"
filter_type = list(/datum/gas/zauker)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/helium
name = "helium filter"
filter_type = list(/datum/gas/helium)
+
/obj/machinery/atmospherics/components/trinary/filter/atmos/flipped/antinoblium
- name = "antinoblium filter"
+ name = "anti-noblium filter"
filter_type = list(/datum/gas/antinoblium)
// These two filter types have critical_machine flagged to on and thus causes the area they are in to be exempt from the Grid Check event.
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm b/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
deleted file mode 100644
index ebe053663f9e..000000000000
--- a/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
+++ /dev/null
@@ -1,231 +0,0 @@
-/obj/machinery/atmospherics/components/unary/bluespace_sender
- icon = 'icons/obj/machines/atmospherics/bluespace_gas_selling.dmi'
- icon_state = "bluespace_sender_off"
- base_icon_state = "bluespace_sender"
- name = "Bluespace Gas Sender"
- desc = "Sends gases to the bluespace network to be shared with the connected vendors, who knows what's beyond!"
- interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
-
- density = TRUE
- max_integrity = 300
- armor_type = /datum/armor/unary_bluespace_sender
- layer = OBJ_LAYER
- circuit = /obj/item/circuitboard/machine/bluespace_sender
- move_resist = MOVE_RESIST_DEFAULT
- set_dir_on_move = FALSE
- pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
-
- ///Gas mixture containing the inserted gases and that is connected to the vendors
- var/datum/gas_mixture/bluespace_network
- ///Rate of gas transfer inside the network (from 0 to 1)
- var/gas_transfer_rate = 0.5
- ///A base price for each and every gases, in case you don't want to change them
- var/list/base_prices = list()
- ///List storing all the vendors connected to the machine
- var/list/vendors
- ///Amount of credits gained from each vendor
- var/credits_gained = 0
-
-/// All bluespace gas senders
-GLOBAL_LIST_EMPTY_TYPED(bluespace_senders, /obj/machinery/atmospherics/components/unary/bluespace_sender)
-
-/datum/armor/unary_bluespace_sender
- energy = 100
- fire = 80
- acid = 30
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/Initialize(mapload)
- . = ..()
- initialize_directions = dir
- bluespace_network = new
- for(var/gas_id in GLOB.meta_gas_info)
- bluespace_network.assert_gas(gas_id)
- for(var/gas_id in GLOB.meta_gas_info)
- var/datum/gas/gas = gas_id
- base_prices[gas_id] = initial(gas.base_value)
-
- GLOB.bluespace_senders += src
-
- update_appearance()
- register_context()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/Destroy()
- GLOB.bluespace_senders -= src
-
- return ..()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/examine(mob/user)
- . = ..()
- . += span_notice("With the panel open:")
- . += span_notice(" -Use a wrench with left-click to rotate [src] and right-click to unanchor it.")
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
- if(anchored && !panel_open && is_operational)
- context[SCREENTIP_CONTEXT_CTRL_LMB] = "Turn [on ? "off" : "on"]"
- if(!held_item)
- return CONTEXTUAL_SCREENTIP_SET
- switch(held_item.tool_behaviour)
- if(TOOL_SCREWDRIVER)
- context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] panel"
- if(TOOL_WRENCH)
- context[SCREENTIP_CONTEXT_LMB] = "Rotate"
- context[SCREENTIP_CONTEXT_RMB] = "[anchored ? "Unan" : "An"]chor"
- return CONTEXTUAL_SCREENTIP_SET
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/is_connectable()
- if(!anchored)
- return FALSE
- . = ..()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/update_icon_state()
- if(panel_open)
- icon_state = "[base_icon_state]_open"
- return ..()
- if(on && is_operational)
- icon_state = "[base_icon_state]_on"
- return ..()
- icon_state = "[base_icon_state]_off"
- return ..()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/update_overlays()
- . = ..()
- . += get_pipe_image(icon, "pipe", dir, , piping_layer)
- if(showpipe)
- . += get_pipe_image(icon, "pipe", initialize_directions)
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/process_atmos()
- if(!is_operational || !on || !nodes[1]) //if it has no power or its switched off, dont process atmos
- return
-
- var/datum/gas_mixture/content = airs[1]
- var/datum/gas_mixture/remove = content.remove_ratio(gas_transfer_rate)
- bluespace_network.merge(remove)
- bluespace_network.temperature = T20C
- update_parents()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/relocate_airs()
- if(bluespace_network.total_moles() > 0)
- airs[1].merge(bluespace_network)
- airs[1].garbage_collect()
- return ..()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/screwdriver_act(mob/living/user, obj/item/tool)
- if(on)
- balloon_alert(user, "turn off!")
- return ITEM_INTERACT_SUCCESS
- if(!anchored)
- balloon_alert(user, "anchor!")
- return ITEM_INTERACT_SUCCESS
- if(default_deconstruction_screwdriver(user, "[base_icon_state]_open", "[base_icon_state]_off", tool))
- return ITEM_INTERACT_SUCCESS
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/crowbar_act(mob/living/user, obj/item/tool)
- if(panel_open && bluespace_network.total_moles() > 0 && !nodes[1])
- say("WARNING - Bluespace network can contain hazardous gases, deconstruct with caution!")
- return crowbar_deconstruction_act(user, tool)
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/multitool_act(mob/living/user, obj/item/item)
- var/obj/item/multitool/multitool = item
- multitool.set_buffer(src)
- balloon_alert(user, "saved to multitool buffer")
- return TRUE
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/wrench_act(mob/living/user, obj/item/tool)
- return default_change_direction_wrench(user, tool)
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/wrench_act_secondary(mob/living/user, obj/item/tool)
- if(!panel_open)
- balloon_alert(user, "open panel!")
- return
- if(default_unfasten_wrench(user, tool))
- change_pipe_connection(!anchored)
- return ITEM_INTERACT_SUCCESS
- return
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/default_change_direction_wrench(mob/user, obj/item/item)
- if(!..())
- return FALSE
- set_init_directions()
- update_appearance()
- return TRUE
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/click_ctrl(mob/user)
- if(!panel_open && is_operational)
- on = !on
- balloon_alert(user, "turned [on ? "on" : "off"]")
- investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
- update_appearance()
- return CLICK_ACTION_SUCCESS
- return NONE
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "BluespaceSender", name)
- ui.open()
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/ui_data(mob/user)
- var/list/data = list()
- data["on"] = on
- data["gas_transfer_rate"] = gas_transfer_rate
- var/list/bluespace_gasdata = list()
- if(bluespace_network.total_moles())
- for(var/gas_id in bluespace_network.gases)
- bluespace_gasdata.Add(list(list(
- "name" = bluespace_network.gases[gas_id][GAS_META][META_GAS_NAME],
- "id" = bluespace_network.gases[gas_id][GAS_META][META_GAS_ID],
- "amount" = round(bluespace_network.gases[gas_id][MOLES], 0.01),
- "price" = base_prices[gas_id],
- )))
- else
- for(var/gas_id in bluespace_network.gases)
- bluespace_gasdata.Add(list(list(
- "name" = bluespace_network.gases[gas_id][GAS_META][META_GAS_NAME],
- "id" = "",
- "amount" = 0,
- "price" = 0,
- )))
- data["bluespace_network_gases"] = bluespace_gasdata
- var/list/vendors_list = list()
- if(vendors)
- for(var/obj/machinery/bluespace_vendor/vendor in vendors)
- vendors_list.Add(list(list(
- "name" = vendor.name,
- "area" = get_area(vendor),
- )))
- data["vendors_list"] = vendors_list
- data["credits"] = credits_gained
- return data
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/ui_act(action, params)
- . = ..()
- if(.)
- return
-
- switch(action)
- if("power")
- on = !on
- investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", INVESTIGATE_ATMOS)
- update_appearance()
- . = TRUE
-
- if("rate")
- gas_transfer_rate = clamp(params["rate"], 0, 1)
- . = TRUE
-
- if("price")
- var/gas_type = gas_id2path(params["gas_type"])
- base_prices[gas_type] = clamp(params["gas_price"], 0, 100)
- . = TRUE
-
- if("retrieve")
- if(bluespace_network.total_moles() > 0)
- var/datum/gas_mixture/remove = bluespace_network.remove(bluespace_network.total_moles())
- airs[1].merge(remove)
- update_parents()
- bluespace_network.garbage_collect()
- . = TRUE
-
-/obj/machinery/atmospherics/components/unary/bluespace_sender/update_layer()
- return
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 28a6de3a1d8e..617be670dbb7 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -481,7 +481,7 @@
var/datum/gas_mixture/air1 = internal_connector.gas_connector.airs[1]
//check for workable conditions
- if(!internal_connector.gas_connector.nodes[1] || !air1 || !air1.gases.len || air1.total_moles() < CRYO_MIN_GAS_MOLES) // Turn off if the machine won't work.
+ if(!internal_connector.gas_connector.nodes[1] || !air1 || !air1.moles.len || air1.total_moles() < CRYO_MIN_GAS_MOLES) // Turn off if the machine won't work.
set_on(FALSE)
aas_config_announce(/datum/aas_config_entry/medical_cryo_announcements, list("EJECTING" = autoeject), src, list(broadcast_channel), "Insufficient Gas")
if(autoeject) // Eject if configured.
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
index 0f7501c5bb22..2165337b2ff3 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
@@ -179,12 +179,10 @@
on = FALSE
return FALSE
- var/list/changed_gas = air.gases
-
- if(!changed_gas)
+ if(!air.moles)
return FALSE
- if(scrubbing == ATMOS_DIRECTION_SIPHONING || length(filter_types & changed_gas))
+ if(scrubbing == ATMOS_DIRECTION_SIPHONING || length(filter_types & air.moles))
return TRUE
return FALSE
@@ -198,14 +196,16 @@
var/turf/open/us = loc
if(!istype(us))
return
- scrub(us)
+ if(scrub(us))
+ us.air_update_turf(FALSE, FALSE)
if(widenet)
if(COOLDOWN_FINISHED(src, check_turfs_cooldown))
check_turfs()
COOLDOWN_START(src, check_turfs_cooldown, 2 SECONDS)
for(var/turf/tile in adjacent_turfs)
- scrub(tile)
+ if (scrub(tile))
+ tile.air_update_turf(FALSE, FALSE)
return TRUE
///filtered gases at or below this amount automatically get removed from the mix
@@ -216,36 +216,36 @@
return FALSE
var/datum/gas_mixture/environment = tile.return_air()
var/datum/gas_mixture/air_contents = airs[1]
- var/list/env_gases = environment.gases
+ var/list/env_cached_moles = environment.moles
if(air_contents.return_pressure() >= 50 * ONE_ATMOSPHERE)
return FALSE
if(scrubbing == ATMOS_DIRECTION_SCRUBBING)
- if(length(env_gases & filter_types))
+ if(length(env_cached_moles & filter_types))
///contains all of the gas we're sucking out of the tile, gets put into our parent pipenet
var/datum/gas_mixture/filtered_out = new
- var/list/filtered_gases = filtered_out.gases
+ var/list/filtered_out_cached_moles = filtered_out.moles
filtered_out.temperature = environment.temperature
///maximum percentage of the turfs gas we can filter
var/removal_ratio = min(1, volume_rate / environment.volume)
var/total_moles_to_remove = 0
- for(var/gas in filter_types & env_gases)
- total_moles_to_remove += env_gases[gas][MOLES]
+ for(var/gas_id in filter_types & env_cached_moles)
+ total_moles_to_remove += env_cached_moles[gas_id]
if(total_moles_to_remove == 0)//sometimes this gets non gc'd values
environment.garbage_collect()
return FALSE
- for(var/gas in filter_types & env_gases)
- filtered_out.add_gas(gas)
- //take this gases portion of removal_ratio of the turfs air, or all of that gas if less than or equal to MINIMUM_MOLES_TO_SCRUB
- var/transferred_moles = max(QUANTIZE(env_gases[gas][MOLES] * removal_ratio * (env_gases[gas][MOLES] / total_moles_to_remove)), min(MINIMUM_MOLES_TO_SCRUB, env_gases[gas][MOLES]))
+ for(var/gas_id in filter_types & env_cached_moles)
+ filtered_out.add_gas(gas_id)
+ //take this gases portion of removal_ratio of the turfs air, or all of that gas_id if less than or equal to MINIMUM_MOLES_TO_SCRUB
+ var/transferred_moles = max(QUANTIZE(env_cached_moles[gas_id] * removal_ratio * (env_cached_moles[gas_id] / total_moles_to_remove)), min(MINIMUM_MOLES_TO_SCRUB, env_cached_moles[gas_id]))
- filtered_gases[gas][MOLES] = transferred_moles
- env_gases[gas][MOLES] -= transferred_moles
+ filtered_out_cached_moles[gas_id] = transferred_moles
+ env_cached_moles[gas_id] -= transferred_moles
environment.garbage_collect()
diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm
index 35b8fa61bbc2..b862295e8714 100644
--- a/code/modules/atmospherics/machinery/datum_pipeline.dm
+++ b/code/modules/atmospherics/machinery/datum_pipeline.dm
@@ -254,12 +254,13 @@
var/total_thermal_energy = 0
var/total_heat_capacity = 0
- var/list/total_gases = list()
-
var/volume_sum = 0
var/static/process_id = 0
- process_id = (process_id + 1) % (SHORT_REAL_LIMIT - 1)
+ process_id = WRAP_UID(process_id + 1)
+ var/datum/gas_mixture/total_gas_mixture = new
+ var/list/total_cached_moles = total_gas_mixture.moles
+ var/list/cached_specific_heat = GAS_META[META_GAS_SPECIFIC_HEAT]
for(var/datum/gas_mixture/gas_mixture as anything in gas_mixture_list)
// Ensure we never walk the same mix twice
@@ -271,14 +272,11 @@
// This is sort of a combined merge + heat_capacity calculation
- var/list/giver_gases = gas_mixture.gases
- var/heat_capacity = 0
+ var/list/giver_cached_moles = gas_mixture.moles
+ var/heat_capacity = values_dot(giver_cached_moles, cached_specific_heat)
//gas transfer
- for(var/giver_id in giver_gases)
- var/giver_gas_data = giver_gases[giver_id]
- ASSERT_GAS_IN_LIST(giver_id, total_gases)
- total_gases[giver_id][MOLES] += giver_gas_data[MOLES]
- heat_capacity += giver_gas_data[MOLES] * giver_gas_data[GAS_META][META_GAS_SPECIFIC_HEAT]
+ for(var/gas_id, amount in giver_cached_moles)
+ total_cached_moles[gas_id] += amount
total_heat_capacity += heat_capacity
total_thermal_energy += gas_mixture.temperature * heat_capacity
@@ -286,9 +284,8 @@
if(volume_sum == 0)
return
- var/datum/gas_mixture/total_gas_mixture = new(volume_sum)
+ total_gas_mixture.volume = volume_sum
total_gas_mixture.temperature = total_heat_capacity ? (total_thermal_energy / total_heat_capacity) : 0
- total_gas_mixture.gases = total_gases
total_gas_mixture.garbage_collect()
//Update individual gas_mixtures by volume ratio
@@ -329,8 +326,8 @@
var/current_weight = 0
var/current_color
- for(var/datum/gas/gas_path as anything in air.gases)
- var/gas_weight = air.gases[gas_path][MOLES]
+ for(var/datum/gas/gas_path as anything in air.moles)
+ var/gas_weight = air.moles[gas_path]
if(!gas_weight)
continue
var/gas_color = initial(gas_path.primary_color)
diff --git a/code/modules/atmospherics/machinery/other/miner.dm b/code/modules/atmospherics/machinery/other/miner.dm
index f61ebf3766be..d588bd886288 100644
--- a/code/modules/atmospherics/machinery/other/miner.dm
+++ b/code/modules/atmospherics/machinery/other/miner.dm
@@ -136,7 +136,7 @@
return FALSE
var/datum/gas_mixture/merger = new
merger.assert_gas(spawn_id)
- merger.gases[spawn_id][MOLES] = spawn_mol * seconds_per_tick
+ merger.moles[spawn_id] = spawn_mol * seconds_per_tick
merger.temperature = spawn_temp
O.assume_air(merger)
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index 26e75dfd5ad7..b60ad71a9e7e 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -61,7 +61,7 @@
create_gas()
if(ispath(gas_type, /datum/gas))
- desc = "[GLOB.meta_gas_info[gas_type][META_GAS_NAME]]. [GLOB.meta_gas_info[gas_type][META_GAS_DESC]]"
+ desc = "[GLOB.meta_gas_info[META_GAS_NAME][gas_type]]. [GLOB.meta_gas_info[META_GAS_DESC][gas_type]]"
update_window()
@@ -126,7 +126,7 @@
greyscale_colors = "#c6c0b5"
/obj/machinery/portable_atmospherics/canister/antinoblium
- name = "\improper Antinoblium canister"
+ name = "\improper Anti-Noblium canister"
gas_type = /datum/gas/antinoblium
filled = 1
icon_state = "/obj/machinery/portable_atmospherics/canister/antinoblium"
@@ -213,7 +213,7 @@
greyscale_colors = "#e9ff5c#f4fce8"
/obj/machinery/portable_atmospherics/canister/nitrous_oxide
- name = "\improper Nitrous oxide canister"
+ name = "\improper Nitrous Oxide canister"
gas_type = /datum/gas/nitrous_oxide
icon_state = "/obj/machinery/portable_atmospherics/canister/nitrous_oxide"
post_init_icon_state = ""
@@ -229,7 +229,7 @@
greyscale_colors = "#7b4732"
/obj/machinery/portable_atmospherics/canister/nob
- name = "\improper Hyper-noblium canister"
+ name = "\improper Hyper-Noblium canister"
gas_type = /datum/gas/hypernoblium
icon_state = "/obj/machinery/portable_atmospherics/canister/nob"
post_init_icon_state = ""
@@ -253,7 +253,7 @@
greyscale_colors = "#2786e5"
/obj/machinery/portable_atmospherics/canister/proto_nitrate
- name = "\improper Proto Nitrate canister"
+ name = "\improper Proto-Nitrate canister"
gas_type = /datum/gas/proto_nitrate
filled = 1
icon_state = "/obj/machinery/portable_atmospherics/canister/proto_nitrate"
@@ -304,9 +304,8 @@
pressure_limit = 1e14
/obj/machinery/portable_atmospherics/canister/fusion_test/create_gas()
- air_contents.add_gases(/datum/gas/hydrogen, /datum/gas/tritium)
- air_contents.gases[/datum/gas/hydrogen][MOLES] = 300
- air_contents.gases[/datum/gas/tritium][MOLES] = 300
+ air_contents.adjust_gas(/datum/gas/hydrogen, 300)
+ air_contents.adjust_gas(/datum/gas/tritium, 300)
air_contents.temperature = 10000
SSair.start_processing_machine(src)
@@ -319,9 +318,8 @@
greyscale_colors = "#9fba6c#3d4680"
/obj/machinery/portable_atmospherics/canister/anesthetic_mix/create_gas()
- air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrous_oxide)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (O2_ANESTHETIC * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- air_contents.gases[/datum/gas/nitrous_oxide][MOLES] = (N2O_ANESTHETIC * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
+ air_contents.adjust_gas(/datum/gas/oxygen, (O2_ANESTHETIC * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature))
+ air_contents.adjust_gas(/datum/gas/nitrous_oxide, (N2O_ANESTHETIC * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature))
SSair.start_processing_machine(src)
/**
@@ -331,14 +329,12 @@
/obj/machinery/portable_atmospherics/canister/proc/create_gas()
if(!gas_type)
return
- air_contents.add_gas(gas_type)
- air_contents.gases[gas_type][MOLES] = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
+ air_contents.adjust_gas(gas_type, (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature))
SSair.start_processing_machine(src)
/obj/machinery/portable_atmospherics/canister/air/create_gas()
- air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
- air_contents.gases[/datum/gas/oxygen][MOLES] = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
- air_contents.gases[/datum/gas/nitrogen][MOLES] = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
+ air_contents.adjust_gas(/datum/gas/oxygen, (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature))
+ air_contents.adjust_gas(/datum/gas/nitrogen, (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature))
SSair.start_processing_machine(src)
/obj/machinery/portable_atmospherics/canister/update_icon_state()
@@ -717,14 +713,16 @@
var/list/output = list()
output += "[key_name(user)] opened a canister [wire_pulsed ? "via wire pulse" : ""] that contains the following:"
var/list/admin_output = list()
- admin_output += "[ADMIN_LOOKUPFLW(user)] opened a canister [wire_pulsed ? "via wire pulse" : ""] that contains the following at [ADMIN_VERBOSEJMP(src)]:"
- var/list/gases = air_contents.gases
+ admin_output += "[ADMIN_LOOKUPFLW(user)] opened a canister[wire_pulsed ? " via wire pulse" : ""] that contains the following at [ADMIN_VERBOSEJMP(src)]:"
+ var/list/cached_moles = air_contents.moles
+ var/list/cached_gas_name = GAS_META[META_GAS_NAME]
+ var/list/cached_gas_danger = GAS_META[META_GAS_DANGER]
+ var/list/cached_gas_visible = GAS_META[META_GAS_MOLES_VISIBLE]
var/danger = FALSE
- for(var/gas_index in 1 to length(gases))
- var/list/gas_info = gases[gases[gas_index]]
- var/list/meta = gas_info[GAS_META]
- var/name = meta[META_GAS_NAME]
- var/moles = gas_info[MOLES]
+ for(var/gas_index in 1 to length(cached_moles))
+ var/gas_id = cached_moles[gas_index]
+ var/name = cached_gas_name[gas_id]
+ var/moles = cached_moles[gas_id]
output += "[name]: [moles] moles."
if(gas_index <= 5) //the first five gases added
@@ -732,7 +730,7 @@
else if(gas_index == 6) // anddd the warning
admin_output += "Too many gases to log. Check investigate log."
//if moles_visible is undefined, default to default visibility
- if(meta[META_GAS_DANGER] && moles > (meta[META_GAS_MOLES_VISIBLE] || MOLES_GAS_VISIBLE))
+ if(cached_gas_danger[gas_id] && moles > (cached_gas_visible[gas_id] || MOLES_GAS_VISIBLE))
danger = TRUE
if(danger) //sent to admin's chat if contains dangerous gases
diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm
index e7dfc229a39d..ab6af7a22577 100644
--- a/code/modules/atmospherics/machinery/portable/pump.dm
+++ b/code/modules/atmospherics/machinery/portable/pump.dm
@@ -119,11 +119,11 @@
if(on)
SSair.start_processing_machine(src)
if(on && !holding)
- var/plasma = air_contents.gases[/datum/gas/plasma]
- var/n2o = air_contents.gases[/datum/gas/nitrous_oxide]
- if(n2o || plasma)
- message_admins("[ADMIN_LOOKUPFLW(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [ADMIN_VERBOSEJMP(src)]")
- log_admin("[key_name(usr)] turned on a pump that contains [n2o ? "N2O" : ""][n2o && plasma ? " & " : ""][plasma ? "Plasma" : ""] at [AREACOORD(src)]")
+ var/plasma_moles = air_contents.moles[/datum/gas/plasma]
+ var/n2o_moles = air_contents.moles[/datum/gas/nitrous_oxide]
+ if(n2o_moles || plasma_moles)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] turned on a pump that contains [n2o_moles ? "N2O" : ""][n2o_moles && plasma_moles ? " & " : ""][plasma_moles ? "Plasma" : ""] at [ADMIN_VERBOSEJMP(src)]")
+ log_admin("[key_name(usr)] turned on a pump that contains [n2o_moles ? "N2O" : ""][n2o_moles && plasma_moles ? " & " : ""][plasma_moles ? "Plasma" : ""] at [AREACOORD(src)]")
else if(on && direction == PUMP_OUT)
usr.investigate_log("started a transfer into [holding].", INVESTIGATE_ATMOS)
. = TRUE
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index dc148d96e1ec..24b9bcd8dcf3 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -1,16 +1,17 @@
/obj/machinery/portable_atmospherics/scrubber
name = "portable air scrubber"
+ desc = "A portable variant of the station scrubbers, capable of filtering gas from the air around it or inserted tank. May also be wrenched into a port."
icon_state = "scrubber"
density = TRUE
max_integrity = 250
- volume = 1000
+ volume = 2000
///Is the machine on?
var/on = FALSE
///the rate the machine will scrub air
- var/volume_rate = 1000
+ var/volume_rate = 650
///Multiplier with ONE_ATMOSPHERE, if the enviroment pressure is higher than that, the scrubber won't work
- var/overpressure_m = 80
+ var/overpressure_m = 100
///Should the machine use overlay in update_overlays() when open/close?
var/use_overlays = TRUE
///List of gases that can be scrubbed
@@ -31,9 +32,9 @@
/datum/gas/halon,
)
-/obj/machinery/portable_atmospherics/scrubber/Destroy()
- var/turf/T = get_turf(src)
- T.assume_air(air_contents)
+/obj/machinery/portable_atmospherics/scrubber/on_deconstruction(disassembled)
+ var/turf/local_turf = get_turf(src)
+ local_turf.assume_air(air_contents)
return ..()
/obj/machinery/portable_atmospherics/scrubber/update_icon_state()
@@ -59,37 +60,56 @@
excited = TRUE
- var/atom/target = holding || get_turf(src)
- scrub(target.return_air())
+ if(!isnull(holding))
+ scrub(holding.return_air())
+ return ..()
+
+ var/turf/epicentre = get_turf(src)
+ if(isopenturf(epicentre))
+ if(scrub(epicentre.return_air()))
+ epicentre.air_update_turf(FALSE, FALSE)
+ for(var/turf/open/openturf as anything in epicentre.get_atmos_adjacent_turfs(alldir = TRUE))
+ if(scrub(openturf.return_air()))
+ openturf.air_update_turf(FALSE, FALSE)
return ..()
/**
* Called in process_atmos(), handles the scrubbing of the given gas_mixture
* Arguments:
* * mixture: the gas mixture to be scrubbed
+ * Returns: TRUE if anything was scrubbed, FALSE otherwise
*/
-/obj/machinery/portable_atmospherics/scrubber/proc/scrub(datum/gas_mixture/mixture)
+/obj/machinery/portable_atmospherics/scrubber/proc/scrub(datum/gas_mixture/environment)
if(air_contents.return_pressure() >= overpressure_m * ONE_ATMOSPHERE)
- return
+ return FALSE
- var/transfer_moles = min(1, volume_rate / mixture.volume) * mixture.total_moles()
+ var/list/cached_moles = environment.moles
- var/datum/gas_mixture/filtering = mixture.remove(transfer_moles) // Remove part of the mixture to filter.
- var/datum/gas_mixture/filtered = new
- if(!filtering)
- return
+ //contains all of the gas we're sucking out of the tile, gets put into our parent pipenet
+ var/datum/gas_mixture/filtered_out = new
- filtered.temperature = filtering.temperature
- for(var/gas in filtering.gases & scrubbing)
- filtered.add_gas(gas)
- filtered.gases[gas][MOLES] = filtering.gases[gas][MOLES] // Shuffle the "bad" gasses to the filtered mixture.
- filtering.gases[gas][MOLES] = 0
- filtering.garbage_collect() // Now that the gasses are set to 0, clean up the mixture.
+ filtered_out.temperature = environment.temperature
- air_contents.merge(filtered) // Store filtered out gasses.
- mixture.merge(filtering) // Returned the cleaned gas.
- if(!holding)
- air_update_turf(FALSE, FALSE)
+ //maximum percentage of the turfs gas we can filter
+ var/removal_ratio = min(1, volume_rate / environment.volume)
+
+ var/total_moles_to_remove = 0
+ for(var/gas_id in cached_moles & scrubbing)
+ total_moles_to_remove += cached_moles[gas_id]
+
+ if(!total_moles_to_remove)//no gases to remove
+ return FALSE
+
+ for(var/gas_id in cached_moles & scrubbing)
+ var/transferred_moles = max(QUANTIZE(cached_moles[gas_id] * removal_ratio * (cached_moles[gas_id] / total_moles_to_remove)), min(MOLAR_ACCURACY*1000, cached_moles[gas_id]))
+
+ filtered_out.moles[gas_id] += transferred_moles
+ cached_moles[gas_id] -= transferred_moles
+
+ environment.garbage_collect()
+ //Remix the resulting gases
+ air_contents.merge(filtered_out)
+ return TRUE
/obj/machinery/portable_atmospherics/scrubber/emp_act(severity)
. = ..()
@@ -118,9 +138,13 @@
data["reactionSuppressionEnabled"] = !!suppress_reactions
data["filterTypes"] = list()
- for(var/path in GLOB.meta_gas_info)
- var/list/gas = GLOB.meta_gas_info[path]
- data["filterTypes"] += list(list("gasId" = gas[META_GAS_ID], "gasName" = gas[META_GAS_NAME], "enabled" = (path in scrubbing)))
+ var/cached_gas_info = GLOB.meta_gas_info
+ for(var/path in cached_gas_info[META_GAS_ID])
+ data["filterTypes"] += list(list(
+ "gasId" = cached_gas_info[META_GAS_ID][path],
+ "gasName" = cached_gas_info[META_GAS_NAME][path],
+ "enabled" = (path in scrubbing)
+ ))
if(holding)
data["holding"] = list()
@@ -142,7 +166,7 @@
else if(on && holding)
user.investigate_log("started a transfer into [holding].", INVESTIGATE_ATMOS)
-/obj/machinery/portable_atmospherics/scrubber/ui_act(action, params)
+/obj/machinery/portable_atmospherics/scrubber/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -166,7 +190,7 @@
suppress_reactions = !suppress_reactions
SSair.start_processing_machine(src)
message_admins("[ADMIN_LOOKUPFLW(usr)] turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.")
- usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.")
+ usr.investigate_log("turned [suppress_reactions ? "on" : "off"] the [src] reaction suppression.", INVESTIGATE_ATMOS)
. = TRUE
update_appearance()
@@ -210,7 +234,8 @@
if(!holding)
var/turf/T = get_turf(src)
for(var/turf/AT in T.get_atmos_adjacent_turfs(alldir = TRUE))
- scrub(AT.return_air())
+ if(scrub(AT.return_air()))
+ AT.air_update_turf(FALSE, FALSE)
return ..()
diff --git a/code/modules/cargo/bounties/atmos.dm b/code/modules/cargo/bounties/atmos.dm
index 16f52c459acc..76e8dcdf087c 100644
--- a/code/modules/cargo/bounties/atmos.dm
+++ b/code/modules/cargo/bounties/atmos.dm
@@ -13,9 +13,14 @@
return FALSE
var/obj/item/tank/applied_tank = applied_obj
var/datum/gas_mixture/our_mix = applied_tank.return_air()
- if(!our_mix.gases[gas_type])
+ if(!our_mix.moles[gas_type])
return FALSE
- return our_mix.gases[gas_type][MOLES] >= moles_required
+ return our_mix.moles[gas_type] >= moles_required
+
+// /datum/bounty/item/atmospherics/contribution_amount(obj/shipped)
+// var/obj/item/tank/shipped_tank = shipped
+// var/datum/gas_mixture/our_mix = shipped_tank.return_air()
+// return our_mix.moles[gas_type]
/datum/bounty/item/atmospherics/pluox_tank
name = "Full Tank of Pluoxium"
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index 1871d69eb452..c196ba01327d 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -101,6 +101,9 @@
unit_name = "security barrier"
export_types = list(/obj/item/grenade/barrier, /obj/structure/barricade/security)
+///Maximum number of credits you can earn from selling your gas canister cause its theoritically infinite
+#define MAX_GAS_CREDITS 15000
+
/**
* Gas canister exports.
* I'm going to put a quick aside here as this has been a pain to balance for several years now, and I'd like to at least break how to keep gas exports tame.
@@ -120,32 +123,22 @@
export_types = list(/obj/machinery/portable_atmospherics/canister)
k_elasticity = 0.00033
-/datum/export/large/gas_canister/get_cost(obj/O)
- var/obj/machinery/portable_atmospherics/canister/C = O
+/datum/export/large/gas_canister/get_cost(obj/machinery/portable_atmospherics/canister/canister)
+ var/datum/gas_mixture/canister_mix = canister.return_air()
+ if(!canister_mix.total_moles())
+ return 0
+ var/cached_moles = canister_mix.moles
+
var/worth = cost
- var/datum/gas_mixture/canister_mix = C.return_air()
- var/canister_gas = canister_mix.gases
- var/list/gases_to_check = list(
- /datum/gas/bz,
- /datum/gas/nitrium,
- /datum/gas/hypernoblium,
- /datum/gas/miasma,
- /datum/gas/tritium,
- /datum/gas/pluoxium,
- /datum/gas/freon,
- /datum/gas/hydrogen,
- /datum/gas/healium,
- /datum/gas/proto_nitrate,
- /datum/gas/zauker,
- /datum/gas/helium,
- /datum/gas/antinoblium,
- /datum/gas/halon,
- )
-
- for(var/gasID in gases_to_check)
- canister_mix.assert_gas(gasID)
- if(canister_gas[gasID][MOLES] > 0)
- worth += get_gas_value(gasID, canister_gas[gasID][MOLES])
+ for(var/datum/gas/gas as anything in GLOB.meta_gas_info[META_GAS_ID])
+ if(!(initial(gas.cargo_flags) & GAS_EXPORTABLE))
+ continue
+ canister_mix.assert_gas(gas)
+ if(cached_moles[gas] > 0)
+ worth += get_gas_value(gas, cached_moles[gas])
+ if(worth > MAX_GAS_CREDITS)
+ worth = MAX_GAS_CREDITS
+ break
canister_mix.garbage_collect()
return worth
@@ -153,3 +146,5 @@
/datum/export/large/gas_canister/proc/get_gas_value(datum/gas/gasType, moles)
var/baseValue = initial(gasType.base_value)
return round((baseValue/k_elasticity) * (1 - NUM_E**(-1 * k_elasticity * moles)))
+
+#undef MAX_GAS_CREDITS
diff --git a/code/modules/cargo/packs/materials.dm b/code/modules/cargo/packs/materials.dm
index ba9a162698bb..0ece1a647303 100644
--- a/code/modules/cargo/packs/materials.dm
+++ b/code/modules/cargo/packs/materials.dm
@@ -83,19 +83,18 @@
// This is the amount of moles in a default canister
var/moleCount = (initial(fakeCanister.maximum_pressure) * initial(fakeCanister.filled)) * initial(fakeCanister.volume) / (R_IDEAL_GAS_EQUATION * T20C)
- for(var/gasType in GLOB.meta_gas_info)
- var/datum/gas/gas = gasType
- var/name = initial(gas.name)
- if(!initial(gas.purchaseable))
+ for(var/datum/gas/gas as anything in GLOB.meta_gas_info[META_GAS_ID])
+ if(!(initial(gas.cargo_flags) & GAS_PURCHASABLE))
continue
var/datum/supply_pack/materials/pack = new
- pack.name = "[name] Canister"
- pack.desc = "Contains a canister of [name]."
- if(initial(gas.dangerous))
+ var/canname = initial(gas.name)
+ pack.name = "[canname] Canister"
+ pack.desc = "Contains a canister of [canname]."
+ if(initial(gas.cargo_flags) & GAS_DANGEROUS)
pack.access = ACCESS_ATMOSPHERICS
pack.access_view = ACCESS_ATMOSPHERICS
- pack.crate_name = "[name] canister crate"
- pack.id = "[type]([name])"
+ pack.crate_name = "[canname] canister crate"
+ pack.id = "[type]([canname])"
pack.cost = cost + moleCount * initial(gas.base_value) * 1.6
pack.cost = CEILING(pack.cost, 10)
diff --git a/code/modules/clothing/masks/gas_filter.dm b/code/modules/clothing/masks/gas_filter.dm
index 58e518a8c4da..70ca3cab1c6f 100644
--- a/code/modules/clothing/masks/gas_filter.dm
+++ b/code/modules/clothing/masks/gas_filter.dm
@@ -67,29 +67,29 @@
var/danger_points = 0
- for(var/gas_id in breath.gases)
+ for(var/gas_id, amount in breath.moles)
if(gas_id in high_filtering_gases)
- if(breath.gases[gas_id][MOLES] > HIGH_FILTERING_MOLES)
- breath.gases[gas_id][MOLES] = max(breath.gases[gas_id][MOLES] - filter_strength_high * filter_efficiency * HIGH_FILTERING_RATIO, 0)
+ if(amount > HIGH_FILTERING_MOLES)
+ breath.set_gas(gas_id, max(amount - filter_strength_high * filter_efficiency * HIGH_FILTERING_RATIO, 0))
danger_points += 1
continue
- breath.gases[gas_id][MOLES] = max(breath.gases[gas_id][MOLES] - filter_strength_high * filter_efficiency * LOW_FILTERING_RATIO, 0)
+ breath.set_gas(gas_id, max(amount - filter_strength_high * filter_efficiency * LOW_FILTERING_RATIO, 0))
danger_points += 0.2
continue
if(gas_id in mid_filtering_gases)
- if(breath.gases[gas_id][MOLES] > MID_FILTERING_MOLES)
- breath.gases[gas_id][MOLES] = max(breath.gases[gas_id][MOLES] - filter_strength_mid * filter_efficiency * HIGH_FILTERING_RATIO, 0)
+ if(amount > MID_FILTERING_MOLES)
+ breath.set_gas(gas_id, max(amount - filter_strength_mid * filter_efficiency * HIGH_FILTERING_RATIO, 0))
danger_points += 1.25
continue
- breath.gases[gas_id][MOLES] = max(breath.gases[gas_id][MOLES] - filter_strength_mid * filter_efficiency * LOW_FILTERING_RATIO, 0)
+ breath.set_gas(gas_id, max(amount - filter_strength_mid * filter_efficiency * LOW_FILTERING_RATIO, 0))
danger_points += 0.25
continue
if(gas_id in low_filtering_gases)
- if(breath.gases[gas_id][MOLES] > LOW_FILTERING_MOLES)
- breath.gases[gas_id][MOLES] = max(breath.gases[gas_id][MOLES] - filter_strength_low * filter_efficiency * HIGH_FILTERING_RATIO, 0)
+ if(amount > LOW_FILTERING_MOLES)
+ breath.set_gas(gas_id, max(amount - filter_strength_low * filter_efficiency * HIGH_FILTERING_RATIO, 0))
danger_points += 1.5
continue
- breath.gases[gas_id][MOLES] = max(breath.gases[gas_id][MOLES] - filter_strength_low * filter_efficiency * LOW_FILTERING_RATIO, 0)
+ breath.set_gas(gas_id, max(amount - filter_strength_low * filter_efficiency * LOW_FILTERING_RATIO, 0))
danger_points += 0.5
continue
diff --git a/code/modules/events/space_vines/vine_mutations.dm b/code/modules/events/space_vines/vine_mutations.dm
index b1466a4d0851..b7a625230a75 100644
--- a/code/modules/events/space_vines/vine_mutations.dm
+++ b/code/modules/events/space_vines/vine_mutations.dm
@@ -229,69 +229,58 @@
holder.light_state = PASS_LIGHT
holder.alpha = 125
-/datum/spacevine_mutation/oxy_eater
+/datum/spacevine_mutation/gas_eater
+ abstract_type = /datum/spacevine_mutation/gas_eater
+ /// Type of gas consumed by this mutation
+ var/datum/gas/gas_type = null
+
+/datum/spacevine_mutation/gas_eater/process_mutation(obj/structure/spacevine/holder)
+ if(isnull(gas_type))
+ stack_trace("gas_type not set for gas_eater mutation [type]")
+ return
+
+ var/turf/open/floor/turf = holder.loc
+ if(!istype(turf))
+ return
+
+ var/datum/gas_mixture/gas_mix = turf.air
+ if(!gas_mix.moles[gas_type])
+ return
+
+ gas_mix.set_gas(gas_type, max(gas_mix.moles[gas_type] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.growth_stage, 0))
+ gas_mix.garbage_collect()
+
+/datum/spacevine_mutation/gas_eater/oxy_eater
name = "Oxygen consuming"
description = "Consumes Oxygen from the surrounding area."
hue = "#28B5B5"
severity = SEVERITY_AVERAGE
quality = NEGATIVE
+ gas_type = /datum/gas/oxygen
-/datum/spacevine_mutation/oxy_eater/process_mutation(obj/structure/spacevine/holder)
- var/turf/open/floor/turf = holder.loc
- if(istype(turf))
- var/datum/gas_mixture/gas_mix = turf.air
- if(!gas_mix.gases[/datum/gas/oxygen])
- return
- gas_mix.gases[/datum/gas/oxygen][MOLES] = max(gas_mix.gases[/datum/gas/oxygen][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.growth_stage, 0)
- gas_mix.garbage_collect()
-
-/datum/spacevine_mutation/nitro_eater
+/datum/spacevine_mutation/gas_eater/nitro_eater
name = "Nitrogen consuming"
description = "Consumes Nitrogen from the surrounding area."
hue = "#FF7B54"
severity = SEVERITY_AVERAGE
quality = NEGATIVE
+ gas_type = /datum/gas/nitrogen
-/datum/spacevine_mutation/nitro_eater/process_mutation(obj/structure/spacevine/holder)
- var/turf/open/floor/turf = holder.loc
- if(istype(turf))
- var/datum/gas_mixture/gas_mix = turf.air
- if(!gas_mix.gases[/datum/gas/nitrogen])
- return
- gas_mix.gases[/datum/gas/nitrogen][MOLES] = max(gas_mix.gases[/datum/gas/nitrogen][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.growth_stage, 0)
- gas_mix.garbage_collect()
-
-/datum/spacevine_mutation/carbondioxide_eater
+/datum/spacevine_mutation/gas_eater/carbondioxide_eater
name = "CO2 consuming"
description = "Consumes Carbon Dioxide from the surrounding area."
hue = "#798777"
severity = SEVERITY_MINOR
quality = POSITIVE
+ gas_type = /datum/gas/carbon_dioxide
-/datum/spacevine_mutation/carbondioxide_eater/process_mutation(obj/structure/spacevine/holder)
- var/turf/open/floor/turf = holder.loc
- if(istype(turf))
- var/datum/gas_mixture/gas_mix = turf.air
- if(!gas_mix.gases[/datum/gas/carbon_dioxide])
- return
- gas_mix.gases[/datum/gas/carbon_dioxide][MOLES] = max(gas_mix.gases[/datum/gas/carbon_dioxide][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.growth_stage, 0)
- gas_mix.garbage_collect()
-
-/datum/spacevine_mutation/plasma_eater
+/datum/spacevine_mutation/gas_eater/plasma_eater
name = "Plasma consuming"
description = "Consumes Plasma from the surrounding area."
hue = "#9074b6"
severity = SEVERITY_AVERAGE
quality = POSITIVE
-
-/datum/spacevine_mutation/plasma_eater/process_mutation(obj/structure/spacevine/holder)
- var/turf/open/floor/turf = holder.loc
- if(istype(turf))
- var/datum/gas_mixture/gas_mix = turf.air
- if(!gas_mix.gases[/datum/gas/plasma])
- return
- gas_mix.gases[/datum/gas/plasma][MOLES] = max(gas_mix.gases[/datum/gas/plasma][MOLES] - GAS_MUTATION_REMOVAL_MULTIPLIER * holder.growth_stage, 0)
- gas_mix.garbage_collect()
+ gas_type = /datum/gas/plasma
/datum/spacevine_mutation/thorns
name = "Thorny"
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index 89dbfe0abf68..4841ee16ccba 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -89,7 +89,7 @@
sanitized_misc = TRUE
sanitized_reactions = TRUE
require_all = FALSE
- required_reactions = list(/datum/gas_reaction/h2fire, /datum/gas_reaction/tritfire)
+ required_reactions = list(/datum/gas_reaction/standard/h2fire, /datum/gas_reaction/standard/tritfire)
/datum/experiment/ordnance/explosive/nobliumbomb
name = "Noblium Explosives"
@@ -99,7 +99,7 @@
experiment_proper = TRUE
sanitized_misc = TRUE
sanitized_reactions = TRUE
- required_reactions = list(/datum/gas_reaction/nobliumformation)
+ required_reactions = list(/datum/gas_reaction/standard/nobliumformation)
/datum/experiment/ordnance/explosive/pressurebomb
name = "Reactionless Explosives"
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index 4513f51b414e..82cc5772d1d9 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -1007,7 +1007,7 @@
var/datum/gas_mixture/mixture = loc.return_air()
if(!mixture)
return FALSE
- if(safe_air_limits && !check_gases(mixture.gases, safe_air_limits))
+ if(safe_air_limits && !mixture.check_gases(safe_air_limits))
return FALSE
if(!ISINRANGE(mixture.temperature, required_temperature_min, required_temperature_max))
return FALSE
diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm
index 14ed12becf46..570aa5749988 100644
--- a/code/modules/fishing/fish/fish_traits.dm
+++ b/code/modules/fishing/fish/fish_traits.dm
@@ -293,8 +293,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
return
var/datum/gas_mixture/stench = new
- ADD_GAS(/datum/gas/miasma, stench.gases)
- stench.gases[/datum/gas/miasma][MOLES] = MIASMA_CORPSE_MOLES * 2 * seconds_per_tick
+
+ stench.set_gas(/datum/gas/miasma, MIASMA_CORPSE_MOLES * 2 * seconds_per_tick)
stench.temperature = mob.body_temperature
our_turf.assume_air(stench)
diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/dreaming.dm
similarity index 91%
rename from code/modules/flufftext/Dreaming.dm
rename to code/modules/flufftext/dreaming.dm
index 15d9a80578ea..3beaf44eb90f 100644
--- a/code/modules/flufftext/Dreaming.dm
+++ b/code/modules/flufftext/dreaming.dm
@@ -22,9 +22,16 @@
/mob/living/carbon/proc/dream()
set waitfor = FALSE
- var/datum/dream/chosen_dream = pick_weight(GLOB.dreams)
+ var/list/dream_pool = list()
+
+ SEND_SIGNAL(src, COMSIG_PRE_DREAMING, dream_pool)
+ if(!length(dream_pool))
+ dream_pool = GLOB.dreams
+
+ var/datum/dream/chosen_dream = pick_weight(dream_pool)
ADD_TRAIT(src, TRAIT_DREAMING, DREAMING_SOURCE)
+ SEND_SIGNAL(src, COMSIG_START_DREAMING, chosen_dream)
dream_sequence(chosen_dream.GenerateDream(src), chosen_dream)
/**
@@ -39,9 +46,10 @@
*/
/mob/living/carbon/proc/dream_sequence(list/dream_fragments, datum/dream/current_dream)
- if(stat >= SOFT_CRIT || !HAS_TRAIT(src, TRAIT_KNOCKEDOUT))
+ if(!HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || stat >= HARD_CRIT)
REMOVE_TRAIT(src, TRAIT_DREAMING, DREAMING_SOURCE)
current_dream.OnDreamEnd(src)
+ SEND_SIGNAL(src, COMSIG_END_DREAMING, current_dream)
return
var/next_message = dream_fragments[1]
dream_fragments.Cut(1,2)
@@ -60,6 +68,7 @@
else
REMOVE_TRAIT(src, TRAIT_DREAMING, DREAMING_SOURCE)
current_dream.OnDreamEnd(src)
+ SEND_SIGNAL(src, COMSIG_END_DREAMING, current_dream)
//-------------------------
// DREAM DATUMS
@@ -101,12 +110,9 @@ GLOBAL_LIST_INIT(dreams, populate_dream_list())
weight = 1000
/datum/dream/random/GenerateDream(mob/living/carbon/dreamer)
- var/list/custom_dream_nouns = list()
+ var/list/custom_dream_nouns = get_dream_nouns(dreamer) || list()
var/fragment = ""
- for(var/obj/item/bedsheet/sheet in dreamer.loc)
- custom_dream_nouns += sheet.dream_messages
-
. = list()
. += "you see"
@@ -149,6 +155,12 @@ GLOBAL_LIST_INIT(dreams, populate_dream_list())
fragment = "\a [replacetext(fragment, "%A% ", "")]"
. += fragment
+/datum/dream/random/proc/get_dream_nouns(mob/living/carbon/dreamer)
+ var/list/custom_dream_nouns = list()
+ for(var/obj/item/bedsheet/sheet in dreamer.loc)
+ custom_dream_nouns += sheet.dream_messages
+ return custom_dream_nouns
+
/// Dream plays a random sound at you, chosen from all sounds in the folder
/datum/dream/hear_something
weight = 500
diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm
index 93b97aa60c69..7bfa8c46d58e 100644
--- a/code/modules/hydroponics/unique_plant_genes.dm
+++ b/code/modules/hydroponics/unique_plant_genes.dm
@@ -675,8 +675,8 @@
return
var/datum/gas_mixture/stank = new
- ADD_GAS(/datum/gas/miasma, stank.gases)
- stank.gases[/datum/gas/miasma][MOLES] = (seed.yield + 6) * 3.5 * MIASMA_CORPSE_MOLES * seconds_per_tick // this process is only being called about 2/7 as much as corpses so this is 12-32 times a corpses
+
+ stank.set_gas(/datum/gas/miasma, (seed.yield + 6) * 3.5 * MIASMA_CORPSE_MOLES * seconds_per_tick) // this process is only being called about 2/7 as much as corpses so this is 12-32 times a corpses
stank.temperature = T20C // without this the room would eventually freeze and miasma mining would be easier
tray_turf.assume_air(stank)
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 48c0b24d6179..e3fdf410b528 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -278,42 +278,40 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
log_combat(user, target_mob, "attacked", src)
/obj/item/book/bible/interact_with_atom(atom/bible_smacked, mob/living/user, list/modifiers)
+ if(!user.mind?.holy_role)
+ return
if(SEND_SIGNAL(bible_smacked, COMSIG_BIBLE_SMACKED, user) & COMSIG_END_BIBLE_CHAIN)
return ITEM_INTERACT_SUCCESS
if(isfloorturf(bible_smacked))
- if(user.mind?.holy_role)
- var/area/current_area = get_area(bible_smacked)
- if(!GLOB.chaplain_altars.len && istype(current_area, /area/station/service/chapel))
- make_new_altar(bible_smacked, user)
- return ITEM_INTERACT_SUCCESS
- for(var/obj/effect/rune/nearby_runes in range(2, user))
- nearby_runes.SetInvisibility(INVISIBILITY_NONE, id=type, priority=INVISIBILITY_PRIORITY_BASIC_ANTI_INVISIBILITY)
+ var/area/current_area = get_area(bible_smacked)
+ if(!GLOB.chaplain_altars.len && istype(current_area, /area/station/service/chapel))
+ make_new_altar(bible_smacked, user)
+ return ITEM_INTERACT_SUCCESS
+ for(var/obj/effect/rune/nearby_runes in range(2, user))
+ nearby_runes.SetInvisibility(INVISIBILITY_NONE, id=type, priority=INVISIBILITY_PRIORITY_BASIC_ANTI_INVISIBILITY)
bible_smacked.balloon_alert(user, "floor smacked!")
return ITEM_INTERACT_SUCCESS
- if(user.mind?.holy_role)
- if(bible_smacked.reagents?.has_reagent(/datum/reagent/water)) // blesses all the water in the holder
- bible_smacked.balloon_alert(user, "blessed")
- var/water2holy = bible_smacked.reagents.get_reagent_amount(/datum/reagent/water)
- bible_smacked.reagents.del_reagent(/datum/reagent/water)
- bible_smacked.reagents.add_reagent(/datum/reagent/water/holywater,water2holy)
- . = ITEM_INTERACT_SUCCESS
- if(bible_smacked.reagents?.has_reagent(/datum/reagent/fuel/unholywater)) // yeah yeah, copy pasted code - sue me
- bible_smacked.balloon_alert(user, "purified")
- var/unholy2holy = bible_smacked.reagents.get_reagent_amount(/datum/reagent/fuel/unholywater)
- bible_smacked.reagents.del_reagent(/datum/reagent/fuel/unholywater)
- bible_smacked.reagents.add_reagent(/datum/reagent/water/holywater,unholy2holy)
- . = ITEM_INTERACT_SUCCESS
- if(istype(bible_smacked, /obj/item/book/bible) && !istype(bible_smacked, /obj/item/book/bible/syndicate))
- bible_smacked.balloon_alert(user, "converted")
- var/obj/item/book/bible/other_bible = bible_smacked
- other_bible.name = name
- other_bible.icon_state = icon_state
- other_bible.inhand_icon_state = inhand_icon_state
- other_bible.deity_name = deity_name
- . = ITEM_INTERACT_SUCCESS
- if(.)
- return .
+ if(bible_smacked.reagents?.has_reagent(/datum/reagent/water)) // blesses all the water in the holder
+ bible_smacked.balloon_alert(user, "blessed")
+ var/water2holy = bible_smacked.reagents.get_reagent_amount(/datum/reagent/water)
+ bible_smacked.reagents.del_reagent(/datum/reagent/water)
+ bible_smacked.reagents.add_reagent(/datum/reagent/water/holywater,water2holy)
+ return ITEM_INTERACT_SUCCESS
+ if(bible_smacked.reagents?.has_reagent(/datum/reagent/fuel/unholywater)) // yeah yeah, copy pasted code - sue me
+ bible_smacked.balloon_alert(user, "purified")
+ var/unholy2holy = bible_smacked.reagents.get_reagent_amount(/datum/reagent/fuel/unholywater)
+ bible_smacked.reagents.del_reagent(/datum/reagent/fuel/unholywater)
+ bible_smacked.reagents.add_reagent(/datum/reagent/water/holywater,unholy2holy)
+ return ITEM_INTERACT_SUCCESS
+ if(istype(bible_smacked, /obj/item/book/bible) && !istype(bible_smacked, /obj/item/book/bible/syndicate))
+ bible_smacked.balloon_alert(user, "converted")
+ var/obj/item/book/bible/other_bible = bible_smacked
+ other_bible.name = name
+ other_bible.icon_state = icon_state
+ other_bible.inhand_icon_state = inhand_icon_state
+ other_bible.deity_name = deity_name
+ return ITEM_INTERACT_SUCCESS
if(istype(bible_smacked, /obj/item/cult_bastard) && !IS_CULTIST(user))
var/obj/item/cult_bastard/sword = bible_smacked
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm b/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm
index f38025cf8650..5e407401ee5e 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/gaseous.dm
@@ -149,7 +149,7 @@
return // We shouldn't even be registered at this point but just in case
var/datum/gas_mixture/mix_to_spawn = new()
mix_to_spawn.add_gas(active_gas)
- mix_to_spawn.gases[active_gas][MOLES] = possible_gases[active_gas] * seconds_per_tick
+ mix_to_spawn.moles[active_gas] = possible_gases[active_gas] * seconds_per_tick
mix_to_spawn.temperature = T20C
var/turf/open/our_turf = get_turf(owner)
our_turf.assume_air(mix_to_spawn)
diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
index 86b2ef3d9508..a7cfb6733e8c 100644
--- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
+++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
@@ -108,9 +108,9 @@
/mob/living/basic/regal_rat/handle_environment(datum/gas_mixture/environment)
. = ..()
- if(stat == DEAD || isnull(environment) || isnull(environment.gases[/datum/gas/miasma]))
+ if(stat == DEAD || isnull(environment) || isnull(environment.moles[/datum/gas/miasma]))
return
- var/miasma_percentage = environment.gases[/datum/gas/miasma][MOLES] / environment.total_moles()
+ var/miasma_percentage = environment.moles[/datum/gas/miasma] / environment.total_moles()
if(miasma_percentage >= 0.25)
heal_bodypart_damage(1)
diff --git a/code/modules/mob/living/basic/tree.dm b/code/modules/mob/living/basic/tree.dm
index 4fd30a983b2b..47794ea57eb0 100644
--- a/code/modules/mob/living/basic/tree.dm
+++ b/code/modules/mob/living/basic/tree.dm
@@ -66,13 +66,13 @@
if(!isopenturf(loc))
return
var/turf/open/our_turf = src.loc
- if(!our_turf.air || !our_turf.air.gases[/datum/gas/carbon_dioxide])
+ if(!our_turf.air || !our_turf.air.moles[/datum/gas/carbon_dioxide])
return
-
- var/co2 = our_turf.air.gases[/datum/gas/carbon_dioxide][MOLES]
+ var/datum/gas_mixture/our_air = our_turf.air
+ var/co2 = our_air.moles[/datum/gas/carbon_dioxide]
if(co2 > 0 && SPT_PROB(13, seconds_per_tick))
var/amt = min(co2, 9)
- our_turf.air.gases[/datum/gas/carbon_dioxide][MOLES] -= amt
+ our_air.adjust_gas(/datum/gas/carbon_dioxide, -amt)
our_turf.atmos_spawn_air("[GAS_O2]=[amt]")
/mob/living/basic/tree/melee_attack(atom/target, list/modifiers, ignore_cooldown = FALSE)
diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm
index 1d0cafeb8049..e89bb872dd94 100644
--- a/code/modules/mob/living/carbon/alien/life.dm
+++ b/code/modules/mob/living/carbon/alien/life.dm
@@ -16,25 +16,25 @@
var/plasma_used = 0
var/plas_detect_threshold = 0.02
var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
- var/list/breath_gases = breath.gases
+ var/list/breath_moles = breath.moles
breath.assert_gases(/datum/gas/plasma, /datum/gas/oxygen)
//Partial pressure of the plasma in our breath
- var/Plasma_pp = (breath_gases[/datum/gas/plasma][MOLES]/breath.total_moles())*breath_pressure
+ var/plasma_pp = (breath_moles[/datum/gas/plasma] / breath.total_moles()) * breath_pressure
- if(Plasma_pp > plas_detect_threshold) // Detect plasma in air
- adjustPlasma(breath_gases[/datum/gas/plasma][MOLES]*250)
+ if(plasma_pp > plas_detect_threshold) // Detect plasma in air
+ adjustPlasma(breath_moles[/datum/gas/plasma] * 250)
throw_alert(ALERT_XENO_PLASMA, /atom/movable/screen/alert/alien_plas)
- plasma_used = breath_gases[/datum/gas/plasma][MOLES]
+ plasma_used = breath_moles[/datum/gas/plasma]
else
clear_alert(ALERT_XENO_PLASMA)
//Breathe in plasma and out oxygen
- breath_gases[/datum/gas/plasma][MOLES] -= plasma_used
- breath_gases[/datum/gas/oxygen][MOLES] += plasma_used
+ breath_moles[/datum/gas/plasma] -= plasma_used
+ breath_moles[/datum/gas/oxygen] += plasma_used
breath.garbage_collect()
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 2b5330ce2d71..9d5a303053cd 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -82,13 +82,14 @@
* * drop_items - Should the mob drop their items before dusting?
* * force - Should this mob be FORCABLY dusted?
*/
-/mob/living/proc/dust(just_ash, drop_items, force)
+/mob/living/proc/dust(just_ash, drop_items, give_moodlet = TRUE, force)
if(body_position == STANDING_UP)
// keep us upright so the animation fits.
ADD_TRAIT(src, TRAIT_FORCED_STANDING, TRAIT_GENERIC)
death(TRUE, "being vaporized")
- send_death_moodlets(dusted = TRUE)
+ if(give_moodlet)
+ send_death_moodlets(dusted = TRUE)
if(drop_items)
unequip_everything()
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index ca23f14f25da..033b378b47a9 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -301,13 +301,13 @@
if(isturf(loc) && isopenturf(loc))
var/turf/open/ST = loc
if(ST.air)
- var/ST_gases = ST.air.gases
+ var/ST_moles = ST.air.moles
ST.air.assert_gases(/datum/gas/oxygen, /datum/gas/pluoxium, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, /datum/gas/plasma)
- var/plas = ST_gases[/datum/gas/plasma][MOLES]
- var/oxy = ST_gases[/datum/gas/oxygen][MOLES] + (ST_gases[/datum/gas/pluoxium][MOLES] * PLUOXIUM_PROPORTION)
- var/n2 = ST_gases[/datum/gas/nitrogen][MOLES]
- var/co2 = ST_gases[/datum/gas/carbon_dioxide][MOLES]
+ var/plas = ST_moles[/datum/gas/plasma]
+ var/oxy = ST_moles[/datum/gas/oxygen] + (ST_moles[/datum/gas/pluoxium] * PLUOXIUM_PROPORTION)
+ var/n2 = ST_moles[/datum/gas/nitrogen]
+ var/co2 = ST_moles[/datum/gas/carbon_dioxide]
ST.air.garbage_collect()
diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm
index 328431b0db99..9e13c1f04bed 100644
--- a/code/modules/mob/living/simple_animal/slime/life.dm
+++ b/code/modules/mob/living/simple_animal/slime/life.dm
@@ -48,9 +48,7 @@
REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, SLIME_COLD)
if(stat != DEAD)
- var/bz_percentage =0
- if(environment.gases[/datum/gas/bz])
- bz_percentage = environment.gases[/datum/gas/bz][MOLES] / environment.total_moles()
+ var/bz_percentage = environment.moles[/datum/gas/bz] / environment.total_moles()
var/stasis = (bz_percentage >= 0.05 && body_temperature < (T0C + 100)) || force_stasis
switch(stat)
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 26ffa76dfdf7..20c534ca9466 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -201,10 +201,10 @@
var/t = "[span_notice("Coordinates: [x],[y] ")]\n"
t += "[span_danger("Temperature: [environment.temperature] ")]\n"
- for(var/id in environment.gases)
- var/gas = environment.gases[id]
- if(gas[MOLES])
- t+="[span_notice("[gas[GAS_META][META_GAS_NAME]]: [gas[MOLES]] ")]\n"
+ var/list/cached_gas_name = GAS_META[META_GAS_NAME]
+ for(var/gas_id, gas_moles in environment.moles)
+ if(gas_moles)
+ t += "[span_notice("[cached_gas_name[gas_id]]: [gas_moles] ")]\n"
to_chat(usr, t)
diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm
index a313a02cbcde..951346515ffe 100644
--- a/code/modules/mob/status_procs.dm
+++ b/code/modules/mob/status_procs.dm
@@ -125,7 +125,8 @@
. = CELCIUS_TO_KELVIN(skin_temp)
// and if we're on fire just add a flat amount of heat
if(on_fire)
- . += fire_stacks ** 2 KELVIN
+ var/fire_heat = (fire_stacks ** 2) KELVIN
+ . += fire_heat * (1 - get_insulation(area_temperature + fire_heat))
return .
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
index 2d43b3d46ec2..cdf03beb188c 100644
--- a/code/modules/mod/modules/modules_maint.dm
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -67,7 +67,7 @@
var/turf/wearer_turf = get_turf(src)
var/datum/gas_mixture/air = wearer_turf.return_air()
- if(!(air.gases[/datum/gas/water_vapor] && (air.gases[/datum/gas/water_vapor][MOLES]) >= 5))
+ if(air.moles[/datum/gas/water_vapor] < 5)
return //return if there aren't more than 5 Moles of Water Vapor in the air
snap_signal()
diff --git a/code/modules/mod/modules/modules_timeline.dm b/code/modules/mod/modules/modules_timeline.dm
index d2a2e86d2744..86c7eee70682 100644
--- a/code/modules/mod/modules/modules_timeline.dm
+++ b/code/modules/mod/modules/modules_timeline.dm
@@ -415,9 +415,8 @@
/obj/structure/chrono_field/return_air() //we always have nominal air and temperature
var/datum/gas_mixture/fresh_air = new
- fresh_air.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
- fresh_air.gases[/datum/gas/oxygen][MOLES] = MOLES_O2STANDARD
- fresh_air.gases[/datum/gas/nitrogen][MOLES] = MOLES_N2STANDARD
+ fresh_air.set_gas(/datum/gas/oxygen, MOLES_O2STANDARD)
+ fresh_air.set_gas(/datum/gas/nitrogen, MOLES_N2STANDARD)
fresh_air.temperature = T20C
return fresh_air
diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm
index 7e2608728597..f726a079620c 100644
--- a/code/modules/modular_computers/file_system/programs/atmosscan.dm
+++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm
@@ -63,7 +63,6 @@
var/datum/gas_mixture/air = turf?.return_air()
data["gasmixes"] = list(gas_mixture_parser(air, "Location Reading"))
if(ATMOZPHERE_SCAN_CLICK)
- LAZYINITLIST(last_gasmix_data)
data["gasmixes"] = last_gasmix_data
return data
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index efb3aef85613..1da2a474651b 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -236,7 +236,7 @@
/obj/item/stock_parts/power_store/cell/crystal_cell
name = "crystal power cell"
- desc = "A very high power cell made from crystallized plasma"
+ desc = "A high power cell made from crystallized plasma."
icon_state = "crystal_cell"
maxcharge = STANDARD_CELL_CHARGE * 50
chargerate = 0
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index ab1a6f6e2811..d2f5d043cf9f 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -293,7 +293,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
// Extra effects should always fire after the compositions are all finished
// Some extra effects like [/datum/sm_gas/carbon_dioxide/extra_effects]
// needs more than one gas and rely on a fully parsed gas_percentage.
- for (var/gas_path in absorbed_gasmix.gases)
+ for (var/gas_path in absorbed_gasmix.moles)
var/datum/sm_gas/sm_gas = current_gas_behavior[gas_path]
sm_gas?.extra_effects(src)
@@ -341,8 +341,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
merged_gasmix.temperature += device_energy * waste_multiplier / THERMAL_RELEASE_MODIFIER
merged_gasmix.temperature = clamp(merged_gasmix.temperature, TCMB, 2500 * waste_multiplier)
merged_gasmix.assert_gases(/datum/gas/plasma, /datum/gas/oxygen)
- merged_gasmix.gases[/datum/gas/plasma][MOLES] += max(device_energy * waste_multiplier / PLASMA_RELEASE_MODIFIER, 0)
- merged_gasmix.gases[/datum/gas/oxygen][MOLES] += max(((device_energy + merged_gasmix.temperature * waste_multiplier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0)
+ merged_gasmix.moles[/datum/gas/plasma] += max(device_energy * waste_multiplier / PLASMA_RELEASE_MODIFIER, 0)
+ merged_gasmix.moles[/datum/gas/oxygen] += max(((device_energy + merged_gasmix.temperature * waste_multiplier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0)
merged_gasmix.garbage_collect()
env.merge(merged_gasmix)
air_update_turf(FALSE, FALSE)
@@ -664,8 +664,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
var/total_moles = absorbed_gasmix.total_moles()
if(total_moles < MINIMUM_MOLE_COUNT) //it's not worth processing small amounts like these, total_moles can also be 0 in vacuume
return
- for (var/gas_path in absorbed_gasmix.gases)
- var/mole_count = absorbed_gasmix.gases[gas_path][MOLES]
+ for (var/gas_path, mole_count in absorbed_gasmix.moles)
if(mole_count < MINIMUM_MOLE_COUNT) //save processing power from small amounts like these
continue
gas_percentage[gas_path] = mole_count / total_moles
diff --git a/code/modules/power/supermatter/supermatter_gas.dm b/code/modules/power/supermatter/supermatter_gas.dm
index 35b9db0731f9..9f26e632a3d6 100644
--- a/code/modules/power/supermatter/supermatter_gas.dm
+++ b/code/modules/power/supermatter/supermatter_gas.dm
@@ -105,18 +105,18 @@ GLOBAL_LIST_INIT(sm_gas_behavior, init_sm_gas())
return
var/co2_pp = sm.absorbed_gasmix.return_pressure() * sm.gas_percentage[/datum/gas/carbon_dioxide]
var/co2_ratio = clamp((1/2 * (co2_pp - CO2_CONSUMPTION_PP) / (co2_pp + CO2_PRESSURE_SCALING)), 0, 1)
- var/consumed_co2 = sm.absorbed_gasmix.gases[/datum/gas/carbon_dioxide][MOLES] * co2_ratio
+ var/consumed_co2 = sm.absorbed_gasmix.moles[/datum/gas/carbon_dioxide] * co2_ratio
consumed_co2 = min(
consumed_co2,
- sm.absorbed_gasmix.gases[/datum/gas/carbon_dioxide][MOLES],
- sm.absorbed_gasmix.gases[/datum/gas/oxygen][MOLES]
+ sm.absorbed_gasmix.moles[/datum/gas/carbon_dioxide],
+ sm.absorbed_gasmix.moles[/datum/gas/oxygen]
)
if(!consumed_co2)
return
- sm.absorbed_gasmix.gases[/datum/gas/carbon_dioxide][MOLES] -= consumed_co2
- sm.absorbed_gasmix.gases[/datum/gas/oxygen][MOLES] -= consumed_co2
- ASSERT_GAS(/datum/gas/pluoxium, sm.absorbed_gasmix)
- sm.absorbed_gasmix.gases[/datum/gas/pluoxium][MOLES] += consumed_co2
+ sm.absorbed_gasmix.adjust_gas(/datum/gas/carbon_dioxide, -consumed_co2)
+ sm.absorbed_gasmix.adjust_gas(/datum/gas/oxygen, -consumed_co2)
+
+ sm.absorbed_gasmix.adjust_gas(/datum/gas/pluoxium, consumed_co2)
/datum/sm_gas/plasma
gas_path = /datum/gas/plasma
@@ -175,10 +175,10 @@ GLOBAL_LIST_INIT(sm_gas_behavior, init_sm_gas())
return
var/miasma_pp = sm.absorbed_gasmix.return_pressure() * sm.gas_percentage[/datum/gas/miasma]
var/miasma_ratio = clamp(((miasma_pp - MIASMA_CONSUMPTION_PP) / (miasma_pp + MIASMA_PRESSURE_SCALING)) * (1 + (sm.gas_heat_power_generation * MIASMA_GASMIX_SCALING)), 0, 1)
- var/consumed_miasma = sm.absorbed_gasmix.gases[/datum/gas/miasma][MOLES] * miasma_ratio
+ var/consumed_miasma = sm.absorbed_gasmix.moles[/datum/gas/miasma] * miasma_ratio
if(!consumed_miasma)
return
- sm.absorbed_gasmix.gases[/datum/gas/miasma][MOLES] -= consumed_miasma
+ sm.absorbed_gasmix.adjust_gas(/datum/gas/miasma, -consumed_miasma)
sm.external_power_trickle += consumed_miasma * MIASMA_POWER_GAIN
sm.log_activation("miasma absorption")
diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm
index f33b0725734c..9163645d68c6 100644
--- a/code/modules/reagents/chemistry/recipes/others.dm
+++ b/code/modules/reagents/chemistry/recipes/others.dm
@@ -37,7 +37,7 @@
/datum/chemical_reaction/sodiumchloride
results = list(/datum/reagent/consumable/salt = 2)
required_reagents = list(/datum/reagent/sodium = 1, /datum/reagent/chlorine = 1) // That's what I said! Sodium Chloride!
- reaction_tags = REACTION_TAG_EASY | REACTION_TAG_FOOD | REACTION_TAG_COMPONENT
+ reaction_tags = REACTION_TAG_EASY | REACTION_TAG_CHEMICAL | REACTION_TAG_COMPONENT
required_other = TRUE
/datum/chemical_reaction/sodiumchloride/pre_reaction_other_checks(datum/reagents/holder)
diff --git a/code/modules/religion/deaconize.dm b/code/modules/religion/deaconize.dm
new file mode 100644
index 000000000000..826787bdd16c
--- /dev/null
+++ b/code/modules/religion/deaconize.dm
@@ -0,0 +1,91 @@
+/**
+ * Deaconize
+ * Makes a sentient, non-cult member of the station into a Holy person, able to use bibles & other chap gear.
+ * Is a one-time use ability, given to all sects that don't have their own variation of it.
+ */
+/datum/religion_rites/deaconize
+ name = "Deaconize"
+ desc = "Converts someone to your sect. They must be willing, so the first invocation will instead prompt them to join. \
+ They will gain the same holy abilities as you, this is a one-time use so make sure they are worthy!"
+ ritual_length = 30 SECONDS
+ ritual_invocations = list(
+ "A good, honorable person has been brought here by faith ...",
+ "With their hands ready to serve ...",
+ "Heart ready to listen ...",
+ "And soul ready to follow ...",
+ "May we offer our own hand in return ..."
+ )
+ invoke_msg = "And use them to the best of our abilities."
+ rite_flags = RITE_ALLOW_MULTIPLE_PERFORMS | RITE_ONE_TIME_USE
+
+ ///The person currently being deaconized.
+ var/mob/living/carbon/human/potential_deacon
+
+/datum/religion_rites/deaconize/Destroy()
+ potential_deacon = null
+ return ..()
+
+/datum/religion_rites/deaconize/perform_rite(mob/living/user, atom/religious_tool)
+ if(!ismovable(religious_tool))
+ to_chat(user, span_warning("This rite requires a religious device that individuals can be buckled to."))
+ return FALSE
+ var/atom/movable/movable_reltool = religious_tool
+ if(!movable_reltool)
+ return FALSE
+ var/mob/living/carbon/human/possible_deacon = locate() in movable_reltool.buckled_mobs
+ if(!possible_deacon)
+ to_chat(user, span_warning("Nothing is buckled to the [movable_reltool]!"))
+ return FALSE
+ if(!is_valid_for_deacon(possible_deacon, user))
+ return FALSE
+ //no one invited or this is not the invited person
+ if(!potential_deacon || (possible_deacon != potential_deacon))
+ INVOKE_ASYNC(src, PROC_REF(invite_deacon), possible_deacon)
+ to_chat(user, span_notice("They have been offered the oppertunity to join our ranks. Wait for them to decide and try again."))
+ return FALSE
+ return ..()
+
+/datum/religion_rites/deaconize/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
+ . = ..()
+ if(!(potential_deacon in religious_tool.buckled_mobs)) //checks one last time if the right corpse is still buckled
+ to_chat(user, span_warning("[potential_deacon] is no longer on the altar!"))
+ return FALSE
+ if(potential_deacon.stat != CONSCIOUS)
+ to_chat(user, span_warning("[potential_deacon] has to be conscious for the rite to work!"))
+ return FALSE
+ if(!potential_deacon.mind)
+ to_chat(user, span_warning("[potential_deacon]'s mind appears to be elsewhere!"))
+ return FALSE
+ if(IS_CULTIST(potential_deacon))//what the fuck?!
+ to_chat(user, span_warning("[GLOB.deity] has seen a true, dark evil in [potential_deacon]'s heart, and they have been smitten!"))
+ playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
+ potential_deacon.gib(DROP_ORGANS|DROP_BODYPARTS)
+ return FALSE
+ var/datum/brain_trauma/special/honorbound/honor = user.has_trauma_type(/datum/brain_trauma/special/honorbound)
+ if(honor && (potential_deacon in honor.guilty))
+ honor.guilty -= potential_deacon
+ to_chat(user, span_notice("[GLOB.deity] has bound [potential_deacon] to the code! They are now a holy role! (albeit the lowest level of such)"))
+ potential_deacon.mind.holy_role = HOLY_ROLE_DEACON
+ GLOB.religious_sect.on_conversion(potential_deacon)
+ playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
+ return TRUE
+
+///Helper if the passed possible_deacon is valid to become a deacon or not.
+/datum/religion_rites/deaconize/proc/is_valid_for_deacon(mob/living/carbon/human/possible_deacon, mob/living/user)
+ if(possible_deacon.stat != CONSCIOUS)
+ to_chat(user, span_warning("[possible_deacon] needs to be alive and conscious to join!"))
+ return FALSE
+ if(possible_deacon.mind && possible_deacon.mind.holy_role)
+ to_chat(user, span_warning("[possible_deacon] is already a member of the religion!"))
+ return FALSE
+ return TRUE
+
+/**
+ * Async proc that waits for a response on joining the sect.
+ * If they accept, the deaconize rite can now recruit them instead of just offering more invites.
+ */
+/datum/religion_rites/deaconize/proc/invite_deacon(mob/living/carbon/human/invited)
+ var/ask = tgui_alert(invited, "Join [GLOB.deity]? You will be expected to follow the Chaplain's order.", "Invitation", list("Yes", "No"), 60 SECONDS)
+ if(ask != "Yes")
+ return
+ potential_deacon = invited
diff --git a/code/modules/religion/dreams/banish_nightmare.dm b/code/modules/religion/dreams/banish_nightmare.dm
new file mode 100644
index 000000000000..889ad2d5a824
--- /dev/null
+++ b/code/modules/religion/dreams/banish_nightmare.dm
@@ -0,0 +1,100 @@
+/datum/religion_rites/banish_nightmare
+ name = "Banish Nightmare"
+ desc = "Banish the corpse of a Nightmare or its heart back from whence it came, protecting the dreams of \
+ the station and earning favor. If a heart is present, you will be rewarded with a special blessing."
+ favor_cost = 0
+ ritual_length = 20 SECONDS
+
+/datum/religion_rites/banish_nightmare/New()
+ . = ..()
+ ritual_invocations = list(
+ "We have bested a terrible Nightmare that plagued our station!..",
+ "With the power of [GLOB.deity], we cast it out!..",
+ "This invader of dreams has no place here...",
+ "May it trouble our flock no longer.",
+ )
+
+/datum/religion_rites/banish_nightmare/perform_rite(mob/living/user, atom/religious_tool)
+ var/has_nightmare = FALSE
+ for(var/mob/living/carbon/human/nightmare in get_turf(religious_tool))
+ if(isnightmare(nightmare))
+ has_nightmare = TRUE
+ break
+
+ for(var/obj/item/organ/organ in get_turf(religious_tool))
+ if(istype(organ, /obj/item/organ/heart/nightmare))
+ has_nightmare = TRUE
+ break
+
+ if(!has_nightmare)
+ to_chat(user, span_warning("There is no corpse or heart of a Nightmare to banish!"))
+ return FALSE
+
+ return ..()
+
+/datum/religion_rites/banish_nightmare/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ var/favor = 0
+ var/give_heart = FALSE
+ for(var/mob/living/carbon/human/nightmare in get_turf(religious_tool))
+ if(!isnightmare(nightmare))
+ continue
+
+ if(istype(nightmare.get_organ_slot(ORGAN_SLOT_HEART), /obj/item/organ/heart/nightmare))
+ give_heart += 1
+ favor += 100
+
+ nightmare.dust(just_ash = TRUE, drop_items = TRUE, give_moodlet = FALSE, force =TRUE)
+ favor += 200
+
+ for(var/obj/item/organ/organ in get_turf(religious_tool))
+ if(!istype(organ, /obj/item/organ/heart/nightmare))
+ continue
+
+ qdel(organ)
+ favor += 100
+ give_heart += 1
+
+ if(favor <= 0)
+ CRASH("Banish nightmare rite invoked without finding a nightmare or nightmare heart to banish.")
+
+ GLOB.religious_sect.adjust_favor(favor, user)
+ if(give_heart)
+ for(var/i in 1 to give_heart)
+ new /obj/item/organ/heart/evolved/sacred/dreamer(get_turf(religious_tool))
+ playsound(religious_tool, 'sound/effects/pray.ogg', 50, TRUE, frequency = 0.5)
+ to_chat(user, span_hypnophrase("[GLOB.deity] blesses you."))
+ else
+ to_chat(user, span_hypnophrase("[GLOB.deity] smiles upon you."))
+ user.add_mood_event("banish_nightmare", /datum/mood_event/banish_nightmare)
+
+/datum/mood_event/banish_nightmare
+ mood_change = 4
+ description = "I banished a nightmare and protected our dreams!"
+ timeout = 10 MINUTES
+
+/obj/item/organ/heart/evolved/sacred/dreamer
+ name = "blessed sacred heart"
+ desc = "Banish the shadows!"
+ maxHealth = STANDARD_ORGAN_THRESHOLD * 1.5
+ /// Magic charges we block
+ var/charges = 3
+
+/obj/item/organ/heart/evolved/sacred/dreamer/on_life(seconds_per_tick)
+ healing_probability = 5
+ if(HAS_TRAIT(owner, TRAIT_DREAMING))
+ healing_probability += 7.5
+ if(owner.stat == UNCONSCIOUS)
+ healing_probability += 7.5
+ return ..()
+
+/obj/item/organ/heart/evolved/sacred/dreamer/on_blocked()
+ charges -= 1
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 1 MINUTES)
+ playsound(owner, 'sound/health/slowbeat.ogg', 80)
+
+/obj/item/organ/heart/evolved/sacred/dreamer/check_block()
+ return charges > 0
+
+/obj/item/organ/heart/evolved/sacred/dreamer/proc/recharge()
+ charges += 1
diff --git a/code/modules/religion/dreams/deaconize_dreamer.dm b/code/modules/religion/dreams/deaconize_dreamer.dm
new file mode 100644
index 000000000000..a6232b10d6cd
--- /dev/null
+++ b/code/modules/religion/dreams/deaconize_dreamer.dm
@@ -0,0 +1,20 @@
+/datum/religion_rites/deaconize/dreamers
+ desc = "Converts someone to your sect. They must be willing, so the first invocation will instead prompt them to join. \
+ They will gain the same holy abilities as you. You can deaconize up to three followers, so choose wisely!"
+ rite_flags = parent_type::rite_flags & ~RITE_ONE_TIME_USE
+
+/datum/religion_rites/deaconize/dreamers/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
+ if(isnightmare(potential_deacon))
+ to_chat(user, span_warning("[potential_deacon] is a nightmare, an affront to [GLOB.deity] and all they stand for!"))
+ return FALSE
+ return ..()
+
+/datum/religion_rites/deaconize/dreamers/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ if(!istype(GLOB.religious_sect, /datum/religion_sect/dreams))
+ return
+
+ var/datum/religion_sect/dreams/sect = GLOB.religious_sect
+ sect.deacon_count += 1
+ if(sect.deacon_count >= sect.max_deacons)
+ sect.rites_list -= type
diff --git a/code/modules/religion/dreams/dream_portent.dm b/code/modules/religion/dreams/dream_portent.dm
new file mode 100644
index 000000000000..82f5ca8bab89
--- /dev/null
+++ b/code/modules/religion/dreams/dream_portent.dm
@@ -0,0 +1,377 @@
+/datum/religion_rites/dream_portent
+ name = "Dream Portent"
+ desc = "Immediately fall into a slumber and receive a portent of the future. \
+ The vision may be difficult to interpret, but will likely come true in some form. \
+ Any form of harm will awaken you and disrupt the vision."
+ favor_cost = 50
+ rite_flags = NONE
+ ritual_length = 6 SECONDS
+
+/datum/religion_rites/dream_portent/New()
+ . = ..()
+ ritual_invocations = list(
+ "O great shepherd [GLOB.deity], grant me a vision of the future!..",
+ "That our flock may persevere through the trials to come...",
+ )
+
+/datum/religion_rites/dream_portent/can_afford(mob/living/user)
+ if(!..())
+ return FALSE
+ if(!iscarbon(user))
+ to_chat(user, span_warning("You are not the sort of creature that can receive a portent."))
+ return FALSE
+ return TRUE
+
+/datum/religion_rites/dream_portent/invoke_effect(mob/living/user, atom/religious_tool)
+ if(!user.SetSleeping(10 SECONDS))
+ to_chat(user, span_warning("You fail to fall asleep."))
+ return FALSE
+
+ user.visible_message(span_notice("[user] suddenly falls into a deep slumber, [user.p_their()] eyes fluttering..."))
+ user.adjust_drowsiness(30 SECONDS)
+ return ..()
+
+/datum/religion_rites/dream_portent/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ RegisterSignal(user, COMSIG_PRE_DREAMING, PROC_REF(add_portent))
+ RegisterSignal(user, COMSIG_START_DREAMING, PROC_REF(check_portent))
+ RegisterSignal(user, COMSIG_END_DREAMING, PROC_REF(end_portent))
+ RegisterSignal(user, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(interrupt_portent))
+ addtimer(CALLBACK(src, PROC_REF(force_dream), user), rand(2, 5) SECONDS, TIMER_DELETE_ME) // force the dream to start immediately
+
+/datum/religion_rites/dream_portent/proc/force_dream(mob/living/carbon/dreamer)
+ if(!iscarbon(dreamer) || HAS_TRAIT(dreamer, TRAIT_DREAMING))
+ return // dreamed naturally already
+ dreamer.dream()
+
+/datum/religion_rites/dream_portent/proc/add_portent(mob/living/carbon/dreamer, list/dream_pool)
+ SIGNAL_HANDLER
+
+ // removes any pre-existing vague portents in the dream pool so we can give the real deal
+ for(var/datum/dream/random/vague_portent/existing in dream_pool)
+ dream_pool -= existing
+
+ dream_pool[new /datum/dream/specific_portent()] = 2000
+
+/datum/religion_rites/dream_portent/proc/check_portent(mob/living/carbon/dreamer, datum/dream/current_dream)
+ SIGNAL_HANDLER
+
+ if(istype(current_dream, /datum/dream/specific_portent))
+ return
+
+ to_chat(dreamer, span_cyan("Your mind wanders, yet you receive no clear vision... You must try again later."))
+ refund(0.8)
+ dreamer.adjust_drowsiness(10 SECONDS)
+ dreamer.add_mood_event("dream_failed", /datum/mood_event/dream_failed)
+
+/datum/religion_rites/dream_portent/proc/interrupt_portent(mob/living/carbon/dreamer, damage_amount)
+ SIGNAL_HANDLER
+
+ if(!prob(damage_amount * 10)) // higher damage = higher chance to interrupt
+ return
+
+ to_chat(dreamer, span_warning("Your dream is interrupted as you are harmed!"))
+ dreamer.SetSleeping(0)
+ dreamer.adjust_drowsiness(10 SECONDS)
+ dreamer.add_mood_event("dream_interrupted", /datum/mood_event/dream_interrupted)
+
+/datum/religion_rites/dream_portent/proc/end_portent(mob/living/carbon/dreamer, datum/dream/current_dream)
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+/datum/mood_event/dream_interrupted
+ mood_change = -2
+ description = "I was rudely awakened from my dreams!"
+ timeout = 5 MINUTES
+
+/datum/mood_event/dream_failed
+ mood_change = -2
+ description = "I couldn't receive a clear vision from my dreams!"
+ timeout = 5 MINUTES
+
+/datum/dream/specific_portent
+ weight = 0
+ sleep_until_finished = TRUE
+
+/datum/dream/specific_portent/GenerateDream(mob/living/carbon/dreamer)
+ . = list()
+ . += span_cyan("a portent of the future")
+
+ var/list/portent_types = list(
+ "[GLOB.deity] greets you warmly" = "[GLOB.deity] bids you farewell, though you feel their presence watch over you",
+ "a crystal ball reveals a vision of the future" = "ultimately, the crystal ball returns to its normal, opaque state",
+ "a divine light blinds you, revealing glimpses of what is to come" = "finally, the light fades, leaving you with a lingering warmth",
+ "a full moon illuminates the sky" = "the moon crosses the horizon, bringing forth a new dawn",
+ "a mysterious figure appears, cloaked in shadow" = "they depart, leaving you with a sense of [pick("wonder", "dread", "curiosity", "foreboding")]",
+ "an incomprehensible entity envelops you, showing you visions of the past, present, and future" = "the entity releases you, leaving you with a sense of awe and fear",
+ "an old [pick("man", "woman", "prophet", "oracle")] approaches you, offering cryptic advice" = "they vanish before you can ask any questions",
+ "the stars align in a way you've never seen before" = "finally, the stars return to their normal constellations",
+ "the trees ahead parts to reveal a hidden path" = "ultimately, you lose the path as the trees sway back into place",
+ "walking through a featureless landscape, shapes begin to form" = "finally, the shapes fade away, leaving you alone in the void",
+ "you see yourself sleeping peacefully" = "finally, you see yourself waking up calmly",
+ "your third eye opens to reveal a hidden truth" = "finally, your third eye closes, but the vision lingers in your mind",
+ )
+ var/picked_portent = pick(portent_types)
+
+ . += span_cyan(picked_portent)
+ for(var/part in get_portent(dreamer))
+ . += span_cyan(part)
+ . += span_cyan(portent_types[picked_portent])
+
+/datum/dream/specific_portent/proc/get_portent(mob/living/carbon/dreamer)
+ if(prob(1))
+ GLOB.religious_sect.adjust_favor(25, dreamer)
+ return pick(list(
+ list("reply hazy", "try again later"),
+ list("ask again later"),
+ list("better not tell you now"),
+ list("cannot predict now"),
+ list("concentrate and ask again"),
+ ))
+
+ for(var/datum/antagonist/nightmare/nightmare as anything in GLOB.antagonists)
+ if(nightmare.owner?.current?.stat == CONSCIOUS)
+ return pick(list(
+ list("you have a terrible nightmare", "filled with indescribable horrors", "leaving you with a lingering sense of dread"),
+ list("you have a terrible nightmare", "filled with visions of your own death", "leaving you with a lingering sense of doom"),
+ list("you have a terrible nightmare", "filled with horrible memories of your past", "leaving you with a lingering sense of sadness"),
+ list("you have a terrible nightmare", "filled with fear of the unknown", "leaving you with a lingering sense of anxiety"),
+ list("you have a terrible nightmare", "filled with stabbing pain and suffocating darkness", "leaving you with a lingering sense of panic"),
+ ))
+
+ for(var/datum/team/cult/cult as anything in GLOB.antagonist_teams)
+ if(cult.cult_ascendent)
+ return list("the Blood Geometer, Nar'sie, invades your dream", "her pressence overwhelming and suffocating", "she eyes you greedily")
+
+ for(var/datum/antagonist/heretic/heretic in GLOB.antagonists)
+ if(!heretic.ascended)
+ continue
+ if(heretic.owner?.current?.stat != CONSCIOUS)
+ return list(
+ "the doors of the Mansus loom ahead of you",
+ "intricately decorated - but cracked, broken, and sealed shut",
+ "a great [IS_HERETIC(dreamer) ? "force" : "evil"] locked away for good",
+ )
+
+ var/list/heretic_text = list("the doors of the Mansus loom ahead of you", "intricately decorated - and ajar", "you look through the crack")
+ switch(prob(75) ? heretic.heretic_path : null)
+ if(PATH_ASH)
+ heretic_text += "beyond it, you see a barren wasteland"
+ heretic_text += "all life long gone, scorched to ash and dust"
+ heretic_text += "you can hardly breathe through the smog"
+ if(PATH_FLESH)
+ heretic_text += "beyond it, you see a vast sea of blood"
+ heretic_text += "the screams of the drowning fill the air"
+ heretic_text += "the blood laps at your feet"
+ if(PATH_VOID)
+ heretic_text += "beyond it, a vast emptiness stretches out in all directions"
+ heretic_text += "the silence is deafening"
+ heretic_text += "you know you are not alone"
+ if(PATH_COSMIC)
+ heretic_text += "beyond it, you see the birth and death of stars, galaxies colliding in a cosmic dance"
+ heretic_text += "the beauty of it all is overwhelming"
+ heretic_text += "you feel insignificant"
+ if(PATH_BLADE)
+ heretic_text += "beyond it, you see a great battle unfolding"
+ heretic_text += "countless warriors grappling in an endless war"
+ heretic_text += "the sound of clashing steel and cries of the fallen fill the air"
+ if(PATH_LOCK)
+ heretic_text += "beyond it, you see an endless labyrinth"
+ heretic_text += "the walls shifting and changing as you navigate it"
+ heretic_text += "no matter which turn you take, you cannot find an exit"
+ if(PATH_MOON)
+ heretic_text += "beyond it, you bear witness to a grand carnival"
+ heretic_text += "filled with strange sights and smells, but endless joy and laughter"
+ heretic_text += "you can't shake the feeling something is wrong"
+ else
+ heretic_text += "what lies beyond cannot be comprehended"
+ heretic_text += "the sheer magnitude of overwhelms you"
+ heretic_text += "you feel a strange mix of awe and terror"
+
+ return heretic_text
+
+ for(var/datum/antagonist/wizard/wizard in GLOB.antagonists)
+ if(wizard.owner?.current?.stat != CONSCIOUS)
+ return
+ if(wizard.ritual?.times_completed < GRAND_RITUAL_RUNES_WARNING_POTENCY)
+ if(prob(1))
+ return list(
+ "a garish fool puts on a show",
+ "many lament their antics, but some are amused",
+ "they seem to have no sense of right or wrong",
+ "what an asshole",
+ )
+ return
+
+ if(wizard.ritual.times_completed == GRAND_RITUAL_FINALE_COUNT)
+ return list(
+ "the magician appears once more",
+ "they ignore you, bowing to an unseen audience",
+ "you hear a crowd cheering and applause",
+ "a bright light envelops you, blinding you",
+ "when your vision returns, the magician is gone",
+ )
+
+ if(wizard.ritual.times_completed == GRAND_RITUAL_IMMINENT_FINALE_POTENCY)
+ return list(
+ "the magician greets you once more, grinning",
+ "they tell you the grand finale is near",
+ "you can feel the air around you crackling with magical energy",
+ "they magician winks, promising an unforgettable show",
+ "before you can question, they vanish once again",
+ )
+
+ return list(
+ "a robed figure manifests in your dream",
+ "they introduce themselves as the magician",
+ "a demonstration of their powers leaves you in awe",
+ "they leave as suddenly as they arrived",
+ )
+
+ for(var/obj/machinery/nuclearbomb/bomb as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/nuclearbomb))
+ if(bomb.timing)
+ return pick(list(
+ list("you see", "a supernova", "bright and blinding", "consuming everything in an instant"),
+ list("you see", "a mushroom cloud on the horizon", "a sign of devastation and ruin"),
+ list("you see", "a ticking clock", "counting down to an inevitable disaster"),
+ list("you see", "a looming tower atop a rocky mountain", "a lightning strikes it, bringing it down", "a sign of imminent calamity"),
+ ))
+
+ for(var/obj/item/disk/nuclear/nuke_disk as anything in SSpoints_of_interest.real_nuclear_disks)
+ var/area/disk_loc = get_area(nuke_disk)
+ if(!istype(disk_loc, /area/station) && !istype(disk_loc, /area/space))
+ return pick(list(
+ list("you see", "a dying star", "slowly dimming", "on the verge of collapse"),
+ list("you see", "a fool, dancing aimlessly", "they holds a ticking bomb", "a sign of recklessness"),
+ list("you see", "a hanging man", "swaying gently in the breeze", "a sign of surrender"),
+ list("you see", "a looming tower atop a rocky mountain", "it rains heavily around you", "a sign of calamity"),
+ ))
+
+ for(var/mob/living/carbon/human/clone as anything in GLOB.human_list)
+ if(clone != dreamer && clone.real_name == dreamer.real_name && clone.stat == CONSCIOUS && prob(50))
+ return list(
+ "you see yourself",
+ "in a foggy mirror",
+ "the reflection is warped and distorted",
+ "but unquestionably you",
+ )
+
+ if(prob(length(dreamer.get_all_orbiters()) * 20))
+ return list(
+ "you feel a ghostly [pick("presence", "entity", "figure")]",
+ "it seems to be trying to communicate with you",
+ "yet you can't comprehend its message",
+ "a sense of sadness and longing washes over you",
+ )
+
+ if(IS_HERETIC(dreamer) && prob(50))
+ var/datum/antagonist/heretic/heretic = GET_HERETIC(dreamer)
+ switch(prob(75) ? heretic.heretic_path : null)
+ if(PATH_START)
+ return list("you see", "a fork in the road ahead", "the path before you uncertain and full of potential")
+ if(PATH_ASH)
+ return list("you see", "a barren wasteland ahead", "burned trees line the horizon", "the air thick with smoke and ash", "a bleak and desolate sight")
+ if(PATH_FLESH)
+ return list("you see", "a legion of amalgamations ahead", "twisted and grotesque", "marching in unison towards an unknown destination")
+ if(PATH_VOID)
+ return list("you see", "nothingness ahead", "a void that seems to swallow all light and hope", "the silence deafening and oppressive")
+ if(PATH_COSMIC)
+ return list("you see", "the birth of a new star", "radiant and full of potential", "an awe-inspiring sight")
+ if(PATH_BLADE)
+ return list("you see", "a towering fortress ahead", "its walls lined with stalwart defenders", "each and every one bowing in respect to you")
+ if(PATH_LOCK)
+ return list("you see", "and endless labyrinth", "the walls shifting and changing as you navigate it", "a test of your resolve and cunning")
+ if(PATH_MOON)
+ return list("you see", "an everlasting carnival", "the air filled with joy and laughter", "but with an undercurrent of melancholy and longing")
+
+ return list("you see", "a large door ahead", "intricately decorated and emanating a powerful aura", "but never opening", "no matter how long you wait")
+
+ var/dead = 0
+ for(var/mob/deceased as anything in GLOB.player_list)
+ if(deceased.stat == DEAD)
+ dead += 1
+
+ switch(dead / length(GLOB.joined_player_list))
+ if(0.25 to 0.5)
+ return pick(list(
+ list("you find yourself", "in a small graveyard", "humble in size but lovingly maintained", "with fresh flowers on the graves"),
+ list("you see", "a vision of spirits", "floating throughout the station"),
+ ))
+ if(0.5 to 0.75)
+ return pick(list(
+ list("you find yourself", "in a dimly lit hallway", "with a sense of dread in the air"),
+ list("you see", "a vision of an encroaching darkness", "threatening you eerily"),
+ ))
+ if(0.75 to 0.9)
+ return pick(list(
+ list("you find yourself", "in a lonely ballroom", "barely lit with flickering lights"),
+ list("you see", "a picture of a silent battlefield", "no clear victor, but heavy losses on all sides"),
+ ))
+ if(0.9 to 1)
+ return pick(list(
+ list("you find yourself", "alone", "no sight but your darkness", "no sound but your heartbeat", "a bleak and hopeless vision"),
+ list("you see", "a vision of yourself, alone", "in a desolate wasteland", "with no signs of life or hope in sight"),
+ ))
+
+ var/max_law_changes = 0
+ for(var/mob/living/silicon/ai/ai as anything in GLOB.ai_list)
+ max_law_changes = max(max_law_changes, ai.law_change_counter)
+
+ if(prob(clamp((max_law_changes - 10) * 10, 0, 50)))
+ return pick(list(
+ list("you see", "a twisted and sickly tree", "with branches that seem to reach into every aspect of the station", "its roots drip with a inky black liquid"),
+ list("you see", "a corrupt political figure", "surrounded by sycophants and puppets", "pulling the strings from behind the scenes"),
+ list("you see", "buzzing electronics", "wires that seem to snake off into the distance", "it bathes you in red light and static"),
+ ))
+
+ if(EMERGENCY_ESCAPED_OR_ENDGAMED)
+ return list("you see", "a new beginning on the horizon", "it feels warm")
+
+ if(EMERGENCY_PAST_POINT_OF_NO_RETURN)
+ return list("you see", "salvation just out of reach")
+
+ if(prob(75) || GLOB.communications_controller.announced_greenshift)
+ if(length(GLOB.admins) >= 5)
+ return list("you see", "a gathering of powerful beings in the distance", "their intentions unclear")
+
+ switch(SSdynamic.current_tier.tier)
+ if(0)
+ return list("you see", "a flourishing field", "teeming with life and vitality", "a symbol of hope for the future")
+ if(1)
+ return pick(list(
+ list("you see", "a lone figure in the distance", "shrouded in mystery"),
+ list("you see", "a [pick("beast", "monster", "animal")] stalking through the shadows", "its intentions unknown"),
+ list("you see", "a [pick("fog", "haze", "cloud")] rolling in", "obscuring everything in its path"),
+ ))
+ if(2)
+ return pick(list(
+ list("you find yourself", "in a branching forest", "dark and with many potential paths"),
+ list("you find yourself", "in an old city", "still bustling with activity", "but with an omnipresent feel of decay"),
+ ))
+ if(3)
+ return list("you find yourself", "in a burning city", "flames reaching high", "but everyone doing their best to survive")
+ if(4)
+ return list("you find yourself", "in a chaotic battlefield", "with no clear sides or victors", "only endless conflict and suffering")
+
+ return list("you see", "nothing of note", "but have a lingering feeling of unease about the future")
+
+/datum/dream/random/vague_portent
+ weight = 0
+ sleep_until_finished = TRUE
+
+/datum/dream/random/vague_portent/get_dream_nouns(mob/living/carbon/dreamer)
+ var/list/antags = list()
+ for(var/datum/antagonist/antag as anything in GLOB.antagonists)
+ antags |= LOWER_TEXT(antag.jobban_flag || antag.pref_flag)
+
+ if(prob(80) || !length(antags))
+ for(var/datum/dynamic_ruleset/ruleset as anything in subtypesof(/datum/dynamic_ruleset))
+ antags |= LOWER_TEXT(initial(ruleset.jobban_flag) || initial(ruleset.pref_flag))
+
+ // chance to make nightmares the focus of the dream
+ var/nightmare_id = /datum/antagonist/nightmare::jobban_flag || /datum/antagonist/nightmare::pref_flag
+ if(prob(20) && (LOWER_TEXT(nightmare_id) in antags))
+ return list(LOWER_TEXT(nightmare_id))
+
+ return antags
diff --git a/code/modules/religion/dreams/dream_projection.dm b/code/modules/religion/dreams/dream_projection.dm
new file mode 100644
index 000000000000..847673b8d53c
--- /dev/null
+++ b/code/modules/religion/dreams/dream_projection.dm
@@ -0,0 +1,186 @@
+/datum/religion_rites/dream_projection
+ name = "Dream Projection"
+ desc = "Astrally project your dream consciousness into the mind of one of your followers. \
+ While projecting, you are asleep, and can communicate with only and see through the eyes of the chosen follower, \
+ but cannot interact with the world in any way. The projection can be ended at any time, \
+ ends if you are woken up or attacked, and ends if the follower dies."
+ favor_cost = 100
+ ritual_length = 15 SECONDS
+
+/datum/religion_rites/dream_projection/New()
+ . = ..()
+ ritual_invocations = list(
+ "A member of the flock has gone astray, lost in the waking world...",
+ "It is the duty of the shepherd to guide them back to the fold, even if they cannot find their way themselves...",
+ "Let me walk through their waking dream, and show them the way back...",
+ )
+
+/datum/religion_rites/dream_projection/perform_rite(mob/living/user, atom/religious_tool)
+ var/list/followers = list()
+ for(var/mob/living/follower as anything in GLOB.mob_living_list)
+ if(follower.mind?.holy_role && user != follower)
+ followers += follower
+
+ if(!length(followers))
+ to_chat(user, span_warning("You have no followers to project into!"))
+ return FALSE
+
+ return ..()
+
+/datum/religion_rites/dream_projection/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ var/list/followers = list()
+ for(var/mob/living/follower as anything in GLOB.mob_living_list)
+ if(follower.mind?.holy_role && user != follower)
+ followers += follower
+
+ if(!length(followers))
+ refund(0.8)
+ return
+
+ var/mob/living/carbon/human/target = tgui_input_list(user, "Choose a follower to project into:", "Dream Projection", followers)
+ if(QDELETED(target) || target.stat == DEAD || isnull(target.mind?.holy_role))
+ refund(0.8)
+ return
+
+ if(!user.apply_status_effect(/datum/status_effect/dream_projection, target))
+ to_chat(user, span_warning("You fail to fall asleep."))
+ refund(0.8)
+ return
+
+/datum/status_effect/dream_projection
+ id = "dream_projection"
+ duration = -1 //STATUS_EFFECT_PERMANENT
+ alert_type = null
+ on_remove_on_mob_delete = TRUE
+
+ /// Target of the projection
+ VAR_PRIVATE/mob/living/carbon/human/target
+ /// Projection mob that the owner is put into
+ VAR_PRIVATE/mob/camera/imaginary_friend/dream_projection/projection
+
+/datum/status_effect/dream_projection/on_creation(mob/living/new_owner, mob/living/carbon/human/target)
+ if(isnull(target))
+ stack_trace("Dream projection created without a target!")
+ qdel(src)
+ return
+
+ src.target = target
+ return ..()
+
+/datum/status_effect/dream_projection/get_examine_text()
+ return "[owner.p_They()] are in a deep slumber, yet [owner.p_their()] eyes show a distant look, as if [owner.p_they()] are somewhere far away..."
+
+/datum/status_effect/dream_projection/on_apply()
+ if(!owner.SetSleeping(20 SECONDS))
+ to_chat(owner, span_warning("You fail to fall asleep."))
+ return FALSE
+
+ . = ..()
+ RegisterSignal(target, COMSIG_QDELETING, PROC_REF(end_projection))
+ RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(end_projection))
+
+ ADD_TRAIT(owner, TRAIT_DREAMING, TRAIT_STATUS_EFFECT(id))
+ RegisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_KNOCKEDOUT), PROC_REF(interrupt_projection))
+ RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(interrupt_projection))
+ RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(interrupt_projection))
+
+ projection = new(target.loc)
+ projection.AddComponent(/datum/component/temporary_body, old_mind = owner.mind)
+ projection.real_name = owner.real_name
+ projection.gender = owner.gender
+ projection.human_icon = getFlatIcon(owner)
+ // projection.PossessByPlayer(owner.ckey)
+ projection.ckey = owner.ckey
+ projection.attach_to_owner(target)
+
+ RegisterSignal(projection, COMSIG_QDELETING, PROC_REF(stop_projection))
+
+ owner.add_filter(id, 1, list("type" = "outline", "color" = "#aee2b2", "alpha" = 0, "size" = 2))
+ owner.update_filters()
+ var/filter = owner.get_filter(id)
+ animate(filter, alpha = 150, time = 2 SECONDS, easing = SINE_EASING|EASE_IN, loop = -1)
+ animate(alpha = 0, time = 2 SECONDS, easing = SINE_EASING|EASE_OUT)
+
+/datum/status_effect/dream_projection/on_remove()
+ . = ..()
+ UnregisterSignal(target, COMSIG_QDELETING)
+ UnregisterSignal(target, COMSIG_LIVING_DEATH)
+ target = null
+
+ REMOVE_TRAIT(owner, TRAIT_DREAMING, TRAIT_STATUS_EFFECT(id))
+ UnregisterSignal(owner, SIGNAL_REMOVETRAIT(TRAIT_KNOCKEDOUT))
+ UnregisterSignal(owner, COMSIG_LIVING_DEATH)
+ UnregisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE)
+
+ if(!QDELING(owner))
+ owner.adjust_drowsiness(10 SECONDS)
+ var/filter = owner.get_filter(id)
+ animate(filter, alpha = 0, time = 1 SECONDS, easing = SINE_EASING|EASE_OUT)
+ addtimer(CALLBACK(owner, TYPE_PROC_REF(/datum, remove_filter), id), 1 SECONDS) // delay the filter removal to let the transition finish
+
+ UnregisterSignal(projection, COMSIG_QDELETING)
+ if(QDELING(projection))
+ projection = null
+ else
+ QDEL_NULL(projection)
+
+/datum/status_effect/dream_projection/tick(seconds_between_ticks)
+ if(isnull(owner.mind?.holy_role))
+ end_projection()
+ return
+
+ owner.SetSleeping(20 SECONDS) // keep the owner asleep
+
+/datum/status_effect/dream_projection/proc/end_projection()
+ SIGNAL_HANDLER
+ to_chat(owner, span_warning("Your dream projection ends as your target is no longer valid."))
+ owner.SetSleeping(10 SECONDS)
+ qdel(src)
+
+/datum/status_effect/dream_projection/proc/interrupt_projection()
+ SIGNAL_HANDLER
+ to_chat(owner, span_warning("Your dream projection is interrupted!"))
+ INVOKE_ASYNC(src, TYPE_PROC_REF(/mob, emote), "gasp")
+ owner.visible_message(span_notice("[owner]'s eyes snap open as they are jolted awake!"), vision_distance = COMBAT_MESSAGE_RANGE, ignored_mobs = owner)
+ qdel(src)
+
+/datum/status_effect/dream_projection/proc/stop_projection()
+ SIGNAL_HANDLER
+ to_chat(owner, span_warning("You end your dream projection and return to your body."))
+ owner.SetSleeping(10 SECONDS)
+ qdel(src)
+
+/mob/camera/imaginary_friend/dream_projection
+ name = "dream projection"
+
+/mob/camera/imaginary_friend/dream_projection/Initialize(mapload)
+ . = ..()
+ var/datum/action/innate/stop_projection/exit_action = new(src)
+ exit_action.Grant(src)
+ overlay_fullscreen("curse", /atom/movable/screen/fullscreen/curse, 1) // todo something more fitting?
+
+/mob/camera/imaginary_friend/dream_projection/Login()
+ . = ..()
+ client.eye = owner || src
+
+/mob/camera/imaginary_friend/dream_projection/greet()
+ return
+
+/mob/camera/imaginary_friend/dream_projection/verb/stop_projection()
+ set category = "IC"
+ set name = "Stop Projection"
+ set desc = "Stop astrally projecting and return to your body."
+
+ qdel(src)
+
+/mob/camera/imaginary_friend/dream_projection/attach_to_owner(mob/living/imaginary_friend_owner)
+ . = ..()
+ client?.eye = owner
+
+/datum/action/innate/stop_projection
+ name = "Stop Projection"
+ desc = "Stop astrally projecting and return to your body."
+
+/datum/action/innate/stop_projection/Activate()
+ qdel(owner)
diff --git a/code/modules/religion/dreams/dream_protection.dm b/code/modules/religion/dreams/dream_protection.dm
new file mode 100644
index 000000000000..6aec31581dcc
--- /dev/null
+++ b/code/modules/religion/dreams/dream_protection.dm
@@ -0,0 +1,130 @@
+/datum/religion_rites/dream_protection
+ name = "Dream Protection"
+ desc = "Bless you and all of your followers with protection in their slumber, \
+ granting resistance to damage while asleep, which is further increased while dreaming."
+ favor_cost = 200
+ rite_flags = RITE_ONE_TIME_USE | RITE_AUTO_DELETE
+ ritual_length = 15 SECONDS
+
+/datum/religion_rites/dream_protection/New()
+ . = ..()
+ ritual_invocations = list(
+ "Protect our flock from harm, great shepherd [GLOB.deity]!..",
+ "Grant us peaceful slumber, free from nightmares and those who would do us harm!..",
+ "Our sleepers shall be safe to dream to their heart's desire!..",
+ )
+
+/datum/religion_rites/dream_protection/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ if(!istype(GLOB.religious_sect, /datum/religion_sect/dreams))
+ return
+
+ var/datum/religion_sect/dreams/sect = GLOB.religious_sect
+ sect.dream_protection = TRUE
+
+ for(var/mob/living/follower as anything in GLOB.mob_living_list)
+ if(follower.mind?.holy_role)
+ follower.apply_status_effect(/datum/status_effect/dream_protection)
+
+/datum/status_effect/dream_protection
+ id = "dream_protection"
+ duration = -1 //STATUS_EFFECT_PERMANENT
+ tick_interval = -1 //STATUS_EFFECT_NO_TICK
+ alert_type = null
+ /// Damage reduction when sleeping/dreaming, multiplicative
+ var/damage_mod = 0.75
+ /// If the filter has been applied
+ VAR_PRIVATE/has_filter = FALSE
+
+/datum/status_effect/dream_protection/on_apply()
+ . = ..()
+ RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(modify_damage))
+ RegisterSignals(owner, list(
+ COMSIG_MOB_STATCHANGE,
+ SIGNAL_ADDTRAIT(TRAIT_DREAMING),
+ SIGNAL_REMOVETRAIT(TRAIT_DREAMING),
+ ), PROC_REF(check_protection))
+ check_protection()
+
+/datum/status_effect/dream_protection/on_remove()
+ . = ..()
+ UnregisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS)
+ REMOVE_TRAIT(owner, TRAIT_HOLY, TRAIT_STATUS_EFFECT(id))
+
+ if(!QDELING(owner))
+ var/filter = owner.get_filter(id)
+ animate(filter, alpha = 0, time = 1 SECONDS, easing = SINE_EASING|EASE_OUT)
+ addtimer(CALLBACK(owner, TYPE_PROC_REF(/datum, remove_filter), id), 1 SECONDS) // delay the filter removal to let the transition finish
+
+/datum/status_effect/dream_protection/proc/check_protection()
+ SIGNAL_HANDLER
+
+ if(owner.stat == UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_DREAMING))
+ if(!has_filter)
+ owner.add_filter(id, 2, list("type" = "outline", "color" = "#bde0dc", "alpha" = 0, "size" = 2))
+ var/filter = owner.get_filter(id)
+ animate(filter, alpha = 150, time = 2 SECONDS, easing = SINE_EASING|EASE_IN, loop = -1)
+ animate(alpha = 0, time = 2 SECONDS, easing = SINE_EASING|EASE_OUT)
+ has_filter = TRUE
+ ADD_TRAIT(owner, TRAIT_HOLY, TRAIT_STATUS_EFFECT(id))
+
+ else
+ if(has_filter)
+ var/filter = owner.get_filter(id)
+ animate(filter, alpha = 0, time = 1 SECONDS, easing = SINE_EASING|EASE_OUT)
+ has_filter = FALSE
+ REMOVE_TRAIT(owner, TRAIT_HOLY, TRAIT_STATUS_EFFECT(id))
+
+/datum/status_effect/dream_protection/proc/modify_damage(mob/living/source, list/damage_mods, ...)
+ SIGNAL_HANDLER
+ if(owner.stat == UNCONSCIOUS)
+ damage_mods += damage_mod
+ if(HAS_TRAIT(owner, TRAIT_DREAMING))
+ damage_mods += damage_mod
+
+/datum/status_effect/dream_protection/get_examine_text()
+ if(owner.stat == UNCONSCIOUS || HAS_TRAIT(owner, TRAIT_DREAMING))
+ return "A soft cyan glow envelops [owner.p_them()], reflecting light."
+
+// Version that only lasts until they wake up (with a set duration backup)
+/datum/status_effect/dream_protection/temporary
+ id = "temporary_dream_protection"
+ duration = 3 MINUTES // lasts until they wake up or if they're an especially long sleeper
+ damage_mod = 0.9
+
+/datum/status_effect/dream_protection/temporary/on_apply()
+ if(owner.stat != UNCONSCIOUS)
+ return FALSE
+ if(owner.has_status_effect(/datum/status_effect/dream_protection))
+ return FALSE
+
+ return ..()
+
+/datum/status_effect/dream_protection/temporary/check_protection()
+ . = ..()
+ if(!has_filter) // soon as it goes, we go
+ qdel(src)
+
+// Version that works on dead mobs and lasts until they revive (with a set duration backup)
+/datum/status_effect/dream_protection/deceased
+ id = "deceased_dream_protection"
+ duration = 3 MINUTES // lasts until they wake up or if they're an especially long sleeper
+
+/datum/status_effect/dream_protection/deceased/on_apply()
+ if(owner.stat != DEAD)
+ return FALSE
+ if(owner.has_status_effect(/datum/status_effect/dream_protection))
+ return FALSE
+
+ RegisterSignal(owner, COMSIG_LIVING_REVIVE, PROC_REF(mob_revived))
+ ADD_TRAIT(owner, TRAIT_DREAMING, TRAIT_STATUS_EFFECT(id)) // "permanent" dreaming
+ return ..()
+
+/datum/status_effect/dream_protection/deceased/on_remove()
+ UnregisterSignal(owner, COMSIG_LIVING_REVIVE)
+ REMOVE_TRAIT(owner, TRAIT_DREAMING, TRAIT_STATUS_EFFECT(id))
+ return ..()
+
+/datum/status_effect/dream_protection/deceased/proc/mob_revived()
+ SIGNAL_HANDLER
+ qdel(src)
diff --git a/code/modules/religion/dreams/slumber_party.dm b/code/modules/religion/dreams/slumber_party.dm
new file mode 100644
index 000000000000..5c7acad5f30a
--- /dev/null
+++ b/code/modules/religion/dreams/slumber_party.dm
@@ -0,0 +1,112 @@
+/datum/religion_rites/slumber_party
+ name = "Slumber Party"
+ desc = "Put all nearby creatures to sleep. \
+ All affected creatures share the same dream and heal rapidly while sleeping. \
+ You and your followers heal even faster during the ritual."
+ favor_cost = 200
+ rite_flags = RITE_AUTO_DELETE
+ ritual_length = 20 SECONDS
+
+/datum/religion_rites/slumber_party/New()
+ . = ..()
+ ritual_invocations = list(
+ "Sleep now, flock of [GLOB.deity], and share in each other's dreams...",
+ "Our slumber shall rejuvenate us for the trials ahead...",
+ "May we all wake up refreshed and renewed!",
+ )
+
+/datum/religion_rites/slumber_party/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ var/datum/dream/random/base_dream = new()
+ for(var/mob/living/carbon/nearby_guy in view(5, get_turf(religious_tool)))
+ nearby_guy.apply_status_effect(/datum/status_effect/slumber_party, base_dream.GenerateDream(user))
+ qdel(base_dream)
+
+/datum/status_effect/slumber_party
+ id = "slumber_party"
+ duration = 20 SECONDS
+ alert_type = null
+ /// Dream fragments we share between all sleepers
+ var/list/shared_dream
+ /// How much we heal per second while sleepin - holy people heal more
+ var/healing = 2
+
+/datum/status_effect/slumber_party/on_creation(mob/living/new_owner, list/shared_dream)
+ src.shared_dream = shared_dream
+ return ..()
+
+/datum/status_effect/slumber_party/on_apply()
+ var/datum/antagonist/cult/cultist = GET_CULTIST(owner)
+ if(cultist?.cult_team?.cult_ascendent)
+ return FALSE
+
+ var/datum/antagonist/heretic/heretic = GET_HERETIC(owner)
+ if(heretic?.ascended)
+ return FALSE
+
+ if(!(owner.mob_biotypes & MOB_ORGANIC))
+ return FALSE
+
+ if(!owner.SetSleeping(duration))
+ return FALSE
+
+ if(owner.mind?.holy_role)
+ healing *= 2
+
+ else if(owner.can_block_magic(MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND, 1))
+ return FALSE
+
+ RegisterSignal(owner, COMSIG_PRE_DREAMING, PROC_REF(add_shared_dream))
+ RegisterSignal(owner, COMSIG_START_DREAMING, PROC_REF(start_shared_dream))
+ RegisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE, PROC_REF(damage_applied))
+ if(iscarbon(owner))
+ addtimer(CALLBACK(src, PROC_REF(force_dream)), rand(4, 8) SECONDS, TIMER_DELETE_ME)
+ return TRUE
+
+/datum/status_effect/slumber_party/on_remove()
+ UnregisterSignal(owner, COMSIG_PRE_DREAMING)
+ UnregisterSignal(owner, COMSIG_START_DREAMING)
+ UnregisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE)
+ REMOVE_TRAIT(owner, TRAIT_DREAMING, TRAIT_STATUS_EFFECT(id))
+ owner.adjust_drowsiness(20 SECONDS)
+
+/datum/status_effect/slumber_party/tick(seconds_between_ticks)
+ if(owner.stat != UNCONSCIOUS)
+ qdel(src)
+ return
+
+ owner.heal_overall_damage(healing * seconds_between_ticks, healing * seconds_between_ticks, required_bodytype = BODYTYPE_ORGANIC)
+ owner.adjustToxLoss(healing * seconds_between_ticks * 0.50, required_biotype = MOB_ORGANIC)
+ owner.adjustOxyLoss(healing * seconds_between_ticks * 0.25, required_biotype = MOB_ORGANIC)
+
+/datum/status_effect/slumber_party/proc/force_dream()
+ var/mob/living/carbon/dreamer = owner
+ if(HAS_TRAIT(dreamer, TRAIT_DREAMING))
+ return // dreamed naturally already
+ dreamer.dream()
+
+/datum/status_effect/slumber_party/proc/add_shared_dream(datum/source, list/dream_pool)
+ SIGNAL_HANDLER
+ dream_pool[new /datum/dream/shared(shared_dream)] = 2000
+
+/datum/status_effect/slumber_party/proc/start_shared_dream(datum/source, datum/dream/current_dream)
+ SIGNAL_HANDLER
+ ADD_TRAIT(owner, TRAIT_DREAMING, TRAIT_STATUS_EFFECT(id)) // so they don't have any OTHER dreams
+
+/datum/status_effect/slumber_party/proc/damage_applied(mob/living/source, damage_amount, ...)
+ SIGNAL_HANDLER
+ owner.AdjustSleeping(-damage_amount * 0.5 SECONDS)
+
+/datum/dream/shared
+ sleep_until_finished = TRUE
+ /// Dream shared between everyone
+ var/list/generated_dream
+
+/datum/dream/shared/New(list/shared_dream)
+ . = ..()
+ generated_dream = LAZYLISTDUPLICATE(shared_dream)
+
+/datum/dream/shared/GenerateDream(mob/living/carbon/dreamer)
+ if(!LAZYLEN(generated_dream))
+ CRASH("Shared dream has no generated dream fragments!")
+ return generated_dream
diff --git a/code/modules/religion/festival/instrument_rites.dm b/code/modules/religion/festival/instrument_rites.dm
index 17868554f8a4..331b4e7ed4a4 100644
--- a/code/modules/religion/festival/instrument_rites.dm
+++ b/code/modules/religion/festival/instrument_rites.dm
@@ -58,7 +58,7 @@
desc = "this is a prototype."
ritual_length = 10 SECONDS
favor_cost = 10
- auto_delete = FALSE
+ rite_flags = NONE
///if repeats count as continuations instead of a song's end, TRUE
var/repeats_okay = TRUE
///personal message sent to the chaplain as feedback for their chosen song
@@ -142,7 +142,7 @@
/datum/religion_rites/song_tuner/light/Destroy()
QDEL_NULL(performer_light_obj)
- . = ..()
+ return ..()
/datum/religion_rites/song_tuner/light/finish_effect(mob/living/carbon/human/listener, atom/song_source)
listener.apply_status_effect(/datum/status_effect/song/light)
diff --git a/code/modules/religion/honorbound/honorbound_rites.dm b/code/modules/religion/honorbound/honorbound_rites.dm
index 88eef0340ea6..8e92ea91ba46 100644
--- a/code/modules/religion/honorbound/honorbound_rites.dm
+++ b/code/modules/religion/honorbound/honorbound_rites.dm
@@ -1,79 +1,3 @@
-/// how much favor is gained when someone joins the crusade and is deaconized
-#define DEACONIZE_FAVOR_GAIN 300
-
-///Makes the person holy, but they now also have to follow the honorbound code (CBT). Actually earns favor, convincing others to uphold the code (tm) is not easy
-/datum/religion_rites/deaconize
- name = "Join Crusade"
- desc = "Converts someone to your sect. They must be willing, so the first invocation will instead prompt them to join. \
- They will become honorbound like you, and you will gain a massive favor boost!"
- ritual_length = 30 SECONDS
- ritual_invocations = list(
- "A good, honorable crusade against evil is required.",
- "We need the righteous ...",
- "... the unflinching ...",
- "... and the just.",
- "Sinners must be silenced ...",
- )
- invoke_msg = "... And the code must be upheld!"
- ///the invited crusader
- var/mob/living/carbon/human/new_crusader
-
-/datum/religion_rites/deaconize/perform_rite(mob/living/user, atom/religious_tool)
- var/datum/religion_sect/honorbound/sect = GLOB.religious_sect
- if(!ismovable(religious_tool))
- to_chat(user, span_warning("This rite requires a religious device that individuals can be buckled to."))
- return FALSE
- var/atom/movable/movable_reltool = religious_tool
- if(!movable_reltool)
- return FALSE
- if(!LAZYLEN(movable_reltool.buckled_mobs))
- to_chat(user, span_warning("Nothing is buckled to the altar!"))
- return FALSE
- for(var/mob/living/carbon/human/possible_crusader in movable_reltool.buckled_mobs)
- if(possible_crusader.stat != CONSCIOUS)
- to_chat(user, span_warning("[possible_crusader] needs to be alive and conscious to join the crusade!"))
- return FALSE
- if(TRAIT_GENELESS in possible_crusader.dna.species.inherent_traits)
- to_chat(user, span_warning("This species disgusts [GLOB.deity]! They would never be allowed to join the crusade!"))
- return FALSE
- if(possible_crusader in sect.currently_asking)
- to_chat(user, span_warning("Wait for them to decide on whether to join or not!"))
- return FALSE
- if(!(possible_crusader in sect.possible_crusaders))
- INVOKE_ASYNC(sect, TYPE_PROC_REF(/datum/religion_sect/honorbound, invite_crusader), possible_crusader)
- to_chat(user, span_notice("They have been given the option to consider joining the crusade against evil. Wait for them to decide and try again."))
- return FALSE
- new_crusader = possible_crusader
- return ..()
-
-/datum/religion_rites/deaconize/invoke_effect(mob/living/carbon/human/user, atom/movable/religious_tool)
- ..()
- var/mob/living/carbon/human/joining_now = new_crusader
- new_crusader = null
- if(!(joining_now in religious_tool.buckled_mobs)) //checks one last time if the right corpse is still buckled
- to_chat(user, span_warning("The new member is no longer on the altar!"))
- return FALSE
- if(joining_now.stat != CONSCIOUS)
- to_chat(user, span_warning("The new member has to stay alive for the rite to work!"))
- return FALSE
- if(!joining_now.mind)
- to_chat(user, span_warning("The new member has no mind!"))
- return FALSE
- if(joining_now.mind.has_antag_datum(/datum/antagonist/cult))//what the fuck?!
- to_chat(user, span_warning("[GLOB.deity] has seen a true, dark evil in [joining_now]'s heart, and they have been smitten!"))
- playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
- joining_now.gib(DROP_ORGANS|DROP_BODYPARTS)
- return FALSE
- var/datum/brain_trauma/special/honorbound/honor = user.has_trauma_type(/datum/brain_trauma/special/honorbound)
- if(joining_now in honor.guilty)
- honor.guilty -= joining_now
- GLOB.religious_sect.adjust_favor(DEACONIZE_FAVOR_GAIN, user)
- to_chat(user, span_notice("[GLOB.deity] has bound [joining_now] to the code! They are now a holy role! (albeit the lowest level of such)"))
- joining_now.mind.holy_role = HOLY_ROLE_DEACON
- GLOB.religious_sect.on_conversion(joining_now)
- playsound(get_turf(religious_tool), 'sound/effects/pray.ogg', 50, TRUE)
- return TRUE
-
///Mostly useless funny rite for forgiving someone, making them innocent once again.
/datum/religion_rites/forgive
name = "Forgive"
@@ -179,4 +103,47 @@
"}
return ..()
+/// how much favor is gained when someone is deaconized
+#define DEACONIZE_FAVOR_GAIN 300
+
+/**
+ * Crusader deaconize
+ * Along with making the person holy & being an infinite-use type, it comes with the cost
+ * of enforcing an honorbound code onto convertees.
+ * Earns the church favor per conversion, but convincing others to uphold the code is not easy.
+ * Geneless species are not welcome for reasons
+ * (The actual reason is because the honorbound trauma used to be a mutation which they couldn't get,
+ * maybe it's time we let them join? idk)
+ */
+/datum/religion_rites/deaconize/crusader
+ name = "Join Crusade"
+ desc = "Converts someone to your sect. They must be willing, so the first invocation will instead prompt them to join. \
+ They will become honorbound like you, and you will gain a massive favor boost!"
+ ritual_length = 30 SECONDS
+ ritual_invocations = list(
+ "A good, honorable crusade against evil is required.",
+ "We need the righteous ...",
+ "... the unflinching ...",
+ "... and the just.",
+ "Sinners must be silenced ...",
+ )
+ invoke_msg = "... And the code must be upheld!"
+ rite_flags = RITE_ALLOW_MULTIPLE_PERFORMS
+
+/datum/religion_rites/deaconize/crusader/post_invoke_effects(mob/living/user, atom/religious_tool)
+ . = ..()
+ GLOB.religious_sect.adjust_favor(DEACONIZE_FAVOR_GAIN, user)
+
+/datum/religion_rites/deaconize/crusader/is_valid_for_deacon(mob/living/carbon/human/possible_deacon, mob/living/user)
+ if(TRAIT_GENELESS in possible_deacon.dna.species.inherent_traits)
+ to_chat(user, span_warning("This species disgusts [GLOB.deity]! They would never be allowed to join the crusade!"))
+ return FALSE
+ return ..()
+
+/datum/religion_rites/deaconize/crusader/invite_deacon(mob/living/carbon/human/invited)
+ var/ask = tgui_alert(invited, "Join [GLOB.deity]? You will be bound to a code of honor.", "Invitation", list("Yes", "No"), 60 SECONDS)
+ if(ask != "Yes")
+ return
+ potential_deacon = invited
+
#undef DEACONIZE_FAVOR_GAIN
diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm
index 7ed15a46d998..335ec7d3370f 100644
--- a/code/modules/religion/religion_sects.dm
+++ b/code/modules/religion/religion_sects.dm
@@ -31,11 +31,13 @@
/// Autopopulated by `desired_items`
var/list/desired_items_typecache
/// Lists of rites by type. Converts itself into a list of rites with "name - desc (favor_cost)" = type
- var/list/rites_list
+ var/list/rites_list = list()
/// Changes the Altar of Gods icon
var/altar_icon
/// Changes the Altar of Gods icon_state
var/altar_icon_state
+ /// Changes the Altar of Gods emissive overlay icon_state
+ var/altar_emissive_icon_state
/// Currently Active (non-deleted) rites
var/list/active_rites
/// Chance that we fail a bible blessing.
@@ -47,6 +49,8 @@
. = ..()
if(desired_items)
desired_items_typecache = typecacheof(desired_items)
+ if(!locate(/datum/religion_rites/deaconize) in rites_list)
+ rites_list += list(/datum/religion_rites/deaconize)
on_select()
/// Activates once selected
@@ -104,30 +108,41 @@
/// Replaces the bible's bless mechanic. Return TRUE if you want to not do the brain hit.
/datum/religion_sect/proc/sect_bless(mob/living/target, mob/living/chap)
if(!ishuman(target))
- return FALSE
+ return BLESSING_FAILED
+
var/mob/living/carbon/human/blessed = target
for(var/obj/item/bodypart/bodypart as anything in blessed.get_bodyparts())
if(IS_ROBOTIC_LIMB(bodypart))
to_chat(chap, span_warning("[GLOB.deity] refuses to heal this metallic taint!"))
- return TRUE
+ return BLESSING_SUCCESS
+
+ return standard_bless_healing(blessed, chap)
+
+/datum/religion_sect/proc/standard_bless_healing(mob/living/carbon/human/blessed, mob/living/chap)
+ if(!ishuman(blessed))
+ blessed.adjustBruteLoss(-10)
+ blessed.adjustFireLoss(-10)
+ return BLESSING_SUCCESS
var/heal_amt = 10
var/list/hurt_limbs = blessed.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC)
- if(hurt_limbs.len)
- for(var/X in hurt_limbs)
- var/obj/item/bodypart/affecting = X
- if(affecting.heal_damage(heal_amt, heal_amt, required_bodytype = BODYTYPE_ORGANIC))
- blessed.update_damage_overlays()
- blessed.visible_message(span_notice("[chap] heals [blessed] with the power of [GLOB.deity]!"))
- to_chat(blessed, span_boldnotice("May the power of [GLOB.deity] compel you to be healed!"))
- playsound(chap, SFX_PUNCH, 25, TRUE, -1)
- blessed.add_mood_event("blessing", /datum/mood_event/blessing)
- return TRUE
+ if(!length(hurt_limbs))
+ return BLESSING_IGNORED
+
+ for(var/obj/item/bodypart/affecting as anything in hurt_limbs)
+ if(affecting.heal_damage(heal_amt, heal_amt, required_bodytype = BODYTYPE_ORGANIC))
+ blessed.update_damage_overlays()
+
+ blessed.visible_message(span_notice("[chap] heals [blessed] with the power of [GLOB.deity]!"))
+ to_chat(blessed, span_boldnotice("May the power of [GLOB.deity] compel you to be healed!"))
+ playsound(chap, SFX_PUNCH, 25, TRUE, -1)
+ blessed.add_mood_event("blessing", /datum/mood_event/blessing)
+ return BLESSING_SUCCESS
/// What happens if we bless a corpse? By default just do the default smack behavior
/datum/religion_sect/proc/sect_dead_bless(mob/living/target, mob/living/chap)
- return FALSE
+ return BLESSING_FAILED
/**** Nanotrasen Approved God ****/
@@ -162,9 +177,11 @@
to_chat(R, span_boldnotice("You are charged by the power of [GLOB.deity]!"))
R.add_mood_event("blessing", /datum/mood_event/blessing)
playsound(chap, 'sound/effects/bang.ogg', 25, TRUE, -1)
- return TRUE
+ return BLESSING_SUCCESS
+
if(!ishuman(target))
- return
+ return BLESSING_FAILED
+
var/mob/living/carbon/human/blessed = target
//first we determine if we can charge them
@@ -179,22 +196,23 @@
if(IS_ORGANIC_LIMB(bodypart))
if(!did_we_charge)
to_chat(chap, span_warning("[GLOB.deity] scoffs at the idea of healing such fleshy matter!"))
- else
- blessed.visible_message(span_notice("[chap] charges [blessed] with the power of [GLOB.deity]!"))
- to_chat(blessed, span_boldnotice("You feel charged by the power of [GLOB.deity]!"))
- blessed.add_mood_event("blessing", /datum/mood_event/blessing)
- playsound(chap, 'sound/machines/synth_yes.ogg', 25, TRUE, -1)
- return TRUE
+ return BLESSING_IGNORED
+
+ blessed.visible_message(span_notice("[chap] charges [blessed] with the power of [GLOB.deity]!"))
+ to_chat(blessed, span_boldnotice("You feel charged by the power of [GLOB.deity]!"))
+ blessed.add_mood_event("blessing", /datum/mood_event/blessing)
+ playsound(chap, 'sound/machines/synth_yes.ogg', 25, TRUE, -1)
+ return BLESSING_SUCCESS
//charge(?) and go
- if(bodypart.heal_damage(5,5,BODYTYPE_ROBOTIC))
+ if(bodypart.heal_damage(5, 5 ,BODYTYPE_ROBOTIC))
blessed.update_damage_overlays()
blessed.visible_message(span_notice("[chap] [did_we_charge ? "repairs and charges" : "repairs"] [blessed] with the power of [GLOB.deity]!"))
to_chat(blessed, span_boldnotice("The inner machinations of [GLOB.deity] [did_we_charge ? "repairs and charges" : "repairs"] you!"))
playsound(chap, 'sound/effects/bang.ogg', 25, TRUE, -1)
blessed.add_mood_event("blessing", /datum/mood_event/blessing)
- return TRUE
+ return BLESSING_SUCCESS
/datum/religion_sect/mechanical/on_sacrifice(obj/item/stock_parts/power_store/cell/power_cell, mob/living/chap)
if(!istype(power_cell))
@@ -258,33 +276,39 @@
return "In the eyes of [GLOB.deity], your wealth is your favor."
/datum/religion_sect/greed/sect_bless(mob/living/blessed_living, mob/living/chap)
+ if(!ishuman(blessed_living))
+ return BLESSING_FAILED
+
var/datum/bank_account/account = chap.get_bank_account()
if(!account)
to_chat(chap, span_warning("You need a way to pay for the heal!"))
- return TRUE
+ return BLESSING_IGNORED
+
if(account.account_balance < GREEDY_HEAL_COST)
- to_chat(chap, span_warning("Healing from [GLOB.deity] costs [GREEDY_HEAL_COST] credits for 30 health!"))
- return TRUE
- if(!ishuman(blessed_living))
- return FALSE
+ to_chat(chap, span_warning("Healing from [GLOB.deity] costs [GREEDY_HEAL_COST] [MONEY_NAME] for 30 health!"))
+ return BLESSING_IGNORED
+
var/mob/living/carbon/human/blessed = blessed_living
for(var/obj/item/bodypart/robolimb as anything in blessed.get_bodyparts())
if(IS_ROBOTIC_LIMB(robolimb))
to_chat(chap, span_warning("[GLOB.deity] refuses to heal this metallic taint!"))
- return TRUE
+ return BLESSING_IGNORED
account.adjust_money(-GREEDY_HEAL_COST, "Church Donation: Treatment")
var/heal_amt = 30
var/list/hurt_limbs = blessed.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC)
- if(hurt_limbs.len)
- for(var/obj/item/bodypart/affecting as anything in hurt_limbs)
- if(affecting.heal_damage(heal_amt, heal_amt, required_bodytype = BODYTYPE_ORGANIC))
- blessed.update_damage_overlays()
- blessed.visible_message(span_notice("[chap] barters a heal for [blessed] from [GLOB.deity]!"))
- to_chat(blessed, span_boldnotice("May the power of [GLOB.deity] compel you to be healed! Thank you for choosing [GLOB.deity]!"))
- playsound(chap, 'sound/effects/cashregister.ogg', 60, TRUE)
- blessed.add_mood_event("blessing", /datum/mood_event/blessing)
- return TRUE
+ if(!length(hurt_limbs))
+ return BLESSING_IGNORED
+
+ for(var/obj/item/bodypart/affecting as anything in hurt_limbs)
+ if(affecting.heal_damage(heal_amt, heal_amt, required_bodytype = BODYTYPE_ORGANIC))
+ blessed.update_damage_overlays()
+
+ blessed.visible_message(span_notice("[chap] barters a heal for [blessed] from [GLOB.deity]!"))
+ to_chat(blessed, span_boldnotice("May the power of [GLOB.deity] compel you to be healed! Thank you for choosing [GLOB.deity]!"))
+ playsound(chap, 'sound/effects/cashregister.ogg', 60, TRUE)
+ blessed.add_mood_event("blessing", /datum/mood_event/blessing)
+ return BLESSING_SUCCESS
#undef GREEDY_HEAL_COST
@@ -321,10 +345,12 @@
/datum/religion_sect/burden/sect_bless(mob/living/carbon/target, mob/living/carbon/chaplain)
if(!istype(target) || !istype(chaplain))
- return FALSE
+ return BLESSING_FAILED
+
var/datum/brain_trauma/special/burdened/burden = chaplain.has_trauma_type(/datum/brain_trauma/special/burdened)
if(!burden)
- return FALSE
+ return BLESSING_FAILED
+
var/burden_modifier = max(1 - 0.07 * burden.burden_level, 0.01)
var/transferred = FALSE
var/list/hurt_limbs = target.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC) + target.get_wounded_bodyparts(BODYTYPE_ORGANIC)
@@ -332,6 +358,7 @@
for(var/obj/item/bodypart/possible_limb in chaplain.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
chaplains_limbs += possible_limb
+
if(length(chaplains_limbs))
for(var/obj/item/bodypart/affected_limb as anything in hurt_limbs)
var/obj/item/bodypart/chaplains_limb = chaplain.get_bodypart(affected_limb.body_zone)
@@ -347,15 +374,18 @@
transferred = TRUE
iter_wound.remove_wound()
iter_wound.apply_wound(chaplains_limb)
+
if(HAS_TRAIT_FROM(target, TRAIT_HUSK, BURN))
transferred = TRUE
target.cure_husk(BURN)
chaplain.become_husk(BURN)
+
var/toxin_damage = target.getToxLoss()
if(toxin_damage && !HAS_TRAIT(chaplain, TRAIT_TOXIMMUNE))
transferred = TRUE
target.adjustToxLoss(-toxin_damage)
chaplain.adjustToxLoss(toxin_damage * burden_modifier, forced = TRUE)
+
var/suffocation_damage = target.getOxyLoss()
if(suffocation_damage && !HAS_TRAIT(chaplain, TRAIT_NOBREATH))
transferred = TRUE
@@ -382,14 +412,15 @@
target.update_damage_overlays()
chaplain.update_damage_overlays()
- if(transferred)
- target.visible_message(span_notice("[chaplain] takes on [target]'s burden!"))
- to_chat(target, span_boldnotice("May the power of [GLOB.deity] compel you to be healed!"))
- playsound(chaplain, SFX_PUNCH, 25, vary = TRUE, extrarange = -1)
- target.add_mood_event("blessing", /datum/mood_event/blessing)
- else
+ if(!transferred)
to_chat(chaplain, span_warning("They hold no burden!"))
- return TRUE
+ return BLESSING_IGNORED
+
+ target.visible_message(span_notice("[chaplain] takes on [target]'s burden!"))
+ to_chat(target, span_boldnotice("May the power of [GLOB.deity] compel you to be healed!"))
+ playsound(chaplain, SFX_PUNCH, 25, vary = TRUE, extrarange = -1)
+ target.add_mood_event("blessing", /datum/mood_event/blessing)
+ return BLESSING_SUCCESS
/datum/religion_sect/burden/sect_dead_bless(mob/living/target, mob/living/chaplain)
return sect_bless(target, chaplain)
@@ -402,22 +433,7 @@
tgui_icon = "scroll"
altar_icon_state = "convertaltar-white"
alignment = ALIGNMENT_GOOD
- rites_list = list(/datum/religion_rites/deaconize, /datum/religion_rites/forgive, /datum/religion_rites/summon_rules)
- ///people who have agreed to join the crusade, and can be deaconized
- var/list/possible_crusaders = list()
- ///people who have been offered an invitation, they haven't finished the alert though.
- var/list/currently_asking = list()
-
-/**
- * Called by deaconize rite, this async'd proc waits for a response on joining the sect.
- * If yes, the deaconize rite can now recruit them instead of just offering invites
- */
-/datum/religion_sect/honorbound/proc/invite_crusader(mob/living/carbon/human/invited)
- currently_asking += invited
- var/ask = tgui_alert(invited, "Join [GLOB.deity]? You will be bound to a code of honor.", "Invitation", list("Yes", "No"), 60 SECONDS)
- currently_asking -= invited
- if(ask == "Yes")
- possible_crusaders += invited
+ rites_list = list(/datum/religion_rites/deaconize/crusader, /datum/religion_rites/forgive, /datum/religion_rites/summon_rules)
/datum/religion_sect/honorbound/on_conversion(mob/living/carbon/new_convert)
..()
@@ -445,17 +461,19 @@
/datum/religion_sect/maintenance/sect_bless(mob/living/blessed_living, mob/living/chap)
if(!ishuman(blessed_living))
- return TRUE
+ return BLESSING_FAILED
+
var/mob/living/carbon/human/blessed = blessed_living
if(blessed.reagents.has_reagent(/datum/reagent/drug/maint/sludge))
to_chat(blessed, span_warning("[GLOB.deity] has already empowered them."))
- return TRUE
+ return BLESSING_IGNORED
+
blessed.reagents.add_reagent(/datum/reagent/drug/maint/sludge, 5)
blessed.visible_message(span_notice("[chap] empowers [blessed] with the power of [GLOB.deity]!"))
to_chat(blessed, span_boldnotice("The power of [GLOB.deity] has made you harder to wound for a while!"))
playsound(chap, SFX_PUNCH, 25, TRUE, -1)
blessed.add_mood_event("blessing", /datum/mood_event/blessing)
- return TRUE //trust me, you'll be feeling the pain from the maint drugs all well enough
+ return BLESSING_SUCCESS //trust me, you'll be feeling the pain from the maint drugs all well enough
/datum/religion_sect/maintenance/on_sacrifice(obj/item/reagent_containers/offering, mob/living/user)
if(!istype(offering))
@@ -523,3 +541,138 @@
/datum/religion_sect/music/on_conversion(mob/living/chap)
. = ..()
new /obj/item/choice_beacon/music(get_turf(chap))
+
+/datum/religion_sect/dreams
+ name = "Dream God"
+ quote = "The dream is a window into the soul."
+ desc = "Dream deeply to gain insights into the universe. Earn favor by dreaming or blessing dreaming creatures. \
+ Your blessings invoke dreams and promote healing in those who are sound asleep."
+ tgui_icon = FA_ICON_CLOUD
+ alignment = ALIGNMENT_GOOD
+ altar_icon_state = "convertaltar-dream"
+ altar_emissive_icon_state = "convertaltar-dream-em"
+ candle_overlay = FALSE
+ rites_list = list(
+ /datum/religion_rites/deaconize/dreamers,
+ /datum/religion_rites/banish_nightmare,
+ /datum/religion_rites/dream_portent,
+ /datum/religion_rites/dream_projection,
+ /datum/religion_rites/dream_protection,
+ /datum/religion_rites/slumber_party,
+ )
+ smack_chance = 20
+ /// Whether the dream protection rite has been used
+ VAR_FINAL/dream_protection = FALSE
+ /// Number of deacons added thus far
+ VAR_FINAL/deacon_count = 0
+ /// Max number of deacons
+ var/max_deacons = 3
+ /// Chance a given dream will be a vague portent
+ var/vague_portent_chance = 10
+ /// Lazylist of mobs that were blessed recently
+ /// Blocks mobs from being repeatedly blessed for favor
+ VAR_PRIVATE/list/recent_bless_refs
+ /// Cooldown between any follower receiving a vague portent
+ COOLDOWN_DECLARE(vague_portent_cooldown)
+
+/datum/religion_sect/dreams/on_conversion(mob/living/chap)
+ . = ..()
+ RegisterSignal(chap, COMSIG_PRE_DREAMING, PROC_REF(pre_dream))
+ RegisterSignal(chap, COMSIG_START_DREAMING, PROC_REF(on_dream))
+ if(dream_protection)
+ chap.apply_status_effect(/datum/status_effect/dream_protection)
+
+/datum/religion_sect/dreams/on_deconversion(mob/living/chap)
+ . = ..()
+ UnregisterSignal(chap, COMSIG_PRE_DREAMING)
+ UnregisterSignal(chap, COMSIG_START_DREAMING)
+ chap.remove_status_effect(/datum/status_effect/dream_protection)
+
+/datum/religion_sect/dreams/proc/pre_dream(mob/living/chap, list/dream_pool)
+ SIGNAL_HANDLER
+
+ // prioritize specific portents if they're in the pool
+ if(locate(/datum/dream/specific_portent) in dream_pool)
+ return
+ if(!COOLDOWN_FINISHED(src, vague_portent_cooldown))
+ return
+ if(!prob(vague_portent_chance))
+ return
+
+ dream_pool[new /datum/dream/random/vague_portent()] = /datum/dream/random::weight * 0.1
+
+/datum/religion_sect/dreams/proc/on_dream(mob/living/chap, datum/dream/dream_instance)
+ SIGNAL_HANDLER
+
+ // no reward for the dream your god is specifically giving you
+ if(istype(dream_instance, /datum/dream/specific_portent))
+ return
+
+ var/dream_favor = 10
+ // but you do get a slight boost for having a *vague* one
+ if(istype(dream_instance, /datum/dream/random/vague_portent))
+ COOLDOWN_START(src, vague_portent_cooldown, 30 SECONDS)
+ dream_favor *= 1.5
+
+ to_chat(chap, span_cyan("[GLOB.deity] approves of your slumber."))
+ adjust_favor(dream_favor, chap)
+
+// dream blessing only works on dreaming targets.
+// blessing someone asleep causes them to dream, and blessing a dreamer rewards favor.
+// it also heals regardless of if the target is mechanical or organic. do robots dream of electric sheep?
+/datum/religion_sect/dreams/sect_bless(mob/living/target, mob/living/chap)
+ if(HAS_TRAIT(target, TRAIT_DREAMING))
+ var/result = standard_bless_healing(target, chap)
+ var/tarref = REF(target)
+ if(!LAZYFIND(recent_bless_refs, tarref))
+ adjust_favor(20 * (isnull(target.mind) ? 0.5 : 1) * (target.IsSleeping() ? 1 : 0.5), chap)
+ LAZYADD(recent_bless_refs, tarref)
+ addtimer(CALLBACK(src, PROC_REF(clear_bless_ref), tarref), 6 MINUTES)
+ result = BLESSING_SUCCESS
+
+ if(result == BLESSING_SUCCESS)
+ to_chat(chap, span_cyan("[GLOB.deity] approves of [target]'s slumber."))
+ return result
+
+ if(target.stat == UNCONSCIOUS)
+ if(iscarbon(target))
+ var/mob/living/carbon/sleeper = target
+ sleeper.dream()
+
+ to_chat(chap, span_cyan("[GLOB.deity] blesses [target]'s slumber."))
+ var/result = standard_bless_healing(target, chap)
+ if(dream_protection && target.mind && target.apply_status_effect(/datum/status_effect/dream_protection/temporary))
+ result = BLESSING_SUCCESS
+
+ if(result == BLESSING_SUCCESS)
+ to_chat(chap, span_cyan("[GLOB.deity] blesses [target]'s slumber."))
+ return result
+
+ to_chat(chap, span_warning("[GLOB.deity] has no interest in blessing the waking."))
+ return BLESSING_IGNORED
+
+/datum/religion_sect/dreams/sect_dead_bless(mob/living/target, mob/living/chap)
+ var/tarref = REF(target)
+ if(LAZYFIND(recent_bless_refs, tarref))
+ return BLESSING_IGNORED
+
+ to_chat(chap, span_cyan("[GLOB.deity] watches over [target]'s eternal rest."))
+ if(dream_protection && target.mind)
+ target.apply_status_effect(/datum/status_effect/dream_protection/deceased)
+ adjust_favor(10 * (target.mind ? 1 : 0.5), chap)
+ LAZYADD(recent_bless_refs, tarref)
+ addtimer(CALLBACK(src, PROC_REF(clear_bless_ref), tarref), 6 MINUTES)
+ return BLESSING_SUCCESS
+
+/datum/religion_sect/dreams/proc/clear_bless_ref(tarref)
+ LAZYREMOVE(recent_bless_refs, tarref)
+
+/datum/religion_sect/dreams/vv_edit_var(var_name, var_value)
+ . = ..()
+ if(var_name == NAMEOF(src, dream_protection))
+ for(var/mob/living/chap as anything in GLOB.mob_living_list)
+ if(chap.mind?.holy_role)
+ if(var_value)
+ chap.apply_status_effect(/datum/status_effect/dream_protection)
+ else
+ chap.remove_status_effect(/datum/status_effect/dream_protection)
diff --git a/code/modules/religion/religion_structures.dm b/code/modules/religion/religion_structures.dm
index 1b30d021268c..2d3625f230da 100644
--- a/code/modules/religion/religion_structures.dm
+++ b/code/modules/religion/religion_structures.dm
@@ -11,6 +11,10 @@
buckle_lying = 90 //we turn to you!
///Avoids having to check global everytime by referencing it locally.
var/datum/religion_sect/sect_to_altar
+ /// Do we have lit candles?
+ var/lit_candles = TRUE
+ /// Optional emissive overlay
+ var/emissive_icon_state
/obj/structure/altar_of_gods/Initialize(mapload)
. = ..()
@@ -24,6 +28,14 @@
GLOB.chaplain_altars -= src
return ..()
+/obj/structure/altar_of_gods/update_overlays()
+ . = ..()
+ if (lit_candles)
+ . += mutable_appearance(icon, "convertaltarcandle", alpha = src.alpha)
+ . += emissive_appearance(icon, "convertaltarcandle", src, alpha = src.alpha)
+ if(emissive_icon_state)
+ . += emissive_appearance(icon, emissive_icon_state, src, alpha = src.alpha)
+
/obj/structure/altar_of_gods/update_overlays()
var/list/new_overlays = ..()
if(GLOB.religious_sect)
@@ -65,12 +77,16 @@
if(isnull(GLOB.religious_sect))
icon = initial(icon)
icon_state = initial(icon_state)
+ emissive_icon_state = initial(emissive_icon_state)
else
sect_to_altar = GLOB.religious_sect
+ lit_candles = GLOB.religious_sect.candle_overlay
if(sect_to_altar.altar_icon)
icon = sect_to_altar.altar_icon
if(sect_to_altar.altar_icon_state)
icon_state = sect_to_altar.altar_icon_state
+ if(sect_to_altar.altar_emissive_icon_state)
+ emissive_icon_state = sect_to_altar.altar_emissive_icon_state
update_appearance() //Light the candles!
/obj/structure/altar_of_gods/proc/get_chaplains()
diff --git a/code/modules/religion/rites.dm b/code/modules/religion/rites.dm
index 14fd752eb3e2..d6725692c724 100644
--- a/code/modules/religion/rites.dm
+++ b/code/modules/religion/rites.dm
@@ -10,8 +10,10 @@
/// message when you invoke
var/invoke_msg
var/favor_cost = 0
- /// does the altar auto-delete the rite
- var/auto_delete = TRUE
+
+ ///Rite flags we use mostly to know when it should be deleted.
+ // RITE_AUTO_DELETE | RITE_ALLOW_MULTIPLE_PERFORMS | RITE_ONE_TIME_USE
+ var/rite_flags = RITE_AUTO_DELETE
/datum/religion_rites/New()
. = ..()
@@ -61,9 +63,18 @@
///Does the thing if the rite was successfully performed. return value denotes that the effect successfully (IE a harm rite does harm)
/datum/religion_rites/proc/invoke_effect(mob/living/user, atom/religious_tool)
SHOULD_CALL_PARENT(TRUE)
- GLOB.religious_sect.on_riteuse(user,religious_tool)
+ GLOB.religious_sect.on_riteuse(user, religious_tool)
return TRUE
+///Called if invoke effect returns TRUE, for effects meant to occur only if the rite passes.
+/datum/religion_rites/proc/post_invoke_effects(mob/living/user, atom/religious_tool)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!(rite_flags & RITE_ONE_TIME_USE))
+ return
+ GLOB.religious_sect.rites_list.Remove(src.type)
+
+/datum/religion_rites/proc/refund(percent = 1.0)
+ GLOB.religious_sect.adjust_favor(favor_cost * percent)
/**** Mechanical God ****/
@@ -120,8 +131,10 @@
name = "Receive Blessing"
desc = "Receive a blessing from the machine god to further your ascension."
ritual_length = 5 SECONDS
- ritual_invocations =list( "Let your will power our forges.",
- "...Help us in our great conquest!")
+ ritual_invocations = list(
+ "Let your will power our forges.",
+ "...Help us in our great conquest!",
+ )
invoke_msg = "The end of flesh is near!"
favor_cost = 2000
@@ -208,7 +221,7 @@
return FALSE
//uses HAS_TRAIT_FROM because junkies are also hopelessly addicted
if(HAS_TRAIT_FROM(user, TRAIT_HOPELESSLY_ADDICTED, "maint_adaptation"))
- to_chat(user, span_warning("You've already adapted."))
+ to_chat(user, span_warning("You've already adapted."))
return FALSE
return ..()
diff --git a/code/modules/research/ordnance/doppler_array.dm b/code/modules/research/ordnance/doppler_array.dm
index 1c59c654c70d..3bdda576c05d 100644
--- a/code/modules/research/ordnance/doppler_array.dm
+++ b/code/modules/research/ordnance/doppler_array.dm
@@ -283,7 +283,7 @@
// Make sure the list is indexed first.
if(reaction_data.len)
for (var/path in reaction_data[TANK_RESULTS_REACTION])
- var/datum/gas_reaction/reaction_path = path
+ var/datum/gas_reaction/standard/reaction_path = path
record_data["reaction_results"] += initial(reaction_path.name)
if(TANK_MERGE_OVERPRESSURE in reaction_data[TANK_RESULTS_MISC])
record_data["reaction_results"] += "Tank overpressurized before reaction"
diff --git a/code/modules/research/ordnance/tank_compressor.dm b/code/modules/research/ordnance/tank_compressor.dm
index 1461a535404f..359efc9c8e23 100644
--- a/code/modules/research/ordnance/tank_compressor.dm
+++ b/code/modules/research/ordnance/tank_compressor.dm
@@ -170,8 +170,8 @@
new_record.name = "Log Recording #[record_number]"
new_record.experiment_source = inserted_tank.name
new_record.timestamp = station_time_timestamp()
- for(var/gas_path in leaked_gas_buffer.gases)
- new_record.gas_data[gas_path] = leaked_gas_buffer.gases[gas_path][MOLES]
+ for(var/gas_path, amount in leaked_gas_buffer.moles)
+ new_record.gas_data[gas_path] = amount
compressor_record += new_record
record_number += 1
diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm
index fa40392135d3..dc1ed32d8541 100644
--- a/code/modules/research/xenobiology/crossbreeding/chilling.dm
+++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm
@@ -113,7 +113,7 @@ Chilling extracts:
var/datum/gas_mixture/G = T.air
if(istype(G))
G.assert_gas(/datum/gas/plasma)
- G.gases[/datum/gas/plasma][MOLES] = 0
+ G.moles[/datum/gas/plasma] = 0
filtered = TRUE
G.garbage_collect()
T.air_update_turf(FALSE, FALSE)
diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm
index 876eacfd3ad2..5e3db3ca148b 100644
--- a/code/modules/surgery/organs/internal/heart/_heart.dm
+++ b/code/modules/surgery/organs/internal/heart/_heart.dm
@@ -557,3 +557,66 @@
owner.heal_overall_damage(brute = 15, burn = 15, required_bodytype = BODYTYPE_ORGANIC)
if(owner.reagents.get_reagent_amount(/datum/reagent/medicine/ephedrine) < 20)
owner.reagents.add_reagent(/datum/reagent/medicine/ephedrine, 10)
+
+/// An improved version of the organic heart, with more health and more "keeping you alive" potential
+/obj/item/organ/heart/evolved
+ name = "evolved heart"
+ desc = "It beats ever strong."
+ icon_state = "heart-evolved-on"
+ base_icon_state = "heart-evolved"
+ maxHealth = STANDARD_ORGAN_THRESHOLD * 1.2
+ /// Chance to heal per on_life
+ var/healing_probability = 10
+ /// Base healing we receive per tick at 0 damage and for standard versions
+ var/base_healing = 1
+
+/obj/item/organ/heart/evolved/on_life(seconds_per_tick)
+ . = ..()
+
+ if(prob(healing_probability * seconds_per_tick))
+ var/damage_to_heal = base_healing * ((maxHealth - damage) / initial(maxHealth)) * seconds_per_tick
+ owner.heal_overall_damage(damage_to_heal, damage_to_heal, required_bodytype = BODYTYPE_ORGANIC)
+
+ if(owner.stat == HARD_CRIT && !owner.has_reagent(/datum/reagent/medicine/atropine, 5))
+ owner.reagents.add_reagent(/datum/reagent/medicine/atropine, 1 * seconds_per_tick)
+
+/// A weaker evolved heart, but can block magic in exchange for our organs health!
+/obj/item/organ/heart/evolved/sacred
+ name = "sacred heart"
+ desc = "Your foul magics stand no chance against the power of LOVE!!!"
+
+ icon_state = "heart-sacred-on"
+ base_icon_state = "heart-sacred"
+
+ healing_probability = 5
+ base_healing = 0.5
+ // How much damage each magic block deals to us
+ var/damage_per_block = 50
+
+/obj/item/organ/heart/evolved/sacred/on_life(seconds_per_tick)
+ . = ..()
+
+ if(IS_CULTIST(owner))
+ owner.reagents.add_reagent(/datum/reagent/water/holywater, 5 * seconds_per_tick)
+
+/obj/item/organ/heart/evolved/sacred/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
+ . = ..()
+
+ receiver.AddComponent(/datum/component/anti_magic, block_magic = CALLBACK(src, PROC_REF(on_blocked)), check_blocking = CALLBACK(src, PROC_REF(check_block)))
+
+/obj/item/organ/heart/evolved/sacred/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
+ . = ..()
+
+ qdel(organ_owner.GetComponent(/datum/component/anti_magic))
+
+/// When we blocked damage, do PAIN on us
+/obj/item/organ/heart/evolved/sacred/proc/on_blocked()
+ apply_organ_damage(damage_per_block)
+ owner.vomit(VOMIT_CATEGORY_BLOOD)
+ playsound(owner, 'sound/health/slowbeat.ogg', 80)
+
+/// We don't block magic if it would kill our heart
+/obj/item/organ/heart/evolved/sacred/proc/check_block()
+ if(maxHealth - damage <= damage_per_block)
+ return FALSE
+ return TRUE
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index 8cb1efebc3f1..06c41294c428 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -267,7 +267,7 @@
// Not safe to check the old pp because of can_breath_vacuum
breather.throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
- var/gas_breathed = handle_suffocation(breather, o2_pp, safe_oxygen_min, breath.gases[/datum/gas/oxygen][MOLES])
+ var/gas_breathed = handle_suffocation(breather, o2_pp, safe_oxygen_min, breath.moles[/datum/gas/oxygen])
if(o2_pp)
breathe_gas_volume(breath, /datum/gas/oxygen, /datum/gas/carbon_dioxide, volume = gas_breathed)
return
@@ -289,7 +289,7 @@
return BREATH_LOST
return
- var/ratio = (breath.gases[/datum/gas/oxygen][MOLES] / safe_oxygen_max) * 10
+ var/ratio = (breath.moles[/datum/gas/oxygen] / safe_oxygen_max) * 10
breather.apply_damage(clamp(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type, spread_damage = TRUE)
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
breather.throw_alert(ALERT_TOO_MUCH_OXYGEN, /atom/movable/screen/alert/too_much_oxy)
@@ -313,7 +313,7 @@
// Not safe to check the old pp because of can_breath_vacuum
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
breather.throw_alert(ALERT_NOT_ENOUGH_NITRO, /atom/movable/screen/alert/not_enough_nitro)
- var/gas_breathed = handle_suffocation(breather, nitro_pp, safe_nitro_min, breath.gases[/datum/gas/nitrogen][MOLES])
+ var/gas_breathed = handle_suffocation(breather, nitro_pp, safe_nitro_min, breath.moles[/datum/gas/nitrogen])
if(nitro_pp)
breathe_gas_volume(breath, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, volume = gas_breathed)
return
@@ -366,7 +366,7 @@
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
breather.throw_alert(ALERT_NOT_ENOUGH_PLASMA, /atom/movable/screen/alert/not_enough_plas)
// Breathe insufficient amount of Plasma, exhale CO2.
- var/gas_breathed = handle_suffocation(breather, plasma_pp, safe_plasma_min, breath.gases[/datum/gas/plasma][MOLES])
+ var/gas_breathed = handle_suffocation(breather, plasma_pp, safe_plasma_min, breath.moles[/datum/gas/plasma])
if(plasma_pp)
breathe_gas_volume(breath, /datum/gas/plasma, /datum/gas/carbon_dioxide, volume = gas_breathed)
return
@@ -390,7 +390,7 @@
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
breather.throw_alert(ALERT_TOO_MUCH_PLASMA, /atom/movable/screen/alert/too_much_plas)
- var/ratio = (breath.gases[/datum/gas/plasma][MOLES] / safe_plasma_max) * 10
+ var/ratio = (breath.moles[/datum/gas/plasma] / safe_plasma_max) * 10
breather.apply_damage(clamp(ratio, plas_breath_dam_min, plas_breath_dam_max), plas_damage_type, spread_damage = TRUE)
/// Resets plasma side effects
@@ -553,7 +553,7 @@
/// Radioactive, green gas. Toxin damage, and a radiation chance
/obj/item/organ/lungs/proc/too_much_tritium(mob/living/carbon/breather, datum/gas_mixture/breath, trit_pp, old_trit_pp)
var/gas_breathed = breathe_gas_volume(breath, /datum/gas/tritium)
- var/moles_visible = GLOB.meta_gas_info[/datum/gas/tritium][META_GAS_MOLES_VISIBLE] * BREATH_PERCENTAGE
+ var/moles_visible = GLOB.meta_gas_info[META_GAS_MOLES_VISIBLE][/datum/gas/tritium] * BREATH_PERCENTAGE
// Tritium side-effects.
if(gas_breathed > moles_visible)
var/ratio = gas_breathed * 15
@@ -642,7 +642,7 @@
apply_organ_damage(maxHealth * 0.075)
// The list of gases in the breath.
- var/list/breath_gases = breath.gases
+ var/list/breath_moles = breath.moles
// Copy the breath's temperature into breath_out to avoid cooling the output breath down unfairly
breath_out.temperature = breath.temperature
@@ -654,8 +654,8 @@
// Build out our partial pressures, for use as we go
var/list/partial_pressures = list()
- for(var/gas_id in breath_gases)
- partial_pressures[gas_id] = breath.get_breath_partial_pressure(breath_gases[gas_id][MOLES] * received_pressure_mult)
+ for(var/gas_id, amount in breath_moles)
+ partial_pressures[gas_id] = breath.get_breath_partial_pressure(amount * received_pressure_mult)
// Treat gas as other types of gas
for(var/list/conversion_packet in treat_as)
@@ -673,32 +673,31 @@
var/partial_pressure = partial_pressures[breath_id] || 0
var/old_partial_pressure = last_partial_pressures[breath_id] || 0
// Ensures the gas will always be instanciated, so people can interact with it safely
- ASSERT_GAS(breath_id, breath)
var/inhale = breathe_always[breath_id]
call(src, inhale)(breather, breath, partial_pressure, old_partial_pressure)
// Now we'll handle the callbacks that want to be run conditionally off our current breath
- for(var/breath_id in breath_gases)
- var/when_present = breath_present[breath_id]
+ for(var/gas_id in breath_moles)
+ var/when_present = breath_present[gas_id]
if(!when_present)
continue
- var/reaction = call(src, when_present)(breather, breath, partial_pressures[breath_id], last_partial_pressures[breath_id])
+ var/reaction = call(src, when_present)(breather, breath, partial_pressures[gas_id], last_partial_pressures[gas_id])
if(reaction == BREATH_LOST)
- var/on_lose = breath_lost[breath_id]
+ var/on_lose = breath_lost[gas_id]
if(on_lose)
- call(src, on_lose)(breather, breath, partial_pressures[breath_id], last_partial_pressures[breath_id])
+ call(src, on_lose)(breather, breath, partial_pressures[gas_id], last_partial_pressures[gas_id])
// Finally, we'll run the callbacks that aren't in breath_gases, but WERE in our last breath
- for(var/gas_lost in last_partial_pressures)
+ for(var/gas_id in last_partial_pressures)
// If we still have it, go away
- if(breath_gases[gas_lost])
+ if(breath_moles[gas_id])
continue
- var/on_loss = breath_lost[gas_lost]
+ var/on_loss = breath_lost[gas_id]
if(!on_loss)
continue
- call(src, on_loss)(breather, breath, last_partial_pressures[gas_lost])
+ call(src, on_loss)(breather, breath, last_partial_pressures[gas_id])
src.last_partial_pressures = partial_pressures
@@ -717,12 +716,11 @@
/// Removes 100% of the given gas type unless given a volume argument.
/// Returns the amount of gas theoretically removed.
/obj/item/organ/lungs/proc/breathe_gas_volume(datum/gas_mixture/breath, remove_id, exchange_id = null, volume = INFINITY)
- var/list/breath_gases = breath.gases
- volume = min(volume, breath_gases[remove_id][MOLES])
- breath_gases[remove_id][MOLES] -= volume
+ var/list/breath_moles = breath.moles
+ volume = min(volume, breath_moles[remove_id])
+ breath_moles[remove_id] -= volume
if(exchange_id)
- ASSERT_GAS(exchange_id, breath_out)
- breath_out.gases[exchange_id][MOLES] += volume
+ breath_out.moles[exchange_id] += volume
return volume
/// Handles what happens when we breathe in successfully, before we parse through the gases to determine specific side effects
@@ -970,8 +968,8 @@
/obj/item/organ/lungs/slime/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather_slime, skip_breath)
. = ..()
- if (breath?.gases[/datum/gas/plasma] && !skip_breath)
- var/plasma_pp = breath.get_breath_partial_pressure(breath.gases[/datum/gas/plasma][MOLES])
+ if (breath?.moles[/datum/gas/plasma])
+ var/plasma_pp = breath.get_breath_partial_pressure(breath.moles[/datum/gas/plasma])
breather_slime.blood_volume += (0.2 * plasma_pp) // 10/s when breathing literally nothing but plasma, which will suffocate you.
/obj/item/organ/lungs/smoker_lungs
@@ -1054,7 +1052,7 @@
// Take a "breath" of the air
var/datum/gas_mixture/breath = mix.remove(mix.total_moles() * BREATH_PERCENTAGE)
- var/list/breath_gases = breath.gases
+ var/list/breath_moles = breath.moles
breath.assert_gases(
/datum/gas/oxygen,
@@ -1065,12 +1063,12 @@
/datum/gas/miasma,
)
- var/oxygen_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen][MOLES])
- var/nitrogen_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen][MOLES])
- var/plasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma][MOLES])
- var/carbon_dioxide_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide][MOLES])
- var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
- var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES])
+ var/oxygen_pp = breath.get_breath_partial_pressure(breath_moles[/datum/gas/oxygen])
+ var/nitrogen_pp = breath.get_breath_partial_pressure(breath_moles[/datum/gas/nitrogen])
+ var/plasma_pp = breath.get_breath_partial_pressure(breath_moles[/datum/gas/plasma])
+ var/carbon_dioxide_pp = breath.get_breath_partial_pressure(breath_moles[/datum/gas/carbon_dioxide])
+ var/bz_pp = breath.get_breath_partial_pressure(breath_moles[/datum/gas/bz])
+ var/miasma_pp = breath.get_breath_partial_pressure(breath_moles[/datum/gas/miasma])
safe_oxygen_min = max(0, oxygen_pp - GAS_TOLERANCE)
safe_nitro_min = max(0, nitrogen_pp - GAS_TOLERANCE)
@@ -1150,11 +1148,10 @@
/// H2O electrolysis
/obj/item/organ/lungs/ethereal/proc/consume_water(mob/living/carbon/breather, datum/gas_mixture/breath, h2o_pp, old_h2o_pp)
- var/gas_breathed = breath.gases[/datum/gas/water_vapor][MOLES]
- breath.gases[/datum/gas/water_vapor][MOLES] -= gas_breathed
- breath_out.assert_gases(/datum/gas/oxygen, /datum/gas/hydrogen)
- breath_out.gases[/datum/gas/oxygen][MOLES] += gas_breathed
- breath_out.gases[/datum/gas/hydrogen][MOLES] += gas_breathed * 2
+ var/gas_breathed = breath.moles[/datum/gas/water_vapor]
+ breath.adjust_gas(/datum/gas/water_vapor, -gas_breathed)
+ var/list/new_gases = list(/datum/gas/oxygen = gas_breathed, /datum/gas/hydrogen = gas_breathed * 2)
+ breath_out.adjust_multiple_gases(new_gases)
#undef BREATH_RELATIONSHIP_INITIAL_GAS
diff --git a/code/modules/unit_tests/breath.dm b/code/modules/unit_tests/breath.dm
index faba1a08e22e..9270f9bbfe28 100644
--- a/code/modules/unit_tests/breath.dm
+++ b/code/modules/unit_tests/breath.dm
@@ -43,7 +43,7 @@
lab_rat = allocate(/mob/living/carbon/human/consistent)
source = equip_labrat_internals(lab_rat, /obj/item/tank/internals/emergency_oxygen/empty)
source.air_contents.assert_gas(/datum/gas/nitrogen)
- source.air_contents.gases[/datum/gas/nitrogen][MOLES] = (10 * ONE_ATMOSPHERE) * source.volume / (R_IDEAL_GAS_EQUATION * T20C)
+ source.air_contents.moles[/datum/gas/nitrogen] = (10 * ONE_ATMOSPHERE) * source.volume / (R_IDEAL_GAS_EQUATION * T20C)
TEST_ASSERT(source.toggle_internals(lab_rat) && !isnull(lab_rat.internal), "Plasmaman toggle_internals() failed to toggle internals")
lab_rat.breathe()
TEST_ASSERT(lab_rat.failed_last_breath && lab_rat.has_alert(ALERT_NOT_ENOUGH_OXYGEN), "Humans should suffocate from pure n2 tanks")
@@ -77,7 +77,7 @@
lab_rat = allocate(/mob/living/carbon/human/species/plasma)
source = equip_labrat_internals(lab_rat, /obj/item/tank/internals/emergency_oxygen/empty)
source.air_contents.assert_gas(/datum/gas/nitrogen)
- source.air_contents.gases[/datum/gas/nitrogen][MOLES] = (10 * ONE_ATMOSPHERE) * source.volume / (R_IDEAL_GAS_EQUATION * T20C)
+ source.air_contents.moles[/datum/gas/nitrogen] = (10 * ONE_ATMOSPHERE) * source.volume / (R_IDEAL_GAS_EQUATION * T20C)
TEST_ASSERT(source.toggle_internals(lab_rat) && !isnull(lab_rat.internal), "Plasmaman toggle_internals() failed to toggle internals")
lab_rat.breathe()
TEST_ASSERT(lab_rat.failed_last_breath && lab_rat.has_alert(ALERT_NOT_ENOUGH_PLASMA), "Humans should suffocate from pure n2 tanks")
diff --git a/code/modules/unit_tests/gas_transfer.dm b/code/modules/unit_tests/gas_transfer.dm
index 2b174ad8c625..06f4afd587ca 100644
--- a/code/modules/unit_tests/gas_transfer.dm
+++ b/code/modules/unit_tests/gas_transfer.dm
@@ -25,13 +25,10 @@
first_mix.volume = 200
second_mix.volume = 200
- ASSERT_GAS(/datum/gas/hypernoblium, first_mix)
- ASSERT_GAS(/datum/gas/tritium, second_mix)
-
- first_mix.gases[/datum/gas/hypernoblium][MOLES] = nob_moles
+ first_mix.moles[/datum/gas/hypernoblium] = nob_moles
first_mix.temperature = nob_temp
- second_mix.gases[/datum/gas/tritium][MOLES] = trit_moles
+ second_mix.moles[/datum/gas/tritium] = trit_moles
second_mix.temperature = trit_temp
var/initial_pressure = second_mix.return_pressure()
diff --git a/code/modules/unit_tests/lungs.dm b/code/modules/unit_tests/lungs.dm
index c8338b6dec88..d7f9bf48f2ae 100644
--- a/code/modules/unit_tests/lungs.dm
+++ b/code/modules/unit_tests/lungs.dm
@@ -1,7 +1,7 @@
#define TEST_CHECK_BREATH_MESSAGE(lungs_organ, message) "[lungs_organ.type]/check_breath() [message]"
#define TEST_ALERT_THROW_MESSAGE(lungs_organ, alert_name) TEST_CHECK_BREATH_MESSAGE(lungs_organ, "failed to throw alert [alert_name] when expected.")
#define TEST_ALERT_INHIBIT_MESSAGE(lungs_organ, alert_name) TEST_CHECK_BREATH_MESSAGE(lungs_organ, "threw alert [alert_name] when it wasn't expected.")
-#define GET_MOLES(gas_mixture, gas_type) (gas_mixture.gases[gas_type] ? gas_mixture.gases[gas_type][MOLES] : 0)
+#define GET_MOLES(gas_mixture, gas_type) (gas_mixture.moles[gas_type] || 0)
/// Tests the standard, plasmaman, and lavaland lungs organ to ensure breathing and suffocation behave as expected.
/// Performs a check on each main (can be life-sustaining) gas, and ensures gas alerts are only thrown when expected.
@@ -174,7 +174,7 @@
test_mix.temperature = T20C
for(var/datum/gas/gas_type as anything in gas_to_percent)
test_mix.add_gas(gas_type)
- test_mix.gases[gas_type][MOLES] = (ONE_ATMOSPHERE * 2500 / (R_IDEAL_GAS_EQUATION * T20C) * gas_to_percent[gas_type])
+ test_mix.moles[gas_type] = (ONE_ATMOSPHERE * 2500 / (R_IDEAL_GAS_EQUATION * T20C) * gas_to_percent[gas_type])
return test_mix
/// Set up an O2/N2 gas mix which is "ideal" for organic life.
diff --git a/code/modules/vehicles/mecha/equipment/tools/air_tank.dm b/code/modules/vehicles/mecha/equipment/tools/air_tank.dm
index f00444ae598b..70926b12e304 100644
--- a/code/modules/vehicles/mecha/equipment/tools/air_tank.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/air_tank.dm
@@ -31,9 +31,8 @@
internal_tank.air_contents.volume = volume
internal_tank.maximum_pressure = maximum_pressure
if(start_full)
- internal_tank.air_contents.temperature = T20C
- internal_tank.air_contents.add_gases(/datum/gas/oxygen)
- internal_tank.air_contents.gases[/datum/gas/oxygen][MOLES] = maximum_pressure * volume / (R_IDEAL_GAS_EQUATION * internal_tank.air_contents.temperature)
+ internal_tank.air_contents.set_temperature(T20C)
+ internal_tank.air_contents.set_gas(/datum/gas/oxygen, maximum_pressure * volume / (R_IDEAL_GAS_EQUATION * internal_tank.air_contents.temperature))
/obj/item/mecha_parts/mecha_equipment/air_tank/Destroy()
if(chassis)
diff --git a/icons/obj/machines/atmospherics/bluespace_gas_selling.dmi b/icons/obj/machines/atmospherics/bluespace_gas_selling.dmi
deleted file mode 100644
index c397dd2dc841..000000000000
Binary files a/icons/obj/machines/atmospherics/bluespace_gas_selling.dmi and /dev/null differ
diff --git a/icons/obj/service/hand_of_god_structures.dmi b/icons/obj/service/hand_of_god_structures.dmi
index cbbc36d77188..e9d94cef78f9 100644
Binary files a/icons/obj/service/hand_of_god_structures.dmi and b/icons/obj/service/hand_of_god_structures.dmi differ
diff --git a/interface/stylesheet.dm b/interface/stylesheet.dm
index 25858be15146..eb8fd5081d62 100644
--- a/interface/stylesheet.dm
+++ b/interface/stylesheet.dm
@@ -164,4 +164,6 @@ h1.alert, h2.alert {color: #000000;}
.resonate {color: #298F85;}
.upside_down {display: inline; -moz-transform: scale(-1, -1); -webkit-transform: scale(-1, -1); -o-transform: scale(-1, -1); -ms-transform: scale(-1, -1); transform: scale(-1, -1);}
+
+.cyan {color: #bde0dc;}
"}
diff --git a/maplestation.dme b/maplestation.dme
index 65fa8cacbaa4..bc67efc11ab2 100644
--- a/maplestation.dme
+++ b/maplestation.dme
@@ -2381,7 +2381,6 @@
#include "code\game\objects\items\maintenance_loot.dm"
#include "code\game\objects\items\manuals.dm"
#include "code\game\objects\items\mop.dm"
-#include "code\game\objects\items\nitrium_crystals.dm"
#include "code\game\objects\items\paint.dm"
#include "code\game\objects\items\paiwire.dm"
#include "code\game\objects\items\pet_carrier.dm"
@@ -3230,6 +3229,7 @@
#include "code\modules\antagonists\heretic\magic\wave_of_desperation.dm"
#include "code\modules\antagonists\heretic\status_effects\buffs.dm"
#include "code\modules\antagonists\heretic\status_effects\debuffs.dm"
+#include "code\modules\antagonists\heretic\status_effects\dreams.dm"
#include "code\modules\antagonists\heretic\status_effects\ghoul.dm"
#include "code\modules\antagonists\heretic\status_effects\mark_effects.dm"
#include "code\modules\antagonists\heretic\structures\carving_knife.dm"
@@ -3412,7 +3412,6 @@
#include "code\modules\atmospherics\gasmixtures\reaction_factors.dm"
#include "code\modules\atmospherics\gasmixtures\reactions.dm"
#include "code\modules\atmospherics\machinery\atmosmachinery.dm"
-#include "code\modules\atmospherics\machinery\bluespace_vendor.dm"
#include "code\modules\atmospherics\machinery\datum_pipeline.dm"
#include "code\modules\atmospherics\machinery\air_alarm\_air_alarm.dm"
#include "code\modules\atmospherics\machinery\air_alarm\air_alarm_circuit.dm"
@@ -3446,7 +3445,6 @@
#include "code\modules\atmospherics\machinery\components\trinary_devices\mixer.dm"
#include "code\modules\atmospherics\machinery\components\trinary_devices\trinary_devices.dm"
#include "code\modules\atmospherics\machinery\components\unary_devices\airlock_pump.dm"
-#include "code\modules\atmospherics\machinery\components\unary_devices\bluespace_sender.dm"
#include "code\modules\atmospherics\machinery\components\unary_devices\cryo.dm"
#include "code\modules\atmospherics\machinery\components\unary_devices\heat_exchanger.dm"
#include "code\modules\atmospherics\machinery\components\unary_devices\machine_connector.dm"
@@ -4127,7 +4125,7 @@
#include "code\modules\fishing\fish\types\tiziran.dm"
#include "code\modules\fishing\sources\_fish_source.dm"
#include "code\modules\fishing\sources\source_types.dm"
-#include "code\modules\flufftext\Dreaming.dm"
+#include "code\modules\flufftext\dreaming.dm"
#include "code\modules\food_and_drinks\pizzabox.dm"
#include "code\modules\food_and_drinks\plate.dm"
#include "code\modules\food_and_drinks\machinery\coffeemaker.dm"
@@ -5625,11 +5623,18 @@
#include "code\modules\recycling\disposal\outlet.dm"
#include "code\modules\recycling\disposal\pipe.dm"
#include "code\modules\recycling\disposal\pipe_sorting.dm"
+#include "code\modules\religion\deaconize.dm"
#include "code\modules\religion\religion_sects.dm"
#include "code\modules\religion\religion_structures.dm"
#include "code\modules\religion\rites.dm"
#include "code\modules\religion\burdened\burdened_trauma.dm"
#include "code\modules\religion\burdened\psyker.dm"
+#include "code\modules\religion\dreams\banish_nightmare.dm"
+#include "code\modules\religion\dreams\deaconize_dreamer.dm"
+#include "code\modules\religion\dreams\dream_portent.dm"
+#include "code\modules\religion\dreams\dream_projection.dm"
+#include "code\modules\religion\dreams\dream_protection.dm"
+#include "code\modules\religion\dreams\slumber_party.dm"
#include "code\modules\religion\festival\festival_violin.dm"
#include "code\modules\religion\festival\instrument_rites.dm"
#include "code\modules\religion\honorbound\honorbound_rites.dm"
diff --git a/maplestation_modules/code/game/machinery/cloning_pod.dm b/maplestation_modules/code/game/machinery/cloning_pod.dm
index 08b6f3367587..7eab2ac148f3 100644
--- a/maplestation_modules/code/game/machinery/cloning_pod.dm
+++ b/maplestation_modules/code/game/machinery/cloning_pod.dm
@@ -284,8 +284,8 @@
/datum/gas_mixture/immutable/cloner/garbage_collect()
. = ..()
- ASSERT_GAS(/datum/gas/nitrogen, src)
- gases[/datum/gas/nitrogen][MOLES] = MOLES_O2STANDARD + MOLES_N2STANDARD
+ assert_gas(/datum/gas/nitrogen)
+ set_gas(/datum/gas/nitrogen, MOLES_O2STANDARD + MOLES_N2STANDARD)
/datum/gas_mixture/immutable/cloner/heat_capacity()
return (MOLES_O2STANDARD + MOLES_N2STANDARD) * 20 //specific heat of nitrogen is 20
diff --git a/maplestation_modules/code/modules/smells/_smell.dm b/maplestation_modules/code/modules/smells/_smell.dm
index e645a49dcad6..92cba48d5683 100644
--- a/maplestation_modules/code/modules/smells/_smell.dm
+++ b/maplestation_modules/code/modules/smells/_smell.dm
@@ -146,3 +146,14 @@
description = "What a nice smell."
mood_change = 1
timeout = 40 SECONDS
+
+/datum/smell/vomit
+ text = "vomit"
+ category = "stench"
+
+/datum/smell/vomit/on_smell(mob/living/whom, intensity)
+ if(isnull(whom.mob_mood) || whom.mob_mood.has_mood_of_category(text))
+ return
+
+ whom.adjust_disgust(intensity)
+ whom.add_mood_event(text, /datum/mood_event/disgust/minor_bad_smell)
diff --git a/maplestation_modules/code/modules/smells/smell_helpers.dm b/maplestation_modules/code/modules/smells/smell_helpers.dm
index 3376b07804b7..e6aba796b6f0 100644
--- a/maplestation_modules/code/modules/smells/smell_helpers.dm
+++ b/maplestation_modules/code/modules/smells/smell_helpers.dm
@@ -51,13 +51,14 @@
// the turf has its own smells affecting it, but we also need to factor in smells from gases present
var/list/collective_smells_with_gasses = LAZYLISTDUPLICATE(smellable.collective_smells)
var/pressuremod = 0
- for(var/datum/gas/gas_type as anything in air.gases)
+ for(var/gas_id, mole_count in air.moles)
+ var/datum/gas/gas_type = gas_id
if(!gas_type::smell)
continue
pressuremod ||= clamp(round(air.return_pressure() / ONE_ATMOSPHERE, 0.1), 0.1, 4.0)
var/datum/smell/gas_smell = get_smell(gas_type::smell)
- switch(air.gases[gas_type][MOLES] / total_moles)
+ switch(mole_count / total_moles)
if(0.05 to 0.25)
LAZYADDASSOC(collective_smells_with_gasses, gas_smell, (SMELL_INTENSITY_WEAK * pressuremod))
if(0.25 to 0.5)
diff --git a/maplestation_modules/code/modules/surgery/bodyparts/cyber_arms.dm b/maplestation_modules/code/modules/surgery/bodyparts/cyber_arms.dm
index 86bd6b8a8f1a..e14b0ac9ef40 100644
--- a/maplestation_modules/code/modules/surgery/bodyparts/cyber_arms.dm
+++ b/maplestation_modules/code/modules/surgery/bodyparts/cyber_arms.dm
@@ -4,13 +4,13 @@
desc = "An advanced robotic arm with in built sharp claws. Makes you formidable in close combat, \
though unfortunately the claws are not retractable, and may make it difficult to manipulate small objects."
id = "clawed_advanced_r_arm"
- build_path = /obj/item/bodypart/arm/left/robot/advanced/claws
+ build_path = /obj/item/bodypart/arm/right/robot/advanced/claws
/datum/design/advanced_l_arm/clawed
name = "Advanced Clawed Left Arm"
desc = /datum/design/advanced_r_arm/clawed::desc
id = "clawed_advanced_l_arm"
- build_path = /obj/item/bodypart/arm/right/robot/advanced/claws
+ build_path = /obj/item/bodypart/arm/left/robot/advanced/claws
/datum/design/advanced_r_arm/lifting
name = "Advanced Lifting Right Arm"
@@ -18,12 +18,12 @@
particularly grappling - and better at construction of large objects, \
though unfortunately the bulkier design may make it difficult to manipulate small objects."
id = "punchy_advanced_r_arm"
- build_path = /obj/item/bodypart/arm/left/robot/advanced/lifting
+ build_path = /obj/item/bodypart/arm/right/robot/advanced/lifting
/datum/design/advanced_l_arm/lifting
name = "Advanced Lifting Left Arm"
id = "punchy_advanced_l_arm"
- build_path = /obj/item/bodypart/arm/right/robot/advanced/lifting
+ build_path = /obj/item/bodypart/arm/left/robot/advanced/lifting
// Limbs
/obj/item/bodypart/arm/left/robot/advanced/claws
diff --git a/tgstation.dme b/tgstation.dme
index cde06e9a327d..63c7de06d550 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3241,6 +3241,7 @@
#include "code\modules\antagonists\heretic\magic\wave_of_desperation.dm"
#include "code\modules\antagonists\heretic\status_effects\buffs.dm"
#include "code\modules\antagonists\heretic\status_effects\debuffs.dm"
+#include "code\modules\antagonists\heretic\status_effects\dreams.dm"
#include "code\modules\antagonists\heretic\status_effects\ghoul.dm"
#include "code\modules\antagonists\heretic\status_effects\mark_effects.dm"
#include "code\modules\antagonists\heretic\structures\carving_knife.dm"
@@ -5643,11 +5644,18 @@
#include "code\modules\recycling\disposal\outlet.dm"
#include "code\modules\recycling\disposal\pipe.dm"
#include "code\modules\recycling\disposal\pipe_sorting.dm"
+#include "code\modules\religion\deaconize.dm"
#include "code\modules\religion\religion_sects.dm"
#include "code\modules\religion\religion_structures.dm"
#include "code\modules\religion\rites.dm"
#include "code\modules\religion\burdened\burdened_trauma.dm"
#include "code\modules\religion\burdened\psyker.dm"
+#include "code\modules\religion\dreams\banish_nightmare.dm"
+#include "code\modules\religion\dreams\deaconize_dreamer.dm"
+#include "code\modules\religion\dreams\dream_portent.dm"
+#include "code\modules\religion\dreams\dream_projection.dm"
+#include "code\modules\religion\dreams\dream_protection.dm"
+#include "code\modules\religion\dreams\slumber_party.dm"
#include "code\modules\religion\festival\festival_violin.dm"
#include "code\modules\religion\festival\instrument_rites.dm"
#include "code\modules\religion\honorbound\honorbound_rites.dm"
diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss
index ba4f94320eac..6d3af7ce81ba 100644
--- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss
+++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss
@@ -958,6 +958,11 @@ em {
display: inline-block;
}
+.cyan {
+ color: #bde0dc;
+ text-shadow: 0 0 6px #bde0dc;
+}
+
.connectionClosed,
.fatalError {
background: red;
diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss
index 3b65f451e25f..b55b847ad7ba 100644
--- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss
+++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss
@@ -965,6 +965,11 @@ h2.alert {
display: inline-block;
}
+.cyan {
+ color: #95afac;
+ text-shadow: 0 0 6px #95afac;
+}
+
.connectionClosed,
.fatalError {
background: red;
diff --git a/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx b/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
index 749c9c87e957..14794037c6cf 100644
--- a/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
+++ b/tgui/packages/tgui/interfaces/AtmosControlConsole.tsx
@@ -25,154 +25,177 @@ type Chamber = {
output_info?: { active: boolean; amount: number };
};
+type Data = {
+ chambers: Chamber[];
+ maxInput: number;
+ maxOutput: number;
+ reconnecting: boolean;
+ control: boolean;
+ defaultGas: string | null;
+};
+
export const AtmosControlConsole = (props) => {
- const { act, data } = useBackend<{
- chambers: Chamber[];
- maxInput: number;
- maxOutput: number;
- reconnecting: boolean;
- control: boolean;
- }>();
- const chambers = data.chambers || [];
+ const { act, data } = useBackend();
+ const {
+ chambers = [],
+ maxInput,
+ maxOutput,
+ reconnecting,
+ control,
+ defaultGas,
+ } = data;
const [chamberId, setChamberId] = useState(chambers[0]?.id);
const selectedChamber =
chambers.length === 1
? chambers[0]
: chambers.find((chamber) => chamber.id === chamberId);
const [setActiveGasId, setActiveReactionId] = atmosHandbookHooks();
+
+ if (defaultGas) {
+ setActiveGasId(defaultGas);
+ }
+
return (
-
+
- {chambers.length > 1 && (
-
- chamber.name)}
- selected={selectedChamber?.name}
- onSelected={(value) =>
- setChamberId(
- chambers.find((chamber) => chamber.name === value)?.id ||
- chambers[0].id,
+
+
+ {chambers.length > 1 && (
+
+ chamber.name)}
+ selected={selectedChamber?.name}
+ onSelected={(value) =>
+ setChamberId(
+ chambers.find((chamber) => chamber.name === value)?.id ||
+ chambers[0].id,
+ )
+ }
+ />
+
+ )}
+ act('reconnect')}
+ />
)
}
- />
-
- )}
- act('reconnect')}
- />
- )
- }
- >
- {!!selectedChamber && !!selectedChamber.gasmix ? (
-
- ) : (
- {'No Sensors Detected!'}
- )}
-
- {!!selectedChamber && !!data.control && (
-
-
-
- {selectedChamber.input_info ? (
-
-
-
-
-
- act('adjust_input', {
- chamber: selectedChamber.id,
- rate: value,
- })
- }
- />
-
-
- ) : (
- {'No Input Device Detected!'}
- )}
-
-
- {selectedChamber.output_info ? (
-
-
-
-
-
- act('adjust_output', {
- chamber: selectedChamber.id,
- rate: value,
- })
- }
- />
-
-
- ) : (
- {'No Output Device Detected !'}
- )}
-
-
-
- )}
-
+ >
+ {selectedChamber?.gasmix ? (
+
+ ) : (
+ {'No Sensors Detected!'}
+ )}
+
+ {!!selectedChamber && !!control && (
+
+
+
+ {selectedChamber.input_info ? (
+
+
+
+
+
+ act('adjust_input', {
+ chamber: selectedChamber.id,
+ rate: value,
+ })
+ }
+ />
+
+
+ ) : (
+ {'No Input Device Detected!'}
+ )}
+
+
+ {selectedChamber.output_info ? (
+
+
+
+
+
+ act('adjust_output', {
+ chamber: selectedChamber.id,
+ rate: value,
+ })
+ }
+ />
+
+
+ ) : (
+ {'No Output Device Detected !'}
+ )}
+
+
+
+ )}
+
+
+
+
+
);
diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.tsx b/tgui/packages/tgui/interfaces/ChemDispenser.tsx
index 0bbeb2ecca0c..5c7d4c31c20e 100644
--- a/tgui/packages/tgui/interfaces/ChemDispenser.tsx
+++ b/tgui/packages/tgui/interfaces/ChemDispenser.tsx
@@ -1,8 +1,10 @@
+import { useState } from 'react';
import {
BlockQuote,
Box,
Button,
Collapsible,
+ Floating,
Icon,
Input,
LabeledList,
@@ -14,12 +16,17 @@ import {
} from 'tgui-core/components';
import type { BooleanLike } from 'tgui-core/react';
import { createSearch, toTitleCase } from 'tgui-core/string';
-
import { useBackend, useSharedState } from '../backend';
import { Window } from '../layouts';
import { type Beaker, BeakerDisplay } from './common/BeakerDisplay';
import { bitflagInfo } from './Reagents/types';
+enum DropdownState {
+ NO_DROPDOWN = 0,
+ DROPDOWN_CLOSED = 1,
+ DROPDOWN_OPEN = 2,
+}
+
type DispensableReagent = {
title: string;
id: string;
@@ -427,10 +434,14 @@ export const ChemDispenser = (props) => {
filteredReactions.map((reaction) => (
))
@@ -498,14 +509,85 @@ const ReagentDispenseButton = (props: ReagentDispenseButtonProps) => {
};
type ReactionDisplayProps = {
+ /// Determines how the collapsible/dropdown is displayed.
+ /// NO_DROPDOWN: no dropdown is displayed, just the recipe list is shown
+ /// DROPDOWN_CLOSED: dropdown is displayed, but closed by default
+ /// DROPDOWN_OPEN: dropdown is displayed and open by default
+ /// If undefined, the default behavior is DROPDOWN_CLOSED.
+ dropdownState?: DropdownState;
+ /// The reaction to display.
reaction: ReagentReaction;
+ /// List of reactions that are pinned to the top of the list.
pinnedReactions: ReactionTypepath[];
+ /// Callback to update the list of pinned reactions.
setPinnedReactions: (reactions: ReactionTypepath[]) => void;
- setSearchTerm: (term: string) => void;
+ /// Callback to force the parent component to keep their floating window open.
+ setParentForceFloating?: (force: boolean) => void;
};
const ReactionDisplay = (props: ReactionDisplayProps) => {
- const { reaction, pinnedReactions, setPinnedReactions } = props;
+ const {
+ dropdownState,
+ reaction,
+ pinnedReactions,
+ setPinnedReactions,
+ setParentForceFloating,
+ } = props;
+
+ const recipeList = (
+
+
+
+
+
+ {reaction.reaction.required_reagents.map((reagent) => (
+
+
+
+ ))}
+ {reaction.reaction.required_catalysts.length > 0 && (
+ <>
+
+
+
+ {reaction.reaction.required_catalysts.map((catalyst) => (
+
+
+
+ ))}
+ >
+ )}
+
+
+
+
+ {getTemperatureMessage(
+ reaction.reaction.lower_temperature,
+ reaction.reaction.upper_temperature,
+ )}
+
+
+
+
+
+ {getPHMessage(reaction.reaction.lower_ph, reaction.reaction.upper_ph)}
+
+
+
+ );
+
return (
{
-
-
-
-
-
-
- {reaction.reaction.required_reagents.map((reagent) => (
-
-
-
- ))}
- {reaction.reaction.required_catalysts.length > 0 && (
- <>
-
-
-
- {reaction.reaction.required_catalysts.map((catalyst) => (
-
-
-
- ))}
- >
- )}
-
-
-
-
- {getTemperatureMessage(
- reaction.reaction.lower_temperature,
- reaction.reaction.upper_temperature,
- )}
-
-
-
-
-
- {getPHMessage(
- reaction.reaction.lower_ph,
- reaction.reaction.upper_ph,
- )}
-
-
-
-
+ {dropdownState === DropdownState.NO_DROPDOWN ? (
+ recipeList
+ ) : (
+
+ {recipeList}
+
+ )}
);
@@ -660,10 +693,14 @@ function getPHMessage(lower: number, upper: number): string {
}
type ReactionComponentDisplayProps = {
+ /// What component of the reaction is being displayed.
reagentComponent: ReactionComponent;
- setSearchTerm: (term: string) => void;
+ /// List of reactions that are pinned to the top of the list.
pinnedReactions: ReactionTypepath[];
+ /// Callback to update the list of pinned reactions.
setPinnedReactions: (reactions: ReactionTypepath[]) => void;
+ /// Callback to force the parent component to keep their floating window open.
+ setParentForceFloating?: (force: boolean) => void;
};
// linkifies a reagent name in the reaction display
@@ -673,9 +710,9 @@ type ReactionComponentDisplayProps = {
const ReactionComponentDisplay = (props: ReactionComponentDisplayProps) => {
const {
reagentComponent,
- setSearchTerm,
pinnedReactions,
setPinnedReactions,
+ setParentForceFloating,
} = props;
const { data } = useBackend();
const { chemicals, reaction_list } = data;
@@ -697,49 +734,82 @@ const ReactionComponentDisplay = (props: ReactionComponentDisplayProps) => {
const reactionReagentList = reagentListToArray(reaction_list);
// check if it's a recipe
- const isRecipe = reactionReagentList
+ const foundRecipe = reactionReagentList
.filter((reaction) => {
return reaction.name === reagentComponent.name;
})
.find((reaction) => reaction.name === reagentComponent.name);
- if (isRecipe) {
+ if (!foundRecipe)
return (
-
+ ) : (
+ gas[1]
+ )
+ }
+ key={gas[1]}
+ >
+ {gas[2].toFixed(2) +
+ ' mol (' +
+ ((gas[2] / total_moles) * 100).toFixed(2) +
+ ' %)'}
+
+ ))}
+
+
+
+ reactionOnClick(reaction[0])}
+ content={'Temperature'}
+ onClick={() => temperatureOnClick()}
/>
-
- ) : (
- {reaction[1]}
- ),
- )
- : 'No reactions detected'}
-
+ ) : (
+ 'Temperature'
+ )
+ }
+ >
+ {`${total_moles ? temperature.toFixed(2) : '-'} K`}
+
+ volumeOnClick()}
+ />
+ ) : (
+ 'Volume'
+ )
+ }
+ >
+ {`${total_moles ? volume.toFixed(2) : '-'} L`}
+
+ pressureOnClick()}
+ />
+ ) : (
+ 'Pressure'
+ )
+ }
+ >
+ {`${total_moles ? pressure.toFixed(2) : '-'} kPa`}
+
+
+
+
+
+ {!!reactions.length && (
+
+
+
+ Active Reactions
+
+ {detailedReactions ? (
+
+ {reactions.map((reaction) => (
+
+ {reactionOnClick ? (
+ reactionOnClick(reaction[0])}
+ mr={1}
+ >
+ {reaction[1]}
+
+ ) : (
+ reaction[1]
+ )}
+ - {reaction[2]}
+
+ ))}
+
+ ) : (
+
+
+ {reactions.map((reaction) => (
+
+ {reactionOnClick ? (
+ reactionOnClick(reaction[0])}
+ mr={1}
+ >
+ {reaction[1]}
+
+ ) : (
+ reaction[1]
+ )}
+
+ ))}
+
+
+ )}
+
+
)}
-
+
);
};