+* @package SS13
+* @subpackage ZAS
+*/
+/ZAS_Settings
+ // INTERNAL USE ONLY
+ var/list/datum/ZAS_Setting/settings = list()
+
+/ZAS_Settings/New()
+ .=..()
+ for(var/S in typesof(/datum/ZAS_Setting) - /datum/ZAS_Setting)
+ var/id=idfrompath("[S]")
+ //testing("Creating zas_settings\[[id]\] = new [S]")
+ src.settings[id]=new S
+
+
+ if(fexists("config/ZAS.txt") == 0)
+ Save()
+ Load()
+
+/ZAS_Settings/proc/Save()
+ var/F = file("config/ZAS.txt")
+ fdel(F)
+ for(var/id in src.settings)
+ var/datum/ZAS_Setting/setting = src.settings[id]
+ F << "# [setting.name]"
+ F << "# [setting.desc]"
+ F << "[id] [setting.value]"
+ F << ""
+
+/ZAS_Settings/proc/Load()
+ for(var/t in file2list("config/ZAS.txt"))
+ if(!t) continue
+
+ t = trim(t)
+ if (length(t) == 0)
+ continue
+ else if (copytext(t, 1, 2) == "#")
+ continue
+
+ var/pos = findtext(t, " ")
+ var/name = null
+ var/value = null
+
+ if (pos)
+ name = copytext(t, 1, pos)
+ value = copytext(t, pos + 1)
+ else
+ name = t
+
+ if (!name)
+ continue
+
+ src.SetFromConfig(name,value)
+
+// INTERNAL USE ONLY
+/ZAS_Settings/proc/idfrompath(var/str)
+ return replacetext(str,"/datum/ZAS_Setting/","")
+
+// INTERNAL USE ONLY
+/ZAS_Settings/proc/ChangeSetting(var/user,var/id)
+ var/datum/ZAS_Setting/setting = src.settings[id]
+ var/displayedValue=""
+ switch(setting.valtype)
+ if(ZAS_TYPE_NUMERIC)
+ setting.value = input(user,"Enter a number:","Settings",setting.value) as num
+ displayedValue="\"[setting.value]\""
+ /*
+ if(ZAS_TYPE_BITFLAG)
+ var/flag = input(user,"Toggle which bit?","Settings") in bitflags
+ flag = text2num(flag)
+ if(newvar & flag)
+ newvar &= ~flag
+ else
+ newvar |= flag
+ */
+ if(ZAS_TYPE_BOOLEAN)
+ setting.value = !setting.value
+ displayedValue = (setting.value) ? "ON" : "OFF"
+ /*
+ if(ZAS_TYPE_STRING)
+ setting.value = input(user,"Enter text:","Settings",newvar) as message
+ */
+ else
+ error("[id] has an invalid typeval.")
+ return
+ world << "\blue [key_name(user)] changed ZAS setting [setting.name] to [displayedValue]."
+
+ ChangeSettingsDialog(user)
+
+/**
+* Set the value of a setting.
+*
+* Recommended to use the actual type of the setting rather than the ID, since
+* this will allow for the compiler to check the validity of id. Kinda.
+*
+* @param id Either the typepath of the desired setting, or the string ID of the setting.
+* @param value The value that the setting should be set to.
+*/
+/ZAS_Settings/proc/Set(var/id, var/value)
+ var/datum/ZAS_Setting/setting = src.settings[idfrompath(id)]
+ setting.value=value
+
+// INTERNAL USE ONLY
+/ZAS_Settings/proc/SetFromConfig(var/id, var/value)
+ var/datum/ZAS_Setting/setting = src.settings[id]
+ switch(setting.valtype)
+ if(ZAS_TYPE_NUMERIC)
+ setting.value = text2num(value)
+ /*
+ if(ZAS_TYPE_BITFLAG)
+ var/flag = input(user,"Toggle which bit?","Settings") in bitflags
+ flag = text2num(flag)
+ if(newvar & flag)
+ newvar &= ~flag
+ else
+ newvar |= flag
+ */
+ if(ZAS_TYPE_BOOLEAN)
+ setting.value = (value == "1")
+ /*
+ if(ZAS_TYPE_STRING)
+ setting.value = input(user,"Enter text:","Settings",newvar) as message
+ */
+
+/**
+* Get a setting.
+*
+* Recommended to use the actual type of the setting rather than the ID, since
+* this will allow for the compiler to check the validity of id. Kinda.
+*
+* @param id Either the typepath of the desired setting, or the string ID of the setting.
+* @returns Value of the desired setting
+*/
+/ZAS_Settings/proc/Get(var/id)
+ if(ispath(id))
+ id="[id]"
+ var/datum/ZAS_Setting/setting = src.settings[idfrompath(id)]
+ if(!setting || !istype(setting))
+ world.log << "ZAS_SETTING DEBUG: [id] | [idfrompath(id)]"
+ return setting.value
+
+/ZAS_Settings/proc/ChangeSettingsDialog(mob/user)
+ var/dat = {"
+
+
+ ZAS Settings 2.0
+
+
+
+ ZAS Configuration
+ Save Settings | Load Settings
+ Please note that changing these settings can and probably will result in death, destruction and mayhem. Change at your own risk.
+ "}
+ for(var/id in src.settings)
+ var/datum/ZAS_Setting/s = src.settings[id]
+ dat += "- [s.name] = [s.value] \[Change\]
"
+ dat += "- [s.desc]
"
+ dat += "
"
+ user << browse(dat,"window=settings")
+
+/ZAS_Settings/Topic(href,href_list)
+ if("changevar" in href_list)
+ ChangeSetting(usr,href_list["changevar"])
+ if("save" in href_list)
+ var/sure = input(usr,"Are you sure? This will overwrite your ZAS configuration!","Overwrite ZAS.txt?", "No") in list("Yes","No")
+ if(sure=="Yes")
+ Save()
+ message_admins("[key_name(usr)] saved ZAS settings to disk.")
+ if("load" in href_list)
+ var/sure = input(usr,"Are you sure?","Reload ZAS.txt?", "No") in list("Yes","No")
+ if(sure=="Yes")
+ Load()
+ message_admins("[key_name(usr)] reloaded ZAS settings from disk.")
+
+/ZAS_Settings/proc/SetDefault(var/mob/user)
+ var/list/setting_choices = list("Plasma - Standard", "Plasma - Low Hazard", "Plasma - High Hazard", "Plasma - Oh Shit!", "ZAS - Normal", "ZAS - Forgiving", "ZAS - Dangerous", "ZAS - Hellish")
+ var/def = input(user, "Which of these presets should be used?") as null|anything in setting_choices
+ if(!def)
+ return
+ switch(def)
+ if("Plasma - Standard")
+ Set("CLOTH_CONTAMINATION", 1) //If this is on, plasma does damage by getting into cloth.
+ Set("PLASMAGUARD_ONLY", 0)
+ Set("GENETIC_CORRUPTION", 0) //Chance of genetic corruption as well as toxic damage, X in 1000.
+ Set("SKIN_BURNS", 0) //Plasma has an effect similar to mustard gas on the un-suited.
+ Set("EYE_BURNS", 1) //Plasma burns the eyes of anyone not wearing eye protection.
+ Set("PLASMA_HALLUCINATION", 0)
+ Set("CONTAMINATION_LOSS", 0.02)
+
+ if("Plasma - Low Hazard")
+ Set("CLOTH_CONTAMINATION", 0) //If this is on, plasma does damage by getting into cloth.
+ Set("PLASMAGUARD_ONLY", 0)
+ Set("GENETIC_CORRUPTION", 0) //Chance of genetic corruption as well as toxic damage, X in 1000
+ Set("SKIN_BURNS", 0) //Plasma has an effect similar to mustard gas on the un-suited.
+ Set("EYE_BURNS", 1) //Plasma burns the eyes of anyone not wearing eye protection.
+ Set("PLASMA_HALLUCINATION", 0)
+ Set("CONTAMINATION_LOSS", 0.01)
+
+ if("Plasma - High Hazard")
+ Set("CLOTH_CONTAMINATION", 1) //If this is on, plasma does damage by getting into cloth.
+ Set("PLASMAGUARD_ONLY", 0)
+ Set("GENETIC_CORRUPTION", 0) //Chance of genetic corruption as well as toxic damage, X in 1000.
+ Set("SKIN_BURNS", 1) //Plasma has an effect similar to mustard gas on the un-suited.
+ Set("EYE_BURNS", 1) //Plasma burns the eyes of anyone not wearing eye protection.
+ Set("PLASMA_HALLUCINATION", 1)
+ Set("CONTAMINATION_LOSS", 0.05)
+
+ if("Plasma - Oh Shit!")
+ Set("CLOTH_CONTAMINATION", 1) //If this is on, plasma does damage by getting into cloth.
+ Set("PLASMAGUARD_ONLY", 1)
+ Set("GENETIC_CORRUPTION", 5) //Chance of genetic corruption as well as toxic damage, X in 1000.
+ Set("SKIN_BURNS", 1) //Plasma has an effect similar to mustard gas on the un-suited.
+ Set("EYE_BURNS", 1) //Plasma burns the eyes of anyone not wearing eye protection.
+ Set("PLASMA_HALLUCINATION", 1)
+ Set("CONTAMINATION_LOSS", 0.075)
+
+ if("ZAS - Normal")
+ Set("airflow_push", 0)
+ Set("airflow_lightest_pressure", 20)
+ Set("airflow_light_pressure", 35)
+ Set("airflow_medium_pressure", 50)
+ Set("airflow_heavy_pressure", 65)
+ Set("airflow_dense_pressure", 85)
+ Set("airflow_stun_pressure", 60)
+ Set("airflow_stun_cooldown", 60)
+ Set("airflow_stun", 1)
+ Set("airflow_damage", 2)
+ Set("airflow_speed_decay", 1.5)
+ Set("airflow_delay", 30)
+ Set("airflow_mob_slowdown", 1)
+
+ if("ZAS - Forgiving")
+ Set("airflow_push", 0)
+ Set("airflow_lightest_pressure", 45)
+ Set("airflow_light_pressure", 60)
+ Set("airflow_medium_pressure", 120)
+ Set("airflow_heavy_pressure", 110)
+ Set("airflow_dense_pressure", 200)
+ Set("airflow_stun_pressure", 150)
+ Set("airflow_stun_cooldown", 90)
+ Set("airflow_stun", 0.15)
+ Set("airflow_damage", 0.15)
+ Set("airflow_speed_decay", 1.5)
+ Set("airflow_delay", 50)
+ Set("airflow_mob_slowdown", 0)
+
+ if("ZAS - Dangerous")
+ Set("airflow_push", 1)
+ Set("airflow_lightest_pressure", 15)
+ Set("airflow_light_pressure", 30)
+ Set("airflow_medium_pressure", 45)
+ Set("airflow_heavy_pressure", 55)
+ Set("airflow_dense_pressure", 70)
+ Set("airflow_stun_pressure", 50)
+ Set("airflow_stun_cooldown", 50)
+ Set("airflow_stun", 2)
+ Set("airflow_damage", 3)
+ Set("airflow_speed_decay", 1.2)
+ Set("airflow_delay", 25)
+ Set("airflow_mob_slowdown", 2)
+
+ if("ZAS - Hellish")
+ Set("airflow_push", 1)
+ Set("airflow_lightest_pressure", 20)
+ Set("airflow_light_pressure", 30)
+ Set("airflow_medium_pressure", 40)
+ Set("airflow_heavy_pressure", 50)
+ Set("airflow_dense_pressure", 60)
+ Set("airflow_stun_pressure", 40)
+ Set("airflow_stun_cooldown", 40)
+ Set("airflow_stun", 3)
+ Set("airflow_damage", 4)
+ Set("airflow_speed_decay", 1)
+ Set("airflow_delay", 20)
+ Set("airflow_mob_slowdown", 3)
+ world << "\blue [key_name(usr)] loaded ZAS preset [def]"
\ No newline at end of file
diff --git a/code/ZAS - vgstation/Plasma.dm b/code/ZAS - vgstation/Plasma.dm
new file mode 100644
index 00000000000..a7bd605c86b
--- /dev/null
+++ b/code/ZAS - vgstation/Plasma.dm
@@ -0,0 +1,125 @@
+var/image/contamination_overlay = image('icons/effects/contamination.dmi')
+
+obj/var/contaminated = 0
+
+obj/item/proc
+ can_contaminate()
+ //Clothing and backpacks can be contaminated.
+ if(flags & PLASMAGUARD) return 0
+ else if(istype(src,/obj/item/weapon/storage/backpack)) return 0 //Cannot be washed :(
+ else if(istype(src,/obj/item/clothing)) return 1
+
+ contaminate()
+ //Do a contamination overlay? Temporary measure to keep contamination less deadly than it was.
+ if(!contaminated)
+ contaminated = 1
+ overlays += contamination_overlay
+
+ decontaminate()
+ contaminated = 0
+ overlays -= contamination_overlay
+
+/mob/proc/contaminate()
+
+/mob/living/carbon/human/contaminate()
+ //See if anything can be contaminated.
+
+ if(!pl_suit_protected())
+ suit_contamination()
+
+ if(!pl_head_protected())
+ if(prob(1)) suit_contamination() //Plasma can sometimes get through such an open suit.
+
+//Cannot wash backpacks currently.
+// if(istype(back,/obj/item/weapon/storage/backpack))
+// back.contaminate()
+
+/mob/proc/pl_effects()
+
+/mob/living/carbon/human/pl_effects()
+ //Handles all the bad things plasma can do.
+
+ //Contamination
+ if(zas_settings.Get(/datum/ZAS_Setting/CLOTH_CONTAMINATION)) contaminate()
+
+ //Anything else requires them to not be dead.
+ if(stat >= 2)
+ return
+
+ //Burn skin if exposed.
+ if(zas_settings.Get(/datum/ZAS_Setting/SKIN_BURNS))
+ if(!pl_head_protected() || !pl_suit_protected())
+ burn_skin(0.75)
+ if(prob(20)) src << "\red Your skin burns!"
+ updatehealth()
+
+ //Burn eyes if exposed.
+ if(zas_settings.Get(/datum/ZAS_Setting/EYE_BURNS))
+ if(!head)
+ if(!wear_mask)
+ burn_eyes()
+ else
+ if(!(wear_mask.flags & MASKCOVERSEYES))
+ burn_eyes()
+ else
+ if(!(head.flags & HEADCOVERSEYES))
+ if(!wear_mask)
+ burn_eyes()
+ else
+ if(!(wear_mask.flags & MASKCOVERSEYES))
+ burn_eyes()
+
+ //Genetic Corruption
+ if(zas_settings.Get(/datum/ZAS_Setting/GENETIC_CORRUPTION))
+ if(rand(1,10000) < zas_settings.Get(/datum/ZAS_Setting/GENETIC_CORRUPTION))
+ randmutb(src)
+ src << "\red High levels of toxins cause you to spontaneously mutate."
+ domutcheck(src,null)
+
+
+/mob/living/carbon/human/proc/burn_eyes()
+ //The proc that handles eye burning.
+ if(prob(20)) src << "\red Your eyes burn!"
+ eye_stat += 2.5
+ eye_blurry = min(eye_blurry+1.5,50)
+ if (prob(max(0,eye_stat - 20) + 1) &&!eye_blind)
+ src << "\red You are blinded!"
+ eye_blind += 20
+ eye_stat = 0
+
+/mob/living/carbon/human/proc/pl_head_protected()
+ //Checks if the head is adequately sealed.
+ if(head)
+ if(zas_settings.Get(/datum/ZAS_Setting/PLASMAGUARD_ONLY))
+ if(head.flags & PLASMAGUARD)
+ return 1
+ else if(head.flags & HEADCOVERSEYES)
+ return 1
+ return 0
+
+/mob/living/carbon/human/proc/pl_suit_protected()
+ //Checks if the suit is adequately sealed.
+ if(wear_suit)
+ if(zas_settings.Get(/datum/ZAS_Setting/PLASMAGUARD_ONLY))
+ if(wear_suit.flags & PLASMAGUARD) return 1
+ else
+ if(wear_suit.flags_inv & HIDEJUMPSUIT) return 1
+ return 0
+
+/mob/living/carbon/human/proc/suit_contamination()
+ //Runs over the things that can be contaminated and does so.
+ if(w_uniform) w_uniform.contaminate()
+ if(shoes) shoes.contaminate()
+ if(gloves) gloves.contaminate()
+
+
+turf/Entered(obj/item/I)
+ . = ..()
+ //Items that are in plasma, but not on a mob, can still be contaminated.
+ if(istype(I) && zas_settings.Get(/datum/ZAS_Setting/CLOTH_CONTAMINATION))
+ var/datum/gas_mixture/env = return_air(1)
+ if(!env)
+ return
+ if(env.toxins > MOLES_PLASMA_VISIBLE + 1)
+ if(I.can_contaminate())
+ I.contaminate()
\ No newline at end of file
diff --git a/code/ZAS - vgstation/Variable Settings.dm b/code/ZAS - vgstation/Variable Settings.dm
new file mode 100644
index 00000000000..09501ffc78a
--- /dev/null
+++ b/code/ZAS - vgstation/Variable Settings.dm
@@ -0,0 +1,340 @@
+var/global/vs_control/vsc = new
+
+// Whoever made this fucking thing: I hate you so much.
+vs_control/var
+ // N3X15 - Added back in so we can tweak performance.
+ airflow_push = 0
+ airflow_push_NAME="Airflow - Push shit around"
+ airflow_push_DESC="1=yes please rape my server, 0=no"
+ airflow_push_METHOD="Toggle" // See ChangeSettings(). I'd rather not let people break this.
+
+ fire_consuption_rate = 0.75
+ fire_consuption_rate_NAME = "Fire - Air Consumption Ratio"
+ fire_consuption_rate_DESC = "Ratio of air removed and combusted per tick."
+
+ fire_firelevel_multiplier = 25
+ fire_firelevel_multiplier_NAME = "Fire - Firelevel Constant"
+ fire_firelevel_multiplier_DESC = "Multiplied by the equation for firelevel, affects mainly the extingiushing of fires."
+
+ fire_fuel_energy_release = 550000
+ fire_fuel_energy_release_NAME = "Fire - Fuel energy release"
+ fire_fuel_energy_release_DESC = "The energy in joule released when burning one mol of a burnable substance"
+
+
+ airflow_lightest_pressure = 20
+ airflow_lightest_pressure_NAME = "Airflow - Small Movement Threshold %"
+ airflow_lightest_pressure_DESC = "Percent of 1 Atm. at which items with the small weight classes will move."
+
+ airflow_light_pressure = 35
+ airflow_light_pressure_NAME = "Airflow - Medium Movement Threshold %"
+ airflow_light_pressure_DESC = "Percent of 1 Atm. at which items with the medium weight classes will move."
+
+ airflow_medium_pressure = 50
+ airflow_medium_pressure_NAME = "Airflow - Heavy Movement Threshold %"
+ airflow_medium_pressure_DESC = "Percent of 1 Atm. at which items with the largest weight classes will move."
+
+ airflow_heavy_pressure = 65
+ airflow_heavy_pressure_NAME = "Airflow - Mob Movement Threshold %"
+ airflow_heavy_pressure_DESC = "Percent of 1 Atm. at which mobs will move."
+
+ airflow_dense_pressure = 85
+ airflow_dense_pressure_NAME = "Airflow - Dense Movement Threshold %"
+ airflow_dense_pressure_DESC = "Percent of 1 Atm. at which items with canisters and closets will move."
+
+ airflow_stun_pressure = 60
+ airflow_stun_pressure_NAME = "Airflow - Mob Stunning Threshold %"
+ airflow_stun_pressure_DESC = "Percent of 1 Atm. at which mobs will be stunned by airflow."
+
+ airflow_stun_cooldown = 60
+ airflow_stun_cooldown_NAME = "Aiflow Stunning - Cooldown"
+ airflow_stun_cooldown_DESC = "How long, in tenths of a second, to wait before stunning them again."
+
+ airflow_stun = 1
+ airflow_stun_NAME = "Airflow Impact - Stunning"
+ airflow_stun_DESC = "How much a mob is stunned when hit by an object."
+
+ airflow_damage = 2
+ airflow_damage_NAME = "Airflow Impact - Damage"
+ airflow_damage_DESC = "Damage from airflow impacts."
+
+ airflow_speed_decay = 1.5
+ airflow_speed_decay_NAME = "Airflow Speed Decay"
+ airflow_speed_decay_DESC = "How rapidly the speed gained from airflow decays."
+
+ airflow_delay = 30
+ airflow_delay_NAME = "Airflow Retrigger Delay"
+ airflow_delay_DESC = "Time in deciseconds before things can be moved by airflow again."
+
+ airflow_mob_slowdown = 1
+ airflow_mob_slowdown_NAME = "Airflow Slowdown"
+ airflow_mob_slowdown_DESC = "Time in tenths of a second to add as a delay to each movement by a mob if they are fighting the pull of the airflow."
+
+ var/connection_insulation = 0.4
+ var/connection_insulation_NAME = "Connections - Insulation"
+ var/connection_insulation_DESC = "How insulative a connection is, in terms of heat transfer. 1 is perfectly insulative, and 0 is perfectly conductive."
+
+ var/connection_temperature_delta = 10
+ var/connection_temperature_delta_NAME = "Connections - Temperature Difference"
+ var/connection_temperature_delta_DESC = "The smallest temperature difference which will cause heat to travel through doors."
+
+vs_control
+ var
+ list/settings = list()
+ list/bitflags = list("1","2","4","8","16","32","64","128","256","512","1024") // Oh jesus why. Learn to shift bits, you idiots.
+ pl_control/plc = new()
+
+ New()
+ . = ..()
+ settings = vars.Copy()
+
+ var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars.
+ for(var/V in D.vars)
+ settings -= V
+
+ for(var/V in settings)
+ if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC") || findtextEx(V,"_METHOD"))
+ settings -= V
+
+ settings -= "settings"
+ settings -= "bitflags"
+ settings -= "plc"
+
+ proc/ChangeSettingsDialog(mob/user,list/L)
+ //var/which = input(user,"Choose a setting:") in L
+ var/dat = ""
+ for(var/ch in L)
+ if(findtextEx(ch,"_RANDOM") || findtextEx(ch,"_DESC") || findtextEx(ch,"_METHOD") || findtextEx(ch,"_NAME")) continue
+ var/vw
+ var/vw_desc = "No Description."
+ var/vw_name = ch
+ if(ch in plc.settings)
+ vw = plc.vars[ch]
+ if("[ch]_DESC" in plc.vars) vw_desc = plc.vars["[ch]_DESC"]
+ if("[ch]_NAME" in plc.vars) vw_name = plc.vars["[ch]_NAME"]
+ else
+ vw = vars[ch]
+ if("[ch]_DESC" in vars) vw_desc = vars["[ch]_DESC"]
+ if("[ch]_NAME" in vars) vw_name = vars["[ch]_NAME"]
+ dat += "[vw_name] = [vw] \[Change\]
"
+ dat += "[vw_desc]
"
+ user << browse(dat,"window=settings")
+ Topic(href,href_list)
+ if("changevar" in href_list)
+ ChangeSetting(usr,href_list["changevar"])
+ proc/ChangeSetting(mob/user,ch)
+ var/vw
+ var/how = "Text"
+ var/display_description = ch
+ if(ch in plc.settings)
+ vw = plc.vars[ch]
+ if("[ch]_NAME" in plc.vars)
+ display_description = plc.vars["[ch]_NAME"]
+ if("[ch]_METHOD" in plc.vars)
+ how = plc.vars["[ch]_METHOD"]
+ else
+ if(isnum(vw))
+ how = "Numeric"
+ else
+ how = "Text"
+ else
+ vw = vars[ch]
+ if("[ch]_NAME" in vars)
+ display_description = vars["[ch]_NAME"]
+ if("[ch]_METHOD" in vars)
+ how = vars["[ch]_METHOD"]
+ else
+ if(isnum(vw))
+ how = "Numeric"
+ else
+ how = "Text"
+ var/newvar = vw
+ switch(how)
+ if("Numeric")
+ newvar = input(user,"Enter a number:","Settings",newvar) as num
+ if("Bit Flag")
+ var/flag = input(user,"Toggle which bit?","Settings") in bitflags
+ flag = text2num(flag)
+ if(newvar & flag)
+ newvar &= ~flag
+ else
+ newvar |= flag
+ if("Toggle")
+ newvar = !newvar
+ if("Text")
+ newvar = input(user,"Enter a string:","Settings",newvar) as text
+ if("Long Text")
+ newvar = input(user,"Enter text:","Settings",newvar) as message
+ vw = newvar
+ if(ch in plc.settings)
+ plc.vars[ch] = vw
+ else
+ vars[ch] = vw
+ if(how == "Toggle")
+ newvar = (newvar?"ON":"OFF")
+ world << "\blue [key_name(user)] changed the setting [display_description] to [newvar]."
+ if(ch in plc.settings)
+ ChangeSettingsDialog(user,plc.settings)
+ else
+ ChangeSettingsDialog(user,settings)
+ proc/RandomizeWithProbability()
+ for(var/V in settings)
+ var/newvalue
+ if("[V]_RANDOM" in vars)
+ if(isnum(vars["[V]_RANDOM"]))
+ newvalue = prob(vars["[V]_RANDOM"])
+ else if(istext(vars["[V]_RANDOM"]))
+ newvalue = roll(vars["[V]_RANDOM"])
+ else
+ newvalue = vars[V]
+ V = newvalue
+
+ proc/ChangePlasma()
+ for(var/V in plc.settings)
+ plc.Randomize(V)
+
+ proc/SetDefault(var/mob/user)
+ var/list/setting_choices = list("Plasma - Standard", "Plasma - Low Hazard", "Plasma - High Hazard", "Plasma - Oh Shit!",\
+ "ZAS - Normal", "ZAS - Forgiving", "ZAS - Dangerous", "ZAS - Hellish")
+ var/def = input(user, "Which of these presets should be used?") as null|anything in setting_choices
+ if(!def)
+ return
+ switch(def)
+ if("Plasma - Standard")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 0
+ plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000.
+ plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 0
+ plc.CONTAMINATION_LOSS = 0.02
+
+ if("Plasma - Low Hazard")
+ plc.CLOTH_CONTAMINATION = 0 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 0
+ plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000
+ plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 0
+ plc.CONTAMINATION_LOSS = 0.01
+
+ if("Plasma - High Hazard")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 0
+ plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000.
+ plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 1
+ plc.CONTAMINATION_LOSS = 0.05
+
+ if("Plasma - Oh Shit!")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ plc.PLASMAGUARD_ONLY = 1
+ plc.GENETIC_CORRUPTION = 5 //Chance of genetic corruption as well as toxic damage, X in 1000.
+ plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited.
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+ plc.PLASMA_HALLUCINATION = 1
+ plc.CONTAMINATION_LOSS = 0.075
+
+ if("ZAS - Normal")
+ airflow_push=0
+ airflow_lightest_pressure = 20
+ airflow_light_pressure = 35
+ airflow_medium_pressure = 50
+ airflow_heavy_pressure = 65
+ airflow_dense_pressure = 85
+ airflow_stun_pressure = 60
+ airflow_stun_cooldown = 60
+ airflow_stun = 1
+ airflow_damage = 2
+ airflow_speed_decay = 1.5
+ airflow_delay = 30
+ airflow_mob_slowdown = 1
+
+ if("ZAS - Forgiving")
+ airflow_push=0
+ airflow_lightest_pressure = 45
+ airflow_light_pressure = 60
+ airflow_medium_pressure = 120
+ airflow_heavy_pressure = 110
+ airflow_dense_pressure = 200
+ airflow_stun_pressure = 150
+ airflow_stun_cooldown = 90
+ airflow_stun = 0.15
+ airflow_damage = 0.15
+ airflow_speed_decay = 1.5
+ airflow_delay = 50
+ airflow_mob_slowdown = 0
+
+ if("ZAS - Dangerous")
+ airflow_push=1
+ airflow_lightest_pressure = 15
+ airflow_light_pressure = 30
+ airflow_medium_pressure = 45
+ airflow_heavy_pressure = 55
+ airflow_dense_pressure = 70
+ airflow_stun_pressure = 50
+ airflow_stun_cooldown = 50
+ airflow_stun = 2
+ airflow_damage = 3
+ airflow_speed_decay = 1.2
+ airflow_delay = 25
+ airflow_mob_slowdown = 2
+
+ if("ZAS - Hellish")
+ airflow_push=1
+ airflow_lightest_pressure = 20
+ airflow_light_pressure = 30
+ airflow_medium_pressure = 40
+ airflow_heavy_pressure = 50
+ airflow_dense_pressure = 60
+ airflow_stun_pressure = 40
+ airflow_stun_cooldown = 40
+ airflow_stun = 3
+ airflow_damage = 4
+ airflow_speed_decay = 1
+ airflow_delay = 20
+ airflow_mob_slowdown = 3
+
+
+ world << "\blue [key_name(user)] changed the global plasma/ZAS settings to \"[def]\""
+
+pl_control
+ var/list/settings = list()
+ New()
+ . = ..()
+ settings = vars.Copy()
+
+ var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars.
+ for(var/V in D.vars)
+ settings -= V
+
+ for(var/V in settings)
+ if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC"))
+ settings -= V
+
+ settings -= "settings"
+ proc/Randomize(V)
+ var/newvalue
+ if("[V]_RANDOM" in vars)
+ if(isnum(vars["[V]_RANDOM"]))
+ newvalue = prob(vars["[V]_RANDOM"])
+ else if(istext(vars["[V]_RANDOM"]))
+ var/txt = vars["[V]_RANDOM"]
+ if(findtextEx(txt,"PROB"))
+ txt = text2list(txt,"/")
+ txt[1] = replacetext(txt[1],"PROB","")
+ var/p = text2num(txt[1])
+ var/r = txt[2]
+ if(prob(p))
+ newvalue = roll(r)
+ else
+ newvalue = vars[V]
+ else if(findtextEx(txt,"PICK"))
+ txt = replacetext(txt,"PICK","")
+ txt = text2list(txt,",")
+ newvalue = pick(txt)
+ else
+ newvalue = roll(txt)
+ else
+ newvalue = vars[V]
+ vars[V] = newvalue
diff --git a/code/ZAS - vgstation/ZAS_Turfs.dm b/code/ZAS - vgstation/ZAS_Turfs.dm
new file mode 100644
index 00000000000..148a5703a2f
--- /dev/null
+++ b/code/ZAS - vgstation/ZAS_Turfs.dm
@@ -0,0 +1,356 @@
+atom/var/pressure_resistance = ONE_ATMOSPHERE
+turf
+
+ var/zone/zone
+
+ assume_air(datum/gas_mixture/giver) //use this for machines to adjust air
+ del(giver)
+ return 0
+
+ return_air()
+ //Create gas mixture to hold data for passing
+ var/datum/gas_mixture/GM = new
+
+ GM.oxygen = oxygen
+ GM.carbon_dioxide = carbon_dioxide
+ GM.nitrogen = nitrogen
+ GM.toxins = toxins
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+ remove_air(amount as num)
+ var/datum/gas_mixture/GM = new
+
+ var/sum = oxygen + carbon_dioxide + nitrogen + toxins
+ if(sum>0)
+ GM.oxygen = (oxygen/sum)*amount
+ GM.carbon_dioxide = (carbon_dioxide/sum)*amount
+ GM.nitrogen = (nitrogen/sum)*amount
+ GM.toxins = (toxins/sum)*amount
+
+ GM.temperature = temperature
+ GM.update_values()
+
+ return GM
+
+turf
+ simulated
+
+ var/current_graphic = null
+
+ var/tmp
+ datum/gas_mixture/air
+
+ processing = 1
+
+ air_check_directions = 0 //Do not modify this, just add turf to air_master.tiles_to_update
+
+ obj/fire/active_hotspot
+
+
+ proc/update_visuals()
+ overlays = null
+
+ var/siding_icon_state = return_siding_icon_state()
+ if(siding_icon_state)
+ overlays += image('floors.dmi',siding_icon_state)
+ var/datum/gas_mixture/model = return_air()
+ switch(model.graphic)
+ if(1)
+ overlays.Add(plmaster) //TODO: Make invisible plasma an option
+ if(2)
+ overlays.Add(slmaster)
+
+
+
+ New()
+ ..()
+
+ if(!blocks_air)
+ air = new
+
+ air.oxygen = oxygen
+ air.carbon_dioxide = carbon_dioxide
+ air.nitrogen = nitrogen
+ air.toxins = toxins
+
+ air.temperature = temperature
+ air.update_values()
+
+ if(air_master)
+ air_master.tiles_to_update.Add(src)
+
+ else
+ if(air_master)
+ for(var/direction in cardinal)
+ var/turf/simulated/floor/target = get_step(src,direction)
+ if(istype(target))
+ air_master.tiles_to_update |= target
+
+ Del()
+ if(active_hotspot)
+ del(active_hotspot)
+ if(blocks_air)
+ for(var/direction in list(NORTH, SOUTH, EAST, WEST))
+ var/turf/simulated/tile = get_step(src,direction)
+ if(istype(tile) && !tile.blocks_air)
+ air_master.tiles_to_update.Add(tile)
+ ..()
+
+ assume_air(datum/gas_mixture/giver)
+ if(!giver) return 0
+ if(zone)
+ zone.air.merge(giver)
+ return 1
+ else
+ return ..()
+
+ return_air()
+ if(zone)
+ return zone.air
+ else if(air)
+ return air
+
+ else
+ return ..()
+
+ remove_air(amount as num)
+ if(zone)
+ var/datum/gas_mixture/removed = null
+ removed = zone.air.remove(amount)
+ return removed
+ else if(air)
+ var/datum/gas_mixture/removed = null
+ removed = air.remove(amount)
+
+ if(air.check_tile_graphic())
+ update_visuals(air)
+ return removed
+
+ else
+ return ..()
+
+ proc/update_air_properties()
+ . = 1
+ var/air_directions_archived = air_check_directions
+ air_check_directions = 0
+
+ for(var/direction in cardinal)
+ if(ZAirPass(get_step(src,direction)))
+ air_check_directions |= direction
+
+ if(!zone && !blocks_air) //No zone, but not a wall.
+ for(var/direction in DoorDirections) //Check door directions first.
+ if(air_check_directions&direction)
+ var/turf/simulated/T = get_step(src,direction)
+ if(!istype(T))
+ continue
+ if(T.zone)
+ T.zone.AddTurf(src)
+ break
+ if(!zone) //Still no zone
+ for(var/direction in CounterDoorDirections) //Check the others second.
+ if(air_check_directions&direction)
+ var/turf/simulated/T = get_step(src,direction)
+ if(!istype(T))
+ continue
+ if(T.zone)
+ T.zone.AddTurf(src)
+ break
+ if(!zone) //No zone found, new zone!
+ new/zone(src)
+ if(!zone) //Still no zone, the floodfill determined it is not part of a larger zone. Force a zone on it.
+ new/zone(list(src))
+
+ //Check pass sanity of the connections.
+ if("\ref[src]" in air_master.turfs_with_connections)
+ for(var/connection/C in air_master.turfs_with_connections["\ref[src]"])
+ air_master.connections_to_check |= C
+
+ if(zone && !zone.rebuild)
+ for(var/direction in cardinal)
+ var/turf/T = get_step(src,direction)
+ if(!istype(T))
+ continue
+
+ //I can connect to air in this direction
+ if(air_check_directions&direction)
+
+ //If either block air, we must look to see if the adjacent turfs need rebuilt.
+ if(!CanPass(null, T, 0, 0))
+
+ //Target blocks air
+ if(!T.CanPass(null, T, 0, 0))
+ var/turf/NT = get_step(T, direction)
+
+ //If that turf is in my zone still, rebuild.
+ if(istype(NT,/turf/simulated) && NT in zone.contents)
+ zone.rebuild = 1
+
+ //If that is an unsimulated tile in my zone, see if we need to rebuild or just remove.
+ else if(istype(NT) && NT in zone.unsimulated_tiles)
+ var/consider_rebuild = 0
+ for(var/d in cardinal)
+ var/turf/UT = get_step(NT,d)
+ if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild
+ consider_rebuild = 1
+ break
+ if(consider_rebuild)
+ zone.rebuild = 1 //Gotta check if we need to rebuild, dammit
+ else
+ zone.RemoveTurf(NT) //Not adjacent to anything, and unsimulated. Goodbye~
+
+ //To make a closed connection through closed door.
+ ZConnect(T, src)
+
+ //If I block air.
+ else if(T.zone && !T.zone.rebuild)
+ var/turf/NT = get_step(src, reverse_direction(direction))
+
+ //If I am splitting a zone, rebuild.
+ if(istype(NT,/turf/simulated) && (NT in T.zone.contents || (NT.zone && T in NT.zone.contents)))
+ T.zone.rebuild = 1
+
+ //If NT is unsimulated, parse if I should remove it or rebuild.
+ else if(istype(NT) && NT in T.zone.unsimulated_tiles)
+ var/consider_rebuild = 0
+ for(var/d in cardinal)
+ var/turf/UT = get_step(NT,d)
+ if(istype(UT, /turf/simulated) && UT.zone == T.zone && UT.CanPass(null, NT, 0, 0)) //If we find a neighboring tile that is in the same zone, check if we need to rebuild
+ consider_rebuild = 1
+ break
+
+ //Needs rebuilt.
+ if(consider_rebuild)
+ T.zone.rebuild = 1
+
+ //Not adjacent to anything, and unsimulated. Goodbye~
+ else
+ T.zone.RemoveTurf(NT)
+
+ else
+ //Produce connection through open door.
+ ZConnect(src,T)
+
+ //Something like a wall was built, changing the geometry.
+ else if(air_directions_archived&direction)
+ var/turf/NT = get_step(T, direction)
+
+ //If the tile is in our own zone, and we cannot connect to it, better rebuild.
+ if(istype(NT,/turf/simulated) && NT in zone.contents)
+ zone.rebuild = 1
+
+ //Parse if we need to remove the tile, or rebuild the zone.
+ else if(istype(NT) && NT in zone.unsimulated_tiles)
+ var/consider_rebuild = 0
+
+ //Loop through all neighboring turfs to see if we should remove the turf or just rebuild.
+ for(var/d in cardinal)
+ var/turf/UT = get_step(NT,d)
+
+ //If we find a neighboring tile that is in the same zone, rebuild
+ if(istype(UT, /turf/simulated) && UT.zone == zone && UT.CanPass(null, NT, 0, 0))
+ consider_rebuild = 1
+ break
+
+ //The unsimulated turf is adjacent to another one of our zone's turfs,
+ // better rebuild to be sure we didn't get cut in twain
+ if(consider_rebuild && NT.zone)
+ NT.zone.rebuild = 1
+
+ //Not adjacent to anything, and unsimulated. Goodbye~
+ else
+ zone.RemoveTurf(NT)
+
+ if(air_check_directions)
+ processing = 1
+ else
+ processing = 0
+
+
+
+
+
+/turf/proc/HasDoor(turf/O)
+ //Checks for the presence of doors, used for zone spreading and connection.
+ //A positive numerical argument checks only for closed doors.
+ //Another turf as an argument checks for windoors between here and there.
+ for(var/obj/machinery/door/D in src)
+ if(isnum(O) && O)
+ if(!D.density) continue
+ if(istype(D,/obj/machinery/door/window))
+ if(!istype(O))
+ continue
+ if(D.dir == get_dir(D,O))
+ return 1
+ else
+ return 1
+
+turf/proc/ZCanPass(turf/simulated/T, var/include_space = 0)
+ //Fairly standard pass checks for turfs, objects and directional windows. Also stops at the edge of space.
+ if(!istype(T))
+ return 0
+
+ if(!istype(T) && !include_space)
+ return 0
+ else
+ if(T.blocks_air||blocks_air)
+ return 0
+
+ for(var/obj/obstacle in src)
+ if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction)
+ continue
+ if(!obstacle.CanPass(null, T, 1.5, 1))
+ return 0
+
+ for(var/obj/obstacle in T)
+ if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction)
+ continue
+ if(!obstacle.CanPass(null, src, 1.5, 1))
+ return 0
+
+ return 1
+
+turf/proc/ZAirPass(turf/T)
+ //Fairly standard pass checks for turfs, objects and directional windows.
+ if(!istype(T))
+ return 0
+
+ if(T.blocks_air||blocks_air)
+ return 0
+
+ for(var/obj/obstacle in src)
+ if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction)
+ continue
+ if(!obstacle.CanPass(null, T, 0, 0))
+ return 0
+
+ for(var/obj/obstacle in T)
+ if(istype(obstacle, /obj/machinery/door) && !obstacle:air_properties_vary_with_direction)
+ continue
+ if(!obstacle.CanPass(null, src, 0, 0))
+ return 0
+
+ return 1
+
+
+/*UNUSED
+/turf/proc/check_connections()
+ //Checks for new connections that can be made.
+ for(var/d in cardinal)
+ var/turf/simulated/T = get_step(src,d)
+ if(istype(T) && ( !T.zone || !T.CanPass(0,src,0,0) ) )
+ continue
+ if(T.zone != zone)
+ ZConnect(src,T)
+
+/turf/proc/check_for_space()
+ //Checks for space around the turf.
+ for(var/d in cardinal)
+ var/turf/T = get_step(src,d)
+ if(istype(T,/turf/space) && T.CanPass(0,src,0,0))
+ zone.AddSpace(T)
+ */
\ No newline at end of file
diff --git a/code/ZAS - vgstation/ZAS_Zones.dm b/code/ZAS - vgstation/ZAS_Zones.dm
new file mode 100644
index 00000000000..a90a699afca
--- /dev/null
+++ b/code/ZAS - vgstation/ZAS_Zones.dm
@@ -0,0 +1,547 @@
+var/list/zones = list()
+var/list/DoorDirections = list(NORTH,WEST) //Which directions doors turfs can connect to zones
+var/list/CounterDoorDirections = list(SOUTH,EAST) //Which directions doors turfs can connect to zones
+
+/zone
+ var/dbg_output = 0 //Enables debug output.
+ var/rebuild = 0 //If 1, zone will be rebuilt on next process. Not sure if used.
+ var/datum/gas_mixture/air //The air contents of the zone.
+ var/list/contents //All the tiles that are contained in this zone.
+ var/list/connections // /connection objects which refer to connections with other zones, e.g. through a door.
+ var/list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number
+ //of points they're connected at.
+ var/list/closed_connection_zones //Same as connected_zones, but for zones where the door or whatever is closed.
+ var/list/unsimulated_tiles // Any space tiles in this list will cause air to flow out.
+ var/last_update = 0
+ var/progress = "nothing"
+
+
+//CREATION AND DELETION
+/zone/New(turf/start)
+ . = ..()
+ //Get the turfs that are part of the zone using a floodfill method
+ if(istype(start,/list))
+ contents = start
+ else
+ contents = FloodFill(start)
+
+ //Change all the zone vars of the turfs, check for space to be added to unsimulated_tiles.
+ for(var/turf/T in contents)
+ if(T.zone && T.zone != src)
+ T.zone.RemoveTurf(T)
+ T.zone = src
+ if(!istype(T,/turf/simulated))
+ AddTurf(T)
+
+ //Generate the gas_mixture for use in txhis zone by using the average of the gases
+ //defined at startup.
+ air = new
+ air.group_multiplier = contents.len
+ for(var/turf/simulated/T in contents)
+ air.oxygen += T.oxygen / air.group_multiplier
+ air.nitrogen += T.nitrogen / air.group_multiplier
+ air.carbon_dioxide += T.carbon_dioxide / air.group_multiplier
+ air.toxins += T.toxins / air.group_multiplier
+ air.temperature += T.temperature / air.group_multiplier
+ air.update_values()
+
+ //Add this zone to the global list.
+ zones.Add(src)
+
+
+ //LEGACY, DO NOT USE. Use the SoftDelete proc.
+/zone/Del()
+ //Ensuring the zone list doesn't get clogged with null values.
+ for(var/turf/simulated/T in contents)
+ RemoveTurf(T)
+ air_master.tiles_to_reconsider_zones += T
+ for(var/zone/Z in connected_zones)
+ if(src in Z.connected_zones)
+ Z.connected_zones.Remove(src)
+ for(var/connection/C in connections)
+ air_master.connections_to_check += C
+ zones.Remove(src)
+ air = null
+ . = ..()
+
+
+ //Handles deletion via garbage collection.
+/zone/proc/SoftDelete()
+ zones.Remove(src)
+ air = null
+
+ //Ensuring the zone list doesn't get clogged with null values.
+ for(var/turf/simulated/T in contents)
+ RemoveTurf(T)
+ air_master.tiles_to_reconsider_zones += T
+
+ //Removing zone connections and scheduling connection cleanup
+ for(var/zone/Z in connected_zones)
+ if(src in Z.connected_zones)
+ Z.connected_zones.Remove(src)
+ connected_zones = null
+
+ for(var/connection/C in connections)
+ air_master.connections_to_check += C
+ connections = null
+
+ return 1
+
+
+//ZONE MANAGEMENT FUNCTIONS
+/zone/proc/AddTurf(turf/T)
+ //Adds the turf to contents, increases the size of the zone, and sets the zone var.
+ if(istype(T, /turf/simulated))
+ if(T in contents)
+ return
+ if(T.zone)
+ T.zone.RemoveTurf(T)
+ contents += T
+ if(air)
+ air.group_multiplier++
+ T.zone = src
+ else
+ if(!unsimulated_tiles)
+ unsimulated_tiles = list()
+ else if(T in unsimulated_tiles)
+ return
+ unsimulated_tiles += T
+ contents -= T
+
+/zone/proc/RemoveTurf(turf/T)
+ //Same, but in reverse.
+ if(istype(T, /turf/simulated))
+ if(!(T in contents))
+ return
+ contents -= T
+ if(air)
+ air.group_multiplier--
+ if(T.zone == src)
+ T.zone = null
+ else if(unsimulated_tiles)
+ unsimulated_tiles -= T
+ if(!unsimulated_tiles.len)
+ unsimulated_tiles = null
+
+ //////////////
+ //PROCESSING//
+//////////////
+
+#define QUANTIZE(variable) (round(variable,0.0001))
+
+/zone/proc/process()
+ . = 1
+
+ progress = "problem with: SoftDelete()"
+
+ //Deletes zone if empty.
+ if(!contents.len)
+ return SoftDelete()
+
+ progress = "problem with: Rebuild()"
+
+ //Does rebuilding stuff.
+ if(rebuild)
+ rebuild = 0
+ Rebuild() //Shoving this into a proc.
+
+ if(!contents.len) //If we got soft deleted.
+ return
+
+ progress = "problem with: air regeneration"
+
+ //Sometimes explosions will cause the air to be deleted for some reason.
+ if(!air)
+ air = new()
+ air.oxygen = MOLES_O2STANDARD
+ air.nitrogen = MOLES_N2STANDARD
+ air.temperature = T0C
+ air.total_moles()
+ world.log << "Air object lost in zone. Regenerating."
+
+
+ progress = "problem with: ShareSpace()"
+
+ if(unsimulated_tiles)
+ if(locate(/turf/simulated) in unsimulated_tiles)
+ for(var/turf/simulated/T in unsimulated_tiles)
+ unsimulated_tiles -= T
+
+ if(unsimulated_tiles.len)
+ var/moved_air = ShareSpace(air,unsimulated_tiles)
+
+ if(moved_air > zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure))
+ AirflowSpace(src)
+ else
+ unsimulated_tiles = null
+
+ //Check the graphic.
+ progress = "problem with: modifying turf graphics"
+
+ air.graphic = 0
+ if(air.toxins > MOLES_PLASMA_VISIBLE)
+ air.graphic = 1
+ else if(air.trace_gases.len)
+ var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air.trace_gases
+ if(sleeping_agent && (sleeping_agent.moles > 1))
+ air.graphic = 2
+
+ progress = "problem with an inbuilt byond function: some conditional checks"
+
+ //Only run through the individual turfs if there's reason to.
+ if(air.graphic != air.graphic_archived || air.temperature > PLASMA_FLASHPOINT)
+
+ progress = "problem with: turf/simulated/update_visuals()"
+
+ for(var/turf/simulated/S in contents)
+ //Update overlays.
+ if(air.graphic != air.graphic_archived)
+ if(S.HasDoor(1))
+ S.update_visuals()
+ else
+ S.update_visuals(air)
+
+ progress = "problem with: item or turf temperature_expose()"
+
+ //Expose stuff to extreme heat.
+ if(air.temperature > PLASMA_FLASHPOINT)
+ for(var/atom/movable/item in S)
+ item.temperature_expose(air, air.temperature, CELL_VOLUME)
+ S.hotspot_expose(air.temperature, CELL_VOLUME)
+
+ progress = "problem with: calculating air graphic"
+
+ //Archive graphic so we can know if it's different.
+ air.graphic_archived = air.graphic
+
+ progress = "problem with: calculating air temp"
+
+ //Ensure temperature does not reach absolute zero.
+ air.temperature = max(TCMB,air.temperature)
+
+ progress = "problem with an inbuilt byond function: length(connections)"
+
+ //Handle connections to other zones.
+ if(length(connections))
+
+ progress = "problem with: ZMerge(), a couple of misc procs"
+
+ for(var/connection/C in connections)
+ //Check if the connection is valid first.
+ if(!C.Cleanup())
+ continue
+
+ //Do merging if conditions are met. Specifically, if there's a non-door connection
+ //to somewhere with space, the zones are merged regardless of equilibrium, to speed
+ //up spacing in areas with double-plated windows.
+ if(C && C.A.zone && C.B.zone)
+ //indirect = 2 is a direct connection.
+ if( C.indirect == 2 )
+ if(C.A.zone.air.compare(C.B.zone.air) || unsimulated_tiles)
+ ZMerge(C.A.zone,C.B.zone)
+
+ progress = "problem with: ShareRatio(), Airflow(), a couple of misc procs"
+
+ //Share some
+ for(var/zone/Z in connected_zones)
+ //If that zone has already processed, skip it.
+ if(Z.last_update > last_update)
+ continue
+
+ if(air && Z.air)
+ //Ensure we're not doing pointless calculations on equilibrium zones.
+ var/moles_delta = abs(air.total_moles() - Z.air.total_moles())
+ if(moles_delta > 0.1 || abs(air.temperature - Z.air.temperature) > 0.1)
+ if(abs(Z.air.return_pressure() - air.return_pressure()) > zas_settings.Get(/datum/ZAS_Setting/airflow_lightest_pressure))
+ Airflow(src,Z)
+ var/unsimulated_boost = 0
+ if(unsimulated_tiles)
+ unsimulated_boost += unsimulated_tiles.len
+ if(Z.unsimulated_tiles)
+ unsimulated_boost += Z.unsimulated_tiles.len
+ unsimulated_boost = max(0, min(3, unsimulated_boost))
+ ShareRatio( air , Z.air , connected_zones[Z] + unsimulated_boost)
+
+ for(var/zone/Z in closed_connection_zones)
+ //If that zone has already processed, skip it.
+ if(Z.last_update > last_update)
+ continue
+ if(air && Z.air)
+ if( abs(air.temperature - Z.air.temperature) > zas_settings.Get(/datum/ZAS_Setting/connection_temperature_delta) )
+ ShareHeat(air, Z.air, closed_connection_zones[Z])
+
+ progress = "all components completed successfully, the problem is not here"
+
+ ////////////////
+ //Air Movement//
+////////////////
+
+var/list/sharing_lookup_table = list(0.03, 0.07, 0.11, 0.15, 0.18, 0.20)
+
+proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
+ //Shares a specific ratio of gas between mixtures using simple weighted averages.
+ var
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ ratio = sharing_lookup_table[6]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ size = max(1,A.group_multiplier)
+ share_size = max(1,B.group_multiplier)
+
+ full_oxy = A.oxygen * size
+ full_nitro = A.nitrogen * size
+ full_co2 = A.carbon_dioxide * size
+ full_plasma = A.toxins * size
+
+ full_heat_capacity = A.heat_capacity() * size
+
+ s_full_oxy = B.oxygen * share_size
+ s_full_nitro = B.nitrogen * share_size
+ s_full_co2 = B.carbon_dioxide * share_size
+ s_full_plasma = B.toxins * share_size
+
+ s_full_heat_capacity = B.heat_capacity() * share_size
+
+ oxy_avg = (full_oxy + s_full_oxy) / (size + share_size)
+ nit_avg = (full_nitro + s_full_nitro) / (size + share_size)
+ co2_avg = (full_co2 + s_full_co2) / (size + share_size)
+ plasma_avg = (full_plasma + s_full_plasma) / (size + share_size)
+
+ temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
+
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[connecting_tiles]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ A.oxygen = max(0, (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
+ A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1-ratio) + nit_avg )
+ A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
+ A.toxins = max(0, (A.toxins - plasma_avg) * (1-ratio) + plasma_avg )
+
+ A.temperature = max(0, (A.temperature - temp_avg) * (1-ratio) + temp_avg )
+
+ B.oxygen = max(0, (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg )
+ B.nitrogen = max(0, (B.nitrogen - nit_avg) * (1-ratio) + nit_avg )
+ B.carbon_dioxide = max(0, (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg )
+ B.toxins = max(0, (B.toxins - plasma_avg) * (1-ratio) + plasma_avg )
+
+ B.temperature = max(0, (B.temperature - temp_avg) * (1-ratio) + temp_avg )
+
+ for(var/datum/gas/G in A.trace_gases)
+ var/datum/gas/H = locate(G.type) in B.trace_gases
+ if(H)
+ var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+ else
+ H = new G.type
+ B.trace_gases += H
+ var/G_avg = (G.moles*size) / (size+share_size)
+ G.moles = (G.moles - G_avg) * (1-ratio) + G_avg
+ H.moles = (H.moles - G_avg) * (1-ratio) + G_avg
+
+ A.update_values()
+ B.update_values()
+
+ if(A.compare(B)) return 1
+ else return 0
+
+proc/ShareSpace(datum/gas_mixture/A, list/unsimulated_tiles, dbg_output)
+ //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a large airless room.
+ if(!unsimulated_tiles || !unsimulated_tiles.len)
+ return 0
+
+ var
+ unsim_oxygen = 0
+ unsim_nitrogen = 0
+ unsim_co2 = 0
+ unsim_plasma = 0
+ unsim_heat_capacity = 0
+ unsim_temperature = 0
+
+ size = max(1,A.group_multiplier)
+
+ // We use the same size for the potentially single space tile
+ // as we use for the entire room. Why is this?
+ // Short answer: We do not want larger rooms to depressurize more
+ // slowly than small rooms, preserving our good old "hollywood-style"
+ // oh-shit effect when large rooms get breached, but still having small
+ // rooms remain pressurized for long enough to make escape possible.
+ share_size = max(1, max(size + 3, 1) + unsimulated_tiles.len)
+ correction_ratio = share_size / unsimulated_tiles.len
+
+ for(var/turf/T in unsimulated_tiles)
+ unsim_oxygen += T.oxygen
+ unsim_co2 += T.carbon_dioxide
+ unsim_nitrogen += T.nitrogen
+ unsim_plasma += T.toxins
+ unsim_temperature += T.temperature/unsimulated_tiles.len
+
+ //These values require adjustment in order to properly represent a room of the specified size.
+ unsim_oxygen *= correction_ratio
+ unsim_co2 *= correction_ratio
+ unsim_nitrogen *= correction_ratio
+ unsim_plasma *= correction_ratio
+ unsim_heat_capacity = HEAT_CAPACITY_CALCULATION(unsim_oxygen,unsim_co2,unsim_nitrogen,unsim_plasma)
+
+ var
+ ratio = sharing_lookup_table[6]
+
+ old_pressure = A.return_pressure()
+
+ full_oxy = A.oxygen * size
+ full_nitro = A.nitrogen * size
+ full_co2 = A.carbon_dioxide * size
+ full_plasma = A.toxins * size
+
+ full_heat_capacity = A.heat_capacity() * size
+
+ oxy_avg = (full_oxy + unsim_oxygen) / (size + share_size)
+ nit_avg = (full_nitro + unsim_nitrogen) / (size + share_size)
+ co2_avg = (full_co2 + unsim_co2) / (size + share_size)
+ plasma_avg = (full_plasma + unsim_plasma) / (size + share_size)
+
+ temp_avg = (A.temperature * full_heat_capacity + unsim_temperature * unsim_heat_capacity) / (full_heat_capacity + unsim_heat_capacity)
+
+ if(sharing_lookup_table.len >= unsimulated_tiles.len) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[unsimulated_tiles.len]
+
+ //We need to adjust it to account for the insulation settings.
+ ratio *= 1 - zas_settings.Get(/datum/ZAS_Setting/connection_insulation)
+
+ A.oxygen = max(0, (A.oxygen - oxy_avg) * (1 - ratio) + oxy_avg )
+ A.nitrogen = max(0, (A.nitrogen - nit_avg) * (1 - ratio) + nit_avg )
+ A.carbon_dioxide = max(0, (A.carbon_dioxide - co2_avg) * (1 - ratio) + co2_avg )
+ A.toxins = max(0, (A.toxins - plasma_avg) * (1 - ratio) + plasma_avg )
+
+ A.temperature = max(TCMB, (A.temperature - temp_avg) * (1 - ratio) + temp_avg )
+
+ for(var/datum/gas/G in A.trace_gases)
+ var/G_avg = (G.moles * size) / (size + share_size)
+ G.moles = (G.moles - G_avg) * (1 - ratio) + G_avg
+
+ A.update_values()
+
+ return abs(old_pressure - A.return_pressure())
+
+
+proc/ShareHeat(datum/gas_mixture/A, datum/gas_mixture/B, connecting_tiles)
+ //Shares a specific ratio of gas between mixtures using simple weighted averages.
+ var
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ ratio = sharing_lookup_table[6]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ full_heat_capacity = A.heat_capacity()
+
+ s_full_heat_capacity = B.heat_capacity()
+
+ temp_avg = (A.temperature * full_heat_capacity + B.temperature * s_full_heat_capacity) / (full_heat_capacity + s_full_heat_capacity)
+
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+ if(sharing_lookup_table.len >= connecting_tiles) //6 or more interconnecting tiles will max at 42% of air moved per tick.
+ ratio = sharing_lookup_table[connecting_tiles]
+ //WOOT WOOT TOUCH THIS AND YOU ARE A RETARD
+
+ A.temperature = max(0, (A.temperature - temp_avg) * (1- (ratio / max(1,A.group_multiplier)) ) + temp_avg )
+ B.temperature = max(0, (B.temperature - temp_avg) * (1- (ratio / max(1,B.group_multiplier)) ) + temp_avg )
+
+
+ ///////////////////
+ //Zone Rebuilding//
+///////////////////
+
+zone/proc/Rebuild()
+ //Choose a random turf and regenerate the zone from it.
+ var
+ turf/simulated/sample = locate() in contents
+ list/new_contents
+ problem = 0
+
+ //
+ var/list/turfs_to_consider = contents.Copy()
+
+ while(!sample || !sample.CanPass(null, sample, 1.5, 1))
+ if(sample)
+ turfs_to_consider.Remove(sample)
+ sample = locate() in turfs_to_consider
+ if(!sample)
+ break
+
+ if(!istype(sample) || !sample.CanPass(null, sample, 1.5, 1)) //Not a single valid turf.
+ for(var/turf/simulated/T in contents)
+ air_master.tiles_to_update |= T
+ return SoftDelete()
+
+ new_contents = FloodFill(sample)
+
+ var/list/new_unsimulated = ( unsimulated_tiles ? unsimulated_tiles : list() )
+
+ for(var/turf/S in new_contents)
+ if(!istype(S, /turf/simulated))
+ new_unsimulated |= S
+ new_contents.Remove(S)
+
+ if(contents.len != new_contents.len)
+ problem = 1
+
+ //If something isn't carried over, there was a complication.
+ for(var/turf/T in contents)
+ if(!(T in new_contents))
+ T.zone = null
+ problem = 1
+
+ if(problem)
+ //Build some new zones for stuff that wasn't included.
+ var/list/turf/simulated/rebuild_turfs = contents - new_contents
+ var/list/turf/simulated/reconsider_turfs = list()
+ contents = new_contents
+ for(var/turf/simulated/T in rebuild_turfs)
+ if(!T.zone && T.CanPass(null, T, 1.5, 1))
+ var/zone/Z = new /zone(T)
+ Z.air.copy_from(air)
+ else
+ reconsider_turfs |= T
+ for(var/turf/simulated/T in reconsider_turfs)
+ if(!T.zone && T.CanPass(null, T, 1.5, 1))
+ var/zone/Z = new /zone(T)
+ Z.air.copy_from(air)
+ else if(!T in air_master.tiles_to_update)
+ air_master.tiles_to_update.Add(T)
+
+ for(var/turf/simulated/T in contents)
+ if(T.zone && T.zone != src)
+ T.zone.RemoveTurf(T)
+ T.zone = src
+ else if(!T.zone)
+ T.zone = src
+ air.group_multiplier = contents.len
+ unsimulated_tiles = null
+
+ if(new_unsimulated.len)
+ for(var/turf/S in new_unsimulated)
+ if(istype(S, /turf/simulated))
+ continue
+ for(var/direction in cardinal)
+ var/turf/simulated/T = get_step(S,direction)
+ if(istype(T) && T.zone && S.CanPass(null, T, 0, 0))
+ T.zone.AddTurf(S)
+
+//UNUSED
+/*
+zone/proc/connected_zones()
+ //A legacy proc for getting connected zones.
+ . = list()
+ for(var/connection/C in connections)
+ var/zone/Z
+ if(C.A.zone == src)
+ Z = C.B.zone
+ else
+ Z = C.A.zone
+
+ if(Z in .)
+ .[Z]++
+ else
+ . += Z
+ .[Z] = 1*/
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 6aec44f7f17..8948fde47cb 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -93,7 +93,8 @@
/obj/machinery/door/proc/bumpopen(mob/user as mob)
if(operating) return
- if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit
+ //if(user.last_airflow > world.time - vsc.airflow_delay) //Fakkit
+ if(user.last_airflow > world.time - zas_settings.Get("airflow_delay")) //Fakkit
return
src.add_fingerprint(user)
if(!src.requiresID())
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index db71270618f..38660b2d319 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2436,12 +2436,13 @@
else if(href_list["vsc"])
if(check_rights(R_ADMIN|R_SERVER))
if(href_list["vsc"] == "airflow")
- vsc.ChangeSettingsDialog(usr,vsc.settings)
+/* vsc.ChangeSettingsDialog(usr,vsc.settings)
if(href_list["vsc"] == "plasma")
- vsc.ChangeSettingsDialog(usr,vsc.plc.settings)
+ vsc.ChangeSettingsDialog(usr,vsc.plc.settings) */
+ zas_settings.ChangeSettingsDialog(usr,zas_settings.settings)
if(href_list["vsc"] == "default")
- vsc.SetDefault(usr)
-
+ //vsc.SetDefault(usr)
+ zas_settings.SetDefault(usr)
// player info stuff
if(href_list["add_player_info"])
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index f5314954069..a3ca2aade32 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -836,7 +836,8 @@
var/total_plasmaloss = 0
for(var/obj/item/I in src)
if(I.contaminated)
- total_plasmaloss += vsc.plc.CONTAMINATION_LOSS
+ //total_plasmaloss += vsc.plc.CONTAMINATION_LOSS
+ total_plasmaloss += zas_settings.Get("CONTAMINATION_LOSS")
if(status_flags & GODMODE) return 0 //godmode
adjustToxLoss(total_plasmaloss)