diff --git a/baystation12.dme b/baystation12.dme
index d007fb1cced..47ceda83761 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -356,12 +356,16 @@
#include "code\defines\sd_procs\constants.dm"
#include "code\defines\sd_procs\direction.dm"
#include "code\defines\sd_procs\math.dm"
+#include "code\FEA\DEBUG_REMOVE_BEFORE_RELEASE.dm"
#include "code\FEA\FEA_airgroup.dm"
#include "code\FEA\FEA_fire.dm"
#include "code\FEA\FEA_gas_mixture.dm"
#include "code\FEA\FEA_group_helpers.dm"
#include "code\FEA\FEA_system.dm"
#include "code\FEA\FEA_turf_tile.dm"
+#include "code\FEA\FEA_variables.dm"
+#include "code\FEA\FEA_ZAS.dm"
+#include "code\FEA\FEA_ZAS_Airflow.dm"
#include "code\game\algorithm.dm"
#include "code\game\atom_procs.dm"
#include "code\game\cellautomata.dm"
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index 74e76392002..577bf562dce 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -464,22 +464,6 @@ obj/machinery/atmospherics/pipe
..()
- oxygen_agent_b
- icon = 'red_orange_pipe_tank.dmi'
- name = "High Capacity Pressure Tank (Oxygen + Plasma)"
-
- New()
- air_temporary = new
- air_temporary.volume = volume
- air_temporary.temperature = T0C
-
- var/datum/gas/oxygen_agent_b/trace_gas = new
- trace_gas.moles = (160*ONE_ATMOSPHERE)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
-
- air_temporary.trace_gases += trace_gas
-
- ..()
-
oxygen
icon = 'blue_pipe_tank.dmi'
name = "High Capacity Pressure Tank (Oxygen)"
diff --git a/code/FEA/DEBUG_REMOVE_BEFORE_RELEASE.dm b/code/FEA/DEBUG_REMOVE_BEFORE_RELEASE.dm
index c3b549b10c3..8942a16bb28 100644
--- a/code/FEA/DEBUG_REMOVE_BEFORE_RELEASE.dm
+++ b/code/FEA/DEBUG_REMOVE_BEFORE_RELEASE.dm
@@ -116,7 +116,7 @@ obj/machinery/portable_atmospherics/canister
valve_open = 1
release_pressure = 1000
-
+/*
obj/machinery/atmospherics
unary
heat_reservoir
@@ -352,6 +352,7 @@ obj/machinery/atmospherics
usr << "[x],[y] is in a pipeline with [parent.members.len] members ([parent.edges.len] edges)! Volume: [parent.air.volume]"
usr << "Pressure: [parent.air.return_pressure()], Temperature: [parent.air.temperature]"
usr << "[parent.air.oxygen], [parent.air.toxins], [parent.air.nitrogen], [parent.air.carbon_dioxide] .. [parent.alert_pressure]"
+*/
mob
verb
flag_all_pipe_networks()
@@ -519,7 +520,7 @@ mob
for(var/obj/effect/hotspot/flame in world)
usr << "[flame.x],[flame.y]: [flame.temperature]K, [flame.volume] L - [flame.loc:air:temperature]"
- process_cycle()
+/* process_cycle()
set category = "Debug"
if(!master_controller)
usr << "Cannot find master_controller"
@@ -595,7 +596,7 @@ mob
if(S.processing)
S.icon_state = "individual_on"
else
- S.icon_state = "individual_off"
+ S.icon_state = "individual_off"*/
mark_groups()
diff --git a/code/FEA/FEA_ZAS.dm b/code/FEA/FEA_ZAS.dm
new file mode 100644
index 00000000000..766cbe5fe4e
--- /dev/null
+++ b/code/FEA/FEA_ZAS.dm
@@ -0,0 +1,57 @@
+/* AN EXPLANATION:
+This file was added because I felt that the computations for movement
+of air over large scales was not adequately done by FEA, and the attempt
+to solve this problem, in the form of ZAS (Zone Atmos System),
+simplified it too far, in my opinion, creating the effect of a single
+opened air canister filling an entire room with a partial mix of that
+gas with the pre-existing mix. This was slightly silly, due to large
+rooms "flash filling" when you opened a canister, and the fact that the
+procedures to recalculate the boundaries of the zones was far from
+foolproof, and, indeed, often let air flash though solid bulkheads to
+space.
+
+To solve this, I've both commented the hell out of the existing FEA code,
+removed and/or replaced procs with more efficient procs, and added this
+file, which contains some procedures for special cases, such as:
+ 1.) Moving large volumes of air into vacuum/lower pressure environs
+ (triggers when pressure behind a tile moving air to a lower
+ pressure exceeds an amount.)
+ e.g. A room of full air gets opened to a depressurised room
+ 2.) Dealing with pressure equalization.
+ (triggers when a large amount of gas is added to a room, it
+ follows behind the "stormfront" of gas change (Managed by FEA)
+ and triggers a rapid equalization of gas by utilizing a
+ psuedo-zone.)
+ e.g. Opening a tank of oxygen in a low pressure room, and after
+ spreading by FEA, it equalizes the pressure in the room, instead
+ instead of slowly flowing out like molasses.
+
+These procedures trigger when a check is made against the pressure changes
+and situations occuring to the airgroup (if it is venting at high pressure,
+airgroups will rejoin even if broken to ZAS process) will call these
+specialized procs to help properly deal with these situation.
+--SkyMarshal, May 2012*/
+/* WIP
+
+ if(space_tiles)
+ for(var/T in space_tiles)
+ if(!istype(T,/turf/space)) space_tiles -= T
+ total_space += length(space_tiles)
+ if(length(space_tiles))
+ space = space_tiles[1]
+
+ if(total_space && space)
+ var/old_pressure = air.return_pressure()
+ air.temperature_mimic(space,OPEN_HEAT_TRANSFER_COEFFICIENT,total_space)
+ air.remove(MOLES_CELLSTANDARD * (air.group_multiplier/40) * total_space)
+ var/p_diff = old_pressure - air.return_pressure()
+ if(p_diff > vsc.AF_TINY_MOVEMENT_THRESHOLD) AirflowSpace(src,p_diff)
+
+ air.graphic_archived = air.graphic
+
+ air.temperature = max(TCMB,air.temperature)
+
+ var/p_diff = (air.return_pressure()-Z.air.return_pressure())*connected_zones[Z]*(vsc.zone_share_percent/100)
+ if(p_diff > vsc.AF_TINY_MOVEMENT_THRESHOLD) Airflow(src,Z,p_diff)
+ air.share_ratio(Z.air,connected_zones[Z]*(vsc.zone_share_percent/100))
+ */
\ No newline at end of file
diff --git a/code/FEA/FEA_ZAS_Airflow.dm b/code/FEA/FEA_ZAS_Airflow.dm
new file mode 100644
index 00000000000..5fc42651c87
--- /dev/null
+++ b/code/FEA/FEA_ZAS_Airflow.dm
@@ -0,0 +1,451 @@
+/*
+
+CONTAINS:
+All AirflowX() procs, all Variable Setting Controls for airflow, save/load variable tweaks for airflow.
+
+VARIABLES:
+
+atom/movable/airflow_dest
+ The destination turf of a flying object.
+
+atom/movable/airflow_speed
+ The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time.
+
+
+OVERLOADABLE PROCS:
+
+mob/airflow_stun()
+ Contains checks for and results of being stunned by airflow.
+ Called when airflow quantities exceed AF_HUMAN_STUN_THRESHOLD.
+ RETURNS: Null
+
+atom/movable/check_airflow_movable(n)
+ Contains checks for moving any object due to airflow.
+ n is the percent of 1 Atmosphere that is flowing.
+ RETURNS: 1 if the object moves under the air conditions, 0 if it stays put.
+
+atom/movable/airflow_hit(atom/A)
+ Contains results of hitting a solid object (A) due to airflow.
+ A is the dense object hit.
+ Use airflow_speed to determine how fast the projectile was going.
+
+
+AUTOMATIC PROCS:
+
+Airflow(datum/air_group/A, datum/air_group/B, n, vae/list/turf/simulated/target)
+ Causes objects to fly along a pressure gradient.
+ Called by airflow calculations finding a high pressure gradient. A and B are two adjacent airgroups.
+ n is the pressure difference between them.
+ targets is a list of turfs between them.
+
+AirflowSpace(zone/A)
+ Causes objects to fly into space.
+ Called by airflow to space. A is a airgroup connected to space.
+
+atom/movable/GotoAirflowDest(n)
+atom/movable/RepelAirflowDest(n)
+ Called by main airflow procs to cause the object to fly to or away from destination at speed n.
+ Probably shouldn't call this directly unless you know what you're
+ doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle.
+
+*/
+
+mob/proc/airflow_stun()
+ //Purpose: AIRFLOW
+ //Called by: AIRFLOW
+ //Inputs: Hate
+ //Outputs: Sadness
+
+ if(weakened <= 0)
+ src << pickweight("\red The sudden rush of air knocks you over!" = 5, "\red You get dragged off your feet by a rush of air!" = 5, "\red FUCKING AIRFLOW!" = 1)
+ weakened = max(weakened,5)
+
+mob/living/silicon/airflow_stun()
+ return
+
+mob/living/carbon/metroid/airflow_stun()
+ return
+
+mob/living/carbon/human/airflow_stun()
+ if(buckled) return 0
+ if(wear_suit)
+ if(wear_suit.flags & SUITSPACE) return 0
+ if(shoes)
+ if(shoes.flags & NOSLIP) return 0
+ if(weakened <= 0)
+ src << pickweight("\red The sudden rush of air knocks you over!" = 5, "\red You get dragged off your feet by a rush of air!" = 5, "\red FUCKING AIRFLOW!" = 1)
+ weakened = max(weakened,2)
+
+atom/movable/proc/check_airflow_movable(n)
+ //Purpose: Determining if src is movable by airflow.
+ //Called by: AIRFLOW
+ //Inputs: Movement force
+ //Outputs: Boolean of capability to be moved.
+
+ if(anchored && !ismob(src)) return 0
+
+ if(!istype(src,/obj/item) && n < air_master.AF_DENSE_MOVEMENT_THRESHOLD) return 0
+ if(ismob(src) && n < air_master.AF_MOB_MOVEMENT_THRESHOLD) return 0
+
+ return 1
+
+mob/dead/observer/check_airflow_movable()
+ return 0
+
+mob/living/silicon/check_airflow_movable()
+ return 0
+
+
+obj/item/check_airflow_movable(n)
+ . = ..()
+ switch(w_class)
+ if(2)
+ if(n < air_master.AF_SMALL_MOVEMENT_THRESHOLD) return 0
+ if(3)
+ if(n < air_master.AF_NORMAL_MOVEMENT_THRESHOLD) return 0
+ if(4,5)
+ if(n < air_master.AF_LARGE_MOVEMENT_THRESHOLD) return 0
+
+proc/Airflow(datum/air_group/A,datum/air_group/B,n,var/list/turf/simulated/target)
+ //Purpose: AIRFLOW between two airgroups.
+ //Called by: AIRFLOW
+ //Inputs: Air group venting, Air group receiving, force of airflow, midpoint turf(s)
+ //Outputs: None.
+
+ //Now n is a percent of one atm.
+ n = round((n/air_master.AF_PERCENT_OF)*100,0.1)
+
+ //Don't go any further if n is lower than the lowest value needed for airflow.
+ if(abs(n) < air_master.AF_TINY_MOVEMENT_THRESHOLD)
+ return
+
+ if(!target || !target.len)
+ return
+
+ //Get lists of things that can be thrown across the room for each zone.
+ var/list/pplz = A.movables()
+ var/list/otherpplz = B.movables()
+
+ for(var/atom/movable/M in pplz)
+
+ //Check for knocking people over
+ if(ismob(M) && n > air_master.AF_HUMAN_STUN_THRESHOLD)
+ if(M:nodamage) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(n))
+
+ //Check for things that are in range of the midpoint turfs.
+ var/fail = 1
+ for(var/turf/simulated/T in target)
+ if(M in range(T))
+ fail = 0
+ break
+ if(fail)
+ continue
+
+ //If they're already being tossed, don't do it again.
+ if(!M.airflow_speed)
+
+ M.airflow_dest = pick(target) //Pick a random midpoint to fly towards.
+
+ spawn M.GotoAirflowDest(abs(n) * (air_master.AF_SPEED_MULTIPLIER/10))
+ //Send the object flying at a speed determined by n and AF_SPEED_MULTIPLIER.
+
+ //Do it again for the stuff in the other zone, making it fly away.
+ for(var/atom/movable/M in otherpplz)
+
+ if(ismob(M) && abs(n) > air_master.AF_HUMAN_STUN_THRESHOLD)
+ if(M:nodamage) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(abs(n)))
+
+ var/fail = 1
+ for(var/turf/simulated/T in target)
+ if(M in range(T))
+ fail = 0
+ break
+ if(fail)
+ continue
+
+ if(M && !M.airflow_speed)
+
+ M.airflow_dest = pick(target)
+
+ spawn M.RepelAirflowDest(abs(n) * (air_master.AF_SPEED_MULTIPLIER/10))
+
+proc/AirflowSpace(datum/air_group/A)
+ //Purpose: AIRFLOW between an airgroup and spess.
+ //Called by: AIRFLOW
+ //Inputs: Air group venting into space.
+ //Outputs: None.
+ // The space version of the Airflow(A,B,n) proc.
+
+ var/n = A.air.total_moles*air_master.AF_SPACE_MULTIPLIER
+ //Here, n is determined by the space multiplier constant and the zone's air.
+
+ n = round((n/air_master.AF_PERCENT_OF)*100,0.1)
+
+ if(n < air_master.AF_TINY_MOVEMENT_THRESHOLD) return
+
+ var/list/connected_turfs = A.space_borders //The midpoints are now all the space connections.
+ var/list/pplz = A.movables() //We only need to worry about things in the zone, not things in space.
+
+ for(var/atom/movable/M in pplz)
+
+ if(ismob(M) && n > air_master.AF_HUMAN_STUN_THRESHOLD)
+ if(M:nodamage) continue
+ M:airflow_stun()
+
+ if(M.check_airflow_movable(n))
+
+ var/fail = 1
+ for(var/turf/U in connected_turfs)
+ if(M in range(U)) fail = 0
+ if(fail) continue
+
+ if(!M.airflow_speed)
+ M.airflow_dest = pick(connected_turfs)
+ spawn
+ if(M) M.GotoAirflowDest(n * (air_master.AF_SPEED_MULTIPLIER/10))
+ //Sometimes shit breaks, and M isn't there after the spawn.
+
+/atom/movable
+ var/turf/airflow_dest
+ var/airflow_speed = 0
+ var/airflow_time = 0
+
+ proc/GotoAirflowDest(n)
+ //Purpose: Moving src between it's current position and where air is dragging them.
+ //Called by: AIRFLOW
+ //Inputs: Force.
+ //Outputs: None.
+
+ if(!airflow_dest) return
+ if(airflow_speed < 0) return
+ if(airflow_speed)
+ airflow_speed = n
+ return
+ if(airflow_dest == loc)
+ step_away(src,loc)
+ if(ismob(src))
+ if(src:nodamage) return
+ if(istype(src, /mob/living/carbon/human))
+ if(istype(src, /mob/living/carbon/human))
+ if(src:buckled) return
+ if(src:wear_suit)
+ if(src:wear_suit.flags & SUITSPACE) return
+ if(src:shoes)
+ if(src:shoes.type == /obj/item/clothing/shoes/magboots && src:shoes.flags & NOSLIP) return
+ src << "\red You are sucked away by airflow!"
+
+ airflow_speed = min(round(n),9)
+ airflow_speed /= get_dist(src,airflow_dest) //The further away you are, the less you get dragged.
+ var
+ xo = airflow_dest.x - src.x
+ yo = airflow_dest.y - src.y
+ od = 0
+ airflow_dest = null
+ if(!density)
+ density = 1
+ od = 1 //It wasn't dense, but it is considered it for when being moved by airflow.
+ //Main loop to AIRFLOW
+ while(airflow_speed > 0)
+ if(airflow_speed <= 0) return
+ airflow_speed = min(airflow_speed,15)
+ airflow_speed -= air_master.AF_SPEED_DECAY
+ if(airflow_speed > 7)
+ if(airflow_time++ >= airflow_speed - 7)
+ sleep(1 * tick_multiplier)
+ else
+ sleep(max(1,10-(airflow_speed+3)) * tick_multiplier)
+ if ((!( src.airflow_dest ) || src.loc == src.airflow_dest))
+ src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z)
+ if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy))
+ return
+ step_towards(src, src.airflow_dest)
+ if(ismob(src) && src:client) src:client:move_delay = world.time + 10
+ airflow_dest = null
+ //You cannot be tossed around by airflow more than once ever 1.5(times tick_multiplier) seconds)
+ airflow_speed = -1
+ spawn(150 * tick_multiplier) airflow_speed = 0
+ //Reset density.
+ if(od)
+ density = 0
+
+ proc/RepelAirflowDest(n)
+ //Purpose: Moving src between it's current position and where air is pushing them.
+ //Called by: AIRFLOW
+ //Inputs: Force.
+ //Outputs: None.
+
+ if(!airflow_dest) return
+ if(airflow_speed < 0) return
+ if(airflow_speed)
+ airflow_speed = n
+ return
+ if(airflow_dest == loc)
+ step_away(src,loc)
+ if(ismob(src))
+ if(src:nodamage) return
+ if(istype(src, /mob/living/carbon/human))
+ if(istype(src, /mob/living/carbon/human))
+ if(src:buckled) return
+ if(src:wear_suit)
+ if(src:wear_suit.flags & SUITSPACE) return
+ if(src:shoes)
+ if(src:shoes.type == /obj/item/clothing/shoes/magboots) return
+ src << "\red You are pushed away by airflow!"
+ airflow_speed = min(round(n),9)
+ airflow_speed /= get_dist(src,airflow_dest) //The further away you are, the less you get dragged.
+ var
+ xo = -(airflow_dest.x - src.x)
+ yo = -(airflow_dest.y - src.y)
+ od = 0
+ airflow_dest = null
+ if(!density)
+ density = 1
+ od = 1
+ while(airflow_speed > 0)
+ if(airflow_speed <= 0) return
+ airflow_speed = min(airflow_speed,15)
+ airflow_speed -= air_master.AF_SPEED_DECAY
+ if(airflow_speed > 7)
+ if(airflow_time++ >= airflow_speed - 7)
+ sleep(1 * tick_multiplier)
+ else
+ sleep(max(1,10-(airflow_speed+3)) * tick_multiplier)
+ if ((!( src.airflow_dest ) || src.loc == src.airflow_dest))
+ src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z)
+ if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy))
+ return
+ step_towards(src, src.airflow_dest)
+ if(ismob(src) && src:client) src:client:move_delay = world.time + 10
+ airflow_dest = null
+ airflow_speed = -1
+ spawn(150 * tick_multiplier) airflow_speed = 0
+ if(od)
+ density = 0
+
+ Bump(atom/A)
+ if(airflow_speed > 0 && airflow_dest)
+ airflow_hit(A)
+ else
+ airflow_speed = 0
+ . = ..()
+
+atom/movable/proc/airflow_hit(atom/A)
+ //Purpose: Managing impacts between objects whirled around by airflow.
+ //Called by: AIRFLOW
+ //Inputs: Object to impact.
+ //Outputs: None.
+
+ airflow_speed = -1
+ spawn(50 * tick_multiplier) airflow_speed = 0
+ airflow_dest = null
+
+mob/airflow_hit(atom/A)
+ for(var/mob/M in hearers(src))
+ M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2)
+ playsound(src.loc, "smash.ogg", 25, 1, -1)
+ . = ..()
+
+obj/airflow_hit(atom/A)
+ for(var/mob/M in hearers(src))
+ M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2)
+ playsound(src.loc, "smash.ogg", 25, 1, -1)
+ . = ..()
+
+obj/item/airflow_hit(atom/A)
+ airflow_speed = -1
+ spawn(50 * tick_multiplier) airflow_speed = 0
+ airflow_dest = null
+
+mob/living/carbon/human/airflow_hit(atom/A)
+ for(var/mob/M in hearers(src))
+ M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2)
+ playsound(src.loc, "punch", 25, 1, -1)
+ loc:add_blood(src)
+ if (src.wear_suit)
+ src.wear_suit.add_blood(src)
+ if (src.w_uniform)
+ src.w_uniform.add_blood(src)
+ var/b_loss = airflow_speed * air_master.AF_DAMAGE_MULTIPLIER
+ for(var/organ in src:organs)
+ var/datum/organ/external/temp = src:organs["[organ]"]
+ if (istype(temp, /datum/organ/external))
+ switch(temp.name)
+ if("head")
+ temp.take_damage(b_loss * 0.2, 0)
+ if("chest")
+ temp.take_damage(b_loss * 0.4, 0)
+ if("diaper")
+ temp.take_damage(b_loss * 0.1, 0)
+ spawn UpdateDamageIcon()
+ if(airflow_speed > 10)
+ paralysis += round(airflow_speed * air_master.AF_STUN_MULTIPLIER)
+ stunned = max(stunned,paralysis + 3)
+ else
+ stunned += round(airflow_speed * air_master.AF_STUN_MULTIPLIER/2)
+ . = ..()
+
+/datum/air_group/proc/movables()
+ //Purpose: Returns everything movable.
+ //Called by: AIRFLOW
+ //Inputs: Possible objects to move.
+ //Outputs: None.
+
+ . = list()
+ for(var/turf/T in members)
+ for(var/atom/A in T)
+ . += A
+
+proc/Get_Dir(atom/S,atom/T) //Shamelessly stolen from AJX.AdvancedGetDir
+ var/GDist=get_dist(S,T)
+ var/GDir=get_dir(S,T)
+ if(GDist<=3)
+ if(GDist==0) return 0
+ if(GDist==1)
+ return GDir
+
+
+ var/X1=S.x*10
+ var/X2=T.x*10
+ var/Y1=S.y*10
+ var/Y2=T.y*10
+ var/Ref
+ if(GDir==NORTHEAST)
+ Ref=(X2/X1)*Y1
+ if(Ref-1>Y2) .=EAST
+ else if(Ref+1Y2) .=WEST
+ else if(Ref+1Y2) .=SOUTH
+ else if(Ref+1Y2) .=SOUTH
+ else if(Ref+1> air_master
+ del F
+ world.log << "TWEAKS: Airflow, Plasma and Damage settings loaded."
\ No newline at end of file
diff --git a/code/FEA/FEA_airgroup.dm b/code/FEA/FEA_airgroup.dm
index c31ab6f503a..c92c3838d14 100644
--- a/code/FEA/FEA_airgroup.dm
+++ b/code/FEA/FEA_airgroup.dm
@@ -10,29 +10,8 @@ datum
// to be rolled into the updating step
//optimization vars
- var/tmp/next_check = 0 //number of ticks before this group updates
- var/tmp/check_delay = 10 //number of ticks between updates, starts fairly high to get boring groups out of the way
-
- proc
- archive()
-
- members()
- //Returns the members of the group
-
- check_regroup()
- //If individually processing tiles, checks all member tiles to see if they are close enough
- // that the group may resume group processing
- //Warning: Do not call, called by air_master.process()
-
- process_group()
- suspend_group_processing()
- update_group_from_tiles()
- //Copy group air information to individual tile air
- //Used right before turning on group processing
-
- update_tiles_from_group()
- //Copy group air information to individual tile air
- //Used right before turning off group processing
+// var/tmp/next_check = 0 //number of ticks before this group updates
+// var/tmp/check_delay = 10 //number of ticks between updates, starts fairly high to get boring groups out of the way
var/list/borders //Tiles that connect this group to other groups/individual tiles
var/list/members //All tiles in this group
@@ -40,13 +19,21 @@ datum
var/list/space_borders
var/length_space_border = 0
- suspend_group_processing()
+ proc/suspend_group_processing()
+ //Purpose: Suspends processing of the group, breaks it into individual tiles.
+ //Called by: Any check where the airgroup is determined to break.
+ //Inputs: None.
+ //Outputs: None.
+
group_processing = 0
update_tiles_from_group()
- check_delay=0
- next_check=0
- update_group_from_tiles()
+ proc/update_group_from_tiles()
+ //Purpose: Regathers the tiles of the group into the airgroup, reverses update_tiles_from_group()
+ //Called by: Any check where the airgroup is determined to reconnect.
+ //Inputs: None.
+ //Outputs: None.
+
var/sample_member = pick(members)
var/datum/gas_mixture/sample_air = sample_member:air
@@ -55,20 +42,30 @@ datum
return 1
- update_tiles_from_group()
+ proc/update_tiles_from_group()
+ //Purpose: Breaks the group into individual tiles.
+ //Called by: Any check where the airgroup is determined to break.
+ //Inputs: None.
+ //Outputs: None.
+
for(var/member in members)
member:air.copy_from(air)
- if (istype(member,/turf/simulated))
- var/turf/simulated/turfmem=member
- turfmem.reset_delay()
- archive()
+ proc/archive()
+ //Purpose: Archives the air for optimization.
+ //Called by: ?
+ //Inputs: None.
+ //Outputs: None.
+
air.archive()
archived_cycle = air_master.current_cycle
- check_regroup()
+ proc/check_regroup()
//Purpose: Checks to see if group processing should be turned back on
- //Returns: group_processing
+ //Called by: Any time an airgroup is checked to reform
+ //Inputs: None.
+ //Outputs: if the group is processing or not, boolean
+
if(group_processing) return 1
@@ -85,15 +82,14 @@ datum
return 1
- turf/process_group()
+ proc/process_group()
+ //Purpose: Processes group atmos
+ //Called by: Atmos Ticker.
+ //Inputs: None.
+ //Outputs: None.
+
current_cycle = air_master.current_cycle
if(group_processing) //See if processing this group as a group
- //check if we're skipping this tick
- if (next_check > 0)
- next_check--
- return 1
- next_check += check_delay + rand(0,check_delay/2)
- check_delay++
var/turf/simulated/list/border_individual = list()
var/datum/air_group/list/border_group = list()
@@ -108,22 +104,9 @@ datum
//But only if another group didn't store it for us
for(var/turf/simulated/border_tile in src.borders)
- //var/obj/movable/floor/movable_on_me = locate(/obj/movable/floor) in border_tile
for(var/direction in cardinal) //Go through all border tiles and get bordering groups and individuals
if(border_tile.group_border&direction)
- var/turf/simulated/enemy_tile = get_step(border_tile, direction) //Add found tile to appropriate category
- //var/obj/movable/floor/movable_on_enemy
- //if(!movable_on_me)
- // movable_on_enemy = locate(/obj/movable/floor) in enemy_tile
- /*if(movable_on_enemy) //guaranteed !movable_on_me if this is set
- if(movable_on_enemy.parent && movable_on_enemy.parent.group_processing)
- border_group += movable_on_enemy.parent
- enemies += movable_on_enemy
- self_group_borders += border_tile
- else
- border_individual += movable_on_enemy
- self_tile_borders += border_tile
- else*/
+ var/turf/simulated/enemy_tile = get_step(border_tile, direction)
if(istype(enemy_tile) && enemy_tile.parent && enemy_tile.parent.group_processing)
border_group += enemy_tile.parent
enemies += enemy_tile
@@ -233,83 +216,4 @@ datum
member.hotspot_expose(air.temperature, CELL_VOLUME)
member.consider_superconductivity(starting=1)
- air.react()
-
- object/process_group()
- current_cycle = air_master.current_cycle
-
- if(group_processing) //See if processing this group as a group
-
- var/turf/simulated/list/border_individual = list()
- var/datum/air_group/list/border_group = list()
-
- var/turf/simulated/list/enemies = list() //used to send the appropriate border tile of a group to the group proc
- var/enemy_index = 1
-
- if(archived_cycle < air_master.current_cycle)
- archive()
- //Archive air data for use in calculations
- //But only if another group didn't store it for us
-/*
- for(var/obj/movable/floor/border_tile in src.borders)
- for(var/direction in list(NORTH,SOUTH,EAST,WEST)) //Go through all border tiles and get bordering groups and individuals
- if(border_tile.group_border&direction)
- var/turf/simulated/enemy_tile = get_step(border_tile, direction) //Add found tile to appropriate category
- var/obj/movable/floor/movable_on_enemy = locate(/obj/movable/floor) in enemy_tile
- if(movable_on_enemy)
- if(movable_on_enemy.parent && movable_on_enemy.parent.group_processing)
- border_group += movable_on_enemy.parent
- enemies += movable_on_enemy
- enemy_index++
- else
- border_individual += movable_on_enemy
-
- else
- if(istype(enemy_tile) && enemy_tile.parent && enemy_tile.parent.group_processing)
- border_group += enemy_tile.parent
- enemies += enemy_tile
- enemy_index++
- else
- border_individual += enemy_tile
-*/
- enemy_index = 1
- var/abort_group = 0
- for(var/datum/air_group/AG in border_group)
- if(AG.archived_cycle < archived_cycle) //archive other groups information if it has not been archived yet this cycle
- AG.archive()
- if(AG.current_cycle < current_cycle)
- //This if statement makes sure two groups only process their individual connections once!
- //Without it, each connection would be processed a second time as the second group is evaluated
-
- var/result = air.check_gas_mixture(AG.air)
- if(result == 1)
- air.share(AG.air)
- else if(result == -1)
- AG.suspend_group_processing()
- var/turf/simulated/floor/enemy_border = enemies[enemy_index]
- air.share(enemy_border.air)
- else
- abort_group = 0
- break
- enemy_index++
-
- if(!abort_group)
- for(var/enemy_tile in border_individual)
- if(istype(enemy_tile, /turf/simulated))
- if(enemy_tile:archived_cycle < archived_cycle) //archive tile information if not already done
- enemy_tile:archive()
- if(enemy_tile:current_cycle < current_cycle)
- if(air.check_gas_mixture(enemy_tile:air))
- air.share(enemy_tile:air)
- else
- abort_group = 1
- break
- else
- if(air.check_turf(enemy_tile))
- air.mimic(enemy_tile)
- else
- abort_group = 1
- break
-
- if(abort_group)
- suspend_group_processing()
\ No newline at end of file
+ air.react()
\ No newline at end of file
diff --git a/code/FEA/FEA_fire.dm b/code/FEA/FEA_fire.dm
index 675b70441fb..14fd727dd92 100644
--- a/code/FEA/FEA_fire.dm
+++ b/code/FEA/FEA_fire.dm
@@ -1,10 +1,18 @@
atom
- proc
- temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- return null
+ proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ //Purpose: Managing temperature exposure.
+ //Called by: Atmos
+ //Inputs: Airmix, temperature, volume.
+ //Outputs: None.
+
+ return null
turf
proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
+ //Purpose: Making the equivilent of a spark.
+ //Called by: Atmos. Anything that might light a fire.
+ //Inputs: Temperature, Volume, ?
+ //Outputs: If it is igniting or not.
simulated
hotspot_expose(exposed_temperature, exposed_volume, soh)
@@ -40,7 +48,7 @@ turf
//remove just_spawned protection if no longer processing this cell
//start processing quickly if we aren't already
- reset_delay()
+// reset_delay()
return igniting
diff --git a/code/FEA/FEA_gas_mixture.dm b/code/FEA/FEA_gas_mixture.dm
index 5ceb602436a..dca70cfb578 100644
--- a/code/FEA/FEA_gas_mixture.dm
+++ b/code/FEA/FEA_gas_mixture.dm
@@ -12,6 +12,7 @@ What are the archived variables for?
#define MINIMUM_HEAT_CAPACITY 0.0003
#define QUANTIZE(variable) (round(variable,0.0001))
+#define TRANSFER_FRACTION 5 //What fraction (1/#) of the air difference to try and transfer
datum
gas //These are used for the "Trace Gases" stuff, but is buggy.
@@ -57,6 +58,31 @@ datum
graphic_archived
fuel_burnt = 0
+
+//FOR THE LOVE OF GOD PLEASE USE THIS PROC
+//Call it with negative numbers to remove gases.
+
+ proc/adjustGases(o2 = 0, co2 = 0, n2 = 0, tx = 0, list/datum/gas/traces = list())
+ //Purpose: Adjusting the gases within a airmix
+ //Called by: Nothing, yet!
+ //Inputs: The values of the gases to adjust
+ //Outputs: null
+
+ oxygen = max(0, oxygen + o2)
+ carbon_dioxide = max(0, carbon_dioxide + co2)
+ nitrogen = max(0, nitrogen + n2)
+ toxins = max(0, toxins + tx)
+
+ //handle trace gasses
+ for(var/datum/gas/G in traces)
+ var/datum/gas/T = locate(G.type) in trace_gases
+ if(T)
+ T.moles = max(G.moles + T.moles, 0)
+ else if(G.moles > 0)
+ trace_gases |= G
+ update_values()
+ return
+
///////////////////////////////
//PV=nRT - related procedures//
///////////////////////////////
@@ -441,10 +467,16 @@ datum
// then -1 if sharer-check failed (sharing airgroup breaks?)
// then 1 if both checks pass (share succesful?)
- var/delta_oxygen = (oxygen_archived - sharer.oxygen_archived)/5
- var/delta_carbon_dioxide = (carbon_dioxide_archived - sharer.carbon_dioxide_archived)/5
- var/delta_nitrogen = (nitrogen_archived - sharer.nitrogen_archived)/5
- var/delta_toxins = (toxins_archived - sharer.toxins_archived)/5
+ var/pressure = 1
+ if(!return_pressure() || !sharer.return_pressure())
+ pressure = 3
+ else if(pressure < 0)
+ pressure = (sharer.return_pressure() / return_pressure())
+ pressure = max(min(pressure,3),1) //Lets cap this.
+ var/delta_oxygen = max(QUANTIZE(oxygen_archived - sharer.oxygen_archived) * pressure/TRANSFER_FRACTION,oxygen)
+ var/delta_carbon_dioxide = max(QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived) * pressure/TRANSFER_FRACTION,carbon_dioxide)
+ var/delta_nitrogen = max(QUANTIZE(nitrogen_archived - sharer.nitrogen_archived) * pressure/TRANSFER_FRACTION,nitrogen)
+ var/delta_toxins = max(QUANTIZE(toxins_archived - sharer.toxins_archived) * pressure/TRANSFER_FRACTION,toxins)
var/delta_temperature = (temperature_archived - sharer.temperature_archived)
@@ -497,10 +529,10 @@ datum
//Inputs: Unsimulated turf
//Outputs: 1 if safe to mimic, 0 if needs to break airgroup.
- var/delta_oxygen = (oxygen_archived - model.oxygen)/5
- var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide)/5
- var/delta_nitrogen = (nitrogen_archived - model.nitrogen)/5
- var/delta_toxins = (toxins_archived - model.toxins)/5
+ var/delta_oxygen = (oxygen_archived - model.oxygen)/TRANSFER_FRACTION
+ var/delta_carbon_dioxide = (carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION
+ var/delta_nitrogen = (nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION
+ var/delta_toxins = (toxins_archived - model.toxins)/TRANSFER_FRACTION
var/delta_temperature = (temperature_archived - model.temperature)
@@ -527,10 +559,16 @@ datum
//Outputs: Amount of gas exchanged (Negative if lost air, positive if gained.)
if(!sharer) return 0
- var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/5
- var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/5
- var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/5
- var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/5
+ var/pressure = 1
+ if(!return_pressure() || !sharer.return_pressure())
+ pressure = 3
+ else if(pressure < 0)
+ pressure = (sharer.return_pressure() / return_pressure())
+ pressure = max(min(pressure,3),1) //Lets cap this.
+ var/delta_oxygen = max(QUANTIZE(oxygen_archived - sharer.oxygen_archived) * pressure/TRANSFER_FRACTION,oxygen)
+ var/delta_carbon_dioxide = max(QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived) * pressure/TRANSFER_FRACTION,carbon_dioxide)
+ var/delta_nitrogen = max(QUANTIZE(nitrogen_archived - sharer.nitrogen_archived) * pressure/TRANSFER_FRACTION,nitrogen)
+ var/delta_toxins = max(QUANTIZE(toxins_archived - sharer.toxins_archived) * pressure/TRANSFER_FRACTION,toxins)
var/delta_temperature = (temperature_archived - sharer.temperature_archived)
@@ -598,12 +636,12 @@ datum
var/delta = 0
if(corresponding)
- delta = QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)/5
+ delta = max(QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)*pressure/TRANSFER_FRACTION,trace_gas.moles)
else
corresponding = new trace_gas.type()
sharer.trace_gases += corresponding
- delta = trace_gas.moles_archived/5
+ delta = max(trace_gas.moles_archived*pressure/TRANSFER_FRACTION,trace_gas.moles)
trace_gas.moles -= delta/group_multiplier
corresponding.moles += delta/sharer.group_multiplier
@@ -632,7 +670,7 @@ datum
corresponding = new trace_gas.type()
trace_gases += corresponding
- delta = trace_gas.moles_archived/5
+ delta = max(trace_gas.moles_archived*pressure/TRANSFER_FRACTION,trace_gas.moles)
trace_gas.moles -= delta/sharer.group_multiplier
corresponding.moles += delta/group_multiplier
@@ -673,10 +711,10 @@ datum
//Inputs: Unsimulated turf, Multiplier for gas transfer (optional)
//Outputs: Amount of gas exchanged
- var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen)/5
- var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide)/5
- var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen)/5
- var/delta_toxins = QUANTIZE(toxins_archived - model.toxins)/5
+ var/delta_oxygen = QUANTIZE(oxygen_archived - model.oxygen)/TRANSFER_FRACTION
+ var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - model.carbon_dioxide)/TRANSFER_FRACTION
+ var/delta_nitrogen = QUANTIZE(nitrogen_archived - model.nitrogen)/TRANSFER_FRACTION
+ var/delta_toxins = QUANTIZE(toxins_archived - model.toxins)/TRANSFER_FRACTION
var/delta_temperature = (temperature_archived - model.temperature)
@@ -721,7 +759,7 @@ datum
for(var/datum/gas/trace_gas in trace_gases)
var/delta = 0
- delta = trace_gas.moles_archived/5
+ delta = trace_gas.moles_archived/TRANSFER_FRACTION
if(border_multiplier)
trace_gas.moles -= delta*border_multiplier/group_multiplier
diff --git a/code/FEA/FEA_system.dm b/code/FEA/FEA_system.dm
index 1f63fcdb67b..fb7e87ac99c 100644
--- a/code/FEA/FEA_system.dm
+++ b/code/FEA/FEA_system.dm
@@ -56,6 +56,11 @@ Important Procedures
var/kill_air = 0
atom/proc/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
+ //Purpose: Determines if the object (or airflow) can pass this atom.
+ //Called by: Movement, airflow.
+ //Inputs: The moving atom (optional), target turf, "height" and air group
+ //Outputs: Boolean if can pass.
+
return (!density || !height || air_group)
turf
@@ -97,41 +102,12 @@ datum
var/list/turf/simulated/groups_to_rebuild = list()
var/current_cycle = 0
+ var/update_delay = 5 //How long between check should it try to process atmos again.
- proc
- setup()
- //Call this at the start to setup air groups geometry
- //Warning: Very processor intensive but only must be done once per round
- assemble_group_turf(turf/simulated/base)
- //Call this to try to construct a group starting from base and merging with neighboring unparented tiles
- //Expands the group until all valid borders explored
-
-// assemble_group_object(obj/movable/floor/base)
-
- process()
+/* process()
//Call this to process air movements for a cycle
- process_groups()
- //Used by process()
- //Warning: Do not call this
-
- process_singletons()
- //Used by process()
- //Warning: Do not call this
-
- process_high_pressure_delta()
- //Used by process()
- //Warning: Do not call this
-
- process_super_conductivity()
- //Used by process()
- //Warning: Do not call this
-
- process_update_tiles()
- //Used by process()
- //Warning: Do not call this
-
process_rebuild_select_groups()
//Used by process()
//Warning: Do not call this
@@ -139,12 +115,16 @@ datum
rebuild_group(datum/air_group)
//Used by process_rebuild_select_groups()
//Warning: Do not call this, add the group to air_master.groups_to_rebuild instead
+ */
- add_singleton(turf/simulated/T)
- if(!active_singletons.Find(T))
- active_singletons += T
- setup()
+ proc/setup()
+ //Purpose: Call this at the start to setup air groups geometry
+ // (Warning: Very processor intensive but only must be done once per round)
+ //Called by: Gameticker/Master controller
+ //Inputs: None.
+ //Outputs: None.
+
set background = 1
world << "\red \b Processing Geometry..."
sleep(-1)
@@ -158,16 +138,16 @@ datum
if(S.z > 4) // Skipping asteroids -- TLE
continue
S.update_air_properties()
-/*
- for(var/obj/movable/floor/S in world)
- if(!S.parent)
- assemble_group_object(S)
- for(var/obj/movable/floor/S in world) //Update all pathing and border information as well
- S.update_air_properties()
-*/
- world << "\red \b Geometry processed in [(world.timeofday-start_time)/10] seconds!"
- assemble_group_turf(turf/simulated/base)
+ world << "\red \b Geometry processed in [time2text(world.timeofday-start_time, "mm:ss")] minutes!"
+ spawn start()
+
+ proc/assemble_group_turf(turf/simulated/base)
+ //Purpose: Call this to try to construct a group starting from base and merging with neighboring unparented tiles
+ // (Expands the group until all valid borders explored)
+ //Called by: setup()
+ //Inputs: turf to flood fill from.
+ //Outputs: resulting group, or null if no group is formed.
var/list/turf/simulated/members = list(base) //Confirmed group members
var/list/turf/simulated/possible_members = list(base) //Possible places for group expansion
@@ -196,7 +176,7 @@ datum
possible_members -= test
if(members.len > 1)
- var/datum/air_group/turf/group = new
+ var/datum/air_group/group = new
if(possible_borders.len>0)
group.borders = possible_borders
if(possible_space_borders.len>0)
@@ -221,85 +201,56 @@ datum
base.update_visuals(base.air)
return null
-/*
- assemble_group_object(obj/movable/floor/base)
- var/list/obj/movable/floor/members = list(base) //Confirmed group members
- var/list/obj/movable/floor/possible_members = list(base) //Possible places for group expansion
- var/list/obj/movable/floor/possible_borders = list()
+ proc/start()
+ //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere.
+ //Called by: Master controller
+ //Inputs: None.
+ //Outputs: None.
- while(possible_members.len>0) //Keep expanding, looking for new members
- for(var/obj/movable/floor/test in possible_members)
- for(var/direction in list(NORTH, SOUTH, EAST, WEST))
- var/turf/T = get_step(test.loc,direction)
- if(T && test.loc.CanPass(null, T, null, 1))
- var/obj/movable/floor/O = locate(/obj/movable/floor) in T
- if(istype(O) && !O.parent)
- if(!members.Find(O))
- possible_members += O
- members += O
- else
- possible_borders -= test
- possible_borders += test
- possible_members -= test
+ set background = 1
+ while(1)
+ if(kill_air)
+ return 1
+ current_cycle++
+ if(groups_to_rebuild.len > 0) //If there are groups to rebuild, do so.
+ spawn process_rebuild_select_groups()
- if(members.len > 1)
- var/datum/air_group/object/group = new
- if(possible_borders.len>0)
- group.borders = possible_borders
+ if(tiles_to_update.len > 0) //If there are tiles to update, do so.
+ for(var/turf/simulated/T in tiles_to_update)
+ spawn T.update_air_properties()
+ tiles_to_update = list()
- for(var/obj/movable/floor/test in members)
- test.parent = group
- test.processing = 0
- active_singletons -= test
+ for(var/datum/air_group/AG in air_groups) //Processing groups
+ spawn AG.process_group()
+ for(var/turf/simulated/T in active_singletons) //Processing Singletons
+ spawn T.process_cell()
- group.members = members
- air_groups += group
+ for(var/turf/simulated/hot_potato in active_super_conductivity) //Process superconduction
+ spawn hot_potato.super_conduct()
- group.update_group_from_tiles() //Initialize air group variables
- return group
- else
- base.processing = 0 //singletons at startup are technically unconnected anyway
- base.parent = null
+ if(high_pressure_delta.len) //Process high pressure delta (airflow)
+ for(var/turf/pressurized in high_pressure_delta)
+ spawn pressurized.high_pressure_movements()
+ high_pressure_delta = list()
- return null
-*/
- process()
- if(kill_air)
- return 1
- current_cycle++
- if(groups_to_rebuild.len > 0) process_rebuild_select_groups()
- if(tiles_to_update.len > 0) process_update_tiles()
+ if(current_cycle%10==5) //Check for groups of tiles to resume group processing every 10 cycles
+ for(var/datum/air_group/AG in air_groups)
+ spawn AG.check_regroup()
+ sleep(max(5,update_delay*tick_multiplier))
- process_groups()
- process_singletons()
-
- process_super_conductivity()
- process_high_pressure_delta()
-
- if(current_cycle%10==5) //Check for groups of tiles to resume group processing every 10 cycles
- for(var/datum/air_group/AG in air_groups)
- AG.check_regroup()
-
- return 1
-
- process_update_tiles()
- for(var/turf/simulated/T in tiles_to_update)
- T.update_air_properties()
-/*
- for(var/obj/movable/floor/O in tiles_to_update)
- O.update_air_properties()
-*/
- tiles_to_update.len = 0
-
- process_rebuild_select_groups()
+ proc/process_rebuild_select_groups()
+ //Purpose: This gets called to recalculate and rebuild group geometry
+ //Called by: process()
+ //Inputs: None.
+ //Outputs: None.
var/turf/list/turfs = list()
- for(var/datum/air_group/turf/turf_AG in groups_to_rebuild) //Deconstruct groups, gathering their old members
- for(var/turf/simulated/T in turf_AG.members)
+ for(var/datum/air_group/AG in groups_to_rebuild) //Deconstruct groups, gathering their old members
+ for(var/turf/simulated/T in AG.members)
T.parent = null
turfs += T
- del(turf_AG)
+ del(AG)
for(var/turf/simulated/S in turfs) //Have old members try to form new groups
if(!S.parent)
@@ -307,37 +258,4 @@ datum
for(var/turf/simulated/S in turfs)
S.update_air_properties()
-// var/obj/movable/list/movable_objects = list()
-
- for(var/datum/air_group/object/object_AG in groups_to_rebuild) //Deconstruct groups, gathering their old members
-/*
- for(var/obj/movable/floor/OM in object_AG.members)
- OM.parent = null
- movable_objects += OM
- del(object_AG)
-
- for(var/obj/movable/floor/OM in movable_objects) //Have old members try to form new groups
- if(!OM.parent)
- assemble_group_object(OM)
- for(var/obj/movable/floor/OM in movable_objects)
- OM.update_air_properties()
-*/
- groups_to_rebuild.len = 0
-
- process_groups()
- for(var/datum/air_group/AG in air_groups)
- AG.process_group()
-
- process_singletons()
- for(var/turf/simulated/T in active_singletons)
- T.process_cell()
-
- process_super_conductivity()
- for(var/turf/simulated/hot_potato in active_super_conductivity)
- hot_potato.super_conduct()
-
- process_high_pressure_delta()
- for(var/turf/pressurized in high_pressure_delta)
- pressurized.high_pressure_movements()
-
- high_pressure_delta.len = 0
+ groups_to_rebuild = list()
diff --git a/code/FEA/FEA_turf_tile.dm b/code/FEA/FEA_turf_tile.dm
index 3dd1204c2ac..fa4081b8afe 100644
--- a/code/FEA/FEA_turf_tile.dm
+++ b/code/FEA/FEA_turf_tile.dm
@@ -49,8 +49,8 @@ turf
var/pressure_direction = 0
//optimization vars
- var/next_check = 0 //number of ticks before this tile updates
- var/check_delay = 0 //number of ticks between updates
+// var/next_check = 0 //number of ticks before this tile updates
+// var/check_delay = 0 //number of ticks between updates
proc
high_pressure_movements()
@@ -95,7 +95,7 @@ turf
datum/gas_mixture/air
processing = 1
- datum/air_group/turf/parent
+ datum/air_group/parent
group_border = 0
length_space_border = 0
@@ -192,8 +192,8 @@ turf
parent.suspend_group_processing()
air.merge(giver)
else
- if (giver.total_moles > MINIMUM_AIR_TO_SUSPEND)
- reset_delay()
+// if (giver.total_moles > MINIMUM_AIR_TO_SUSPEND)
+// reset_delay()
air.merge(giver)
@@ -292,7 +292,7 @@ turf
if(air_check_directions)
processing = 1
if(!parent)
- air_master.add_singleton(src)
+ air_master.active_singletons |= src
else
processing = 0
@@ -302,11 +302,11 @@ turf
//and doesn't afraid of anything
//check if we're skipping this tick
- if (next_check > 0)
- next_check--
- return 1
- next_check += check_delay + rand(0,check_delay/2)
- check_delay++
+// if (next_check > 0)
+// next_check--
+// return 1
+// next_check += check_delay + rand(0,check_delay/2)
+// check_delay++
var/turf/simulated/list/possible_fire_spreads = list()
if(processing)
@@ -323,11 +323,11 @@ turf
if(enemy_tile.archived_cycle < archived_cycle) //archive bordering tile information if not already done
enemy_tile.archive()
- if (air && enemy_tile.air)
- var/delay_trigger = air.compare(enemy_tile.air)
- if (!delay_trigger) //if compare() didn't return 1, air is different enough to trigger processing
- reset_delay()
- enemy_tile.reset_delay()
+// if (air && enemy_tile.air)
+// var/delay_trigger = air.compare(enemy_tile.air)
+// if (!delay_trigger) //if compare() didn't return 1, air is different enough to trigger processing
+// reset_delay()
+// enemy_tile.reset_delay()
if(enemy_tile.parent && enemy_tile.parent.group_processing) //apply tile to group sharing
if(enemy_tile.parent.current_cycle < current_cycle) //if the group hasn't been archived, it could just be out of date
@@ -392,7 +392,7 @@ turf
update_visuals(air)
if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST)
- reset_delay() //hotspots always process quickly
+// reset_delay() //hotspots always process quickly
hotspot_expose(air.temperature, CELL_VOLUME)
for(var/atom/movable/item in src)
item.temperature_expose(air, air.temperature, CELL_VOLUME)
@@ -564,11 +564,11 @@ turf
air_master.active_super_conductivity += src
- proc/reset_delay()
+// proc/reset_delay()
//sets this turf to process quickly again
- next_check=0
- check_delay= -5 //negative numbers mean a mandatory quick-update period
+// next_check=0
+// check_delay= -5 //negative numbers mean a mandatory quick-update period
//if this turf has a parent air group, suspend its processing
- if (parent && parent.group_processing)
- parent.suspend_group_processing()
+// if (parent && parent.group_processing)
+// parent.suspend_group_processing()
diff --git a/code/FEA/FEA_variables.dm b/code/FEA/FEA_variables.dm
new file mode 100644
index 00000000000..10d75864c67
--- /dev/null
+++ b/code/FEA/FEA_variables.dm
@@ -0,0 +1,631 @@
+//Please note that mose of these variables are holdovers form ZAS, but I really do not want to expend the effort to prune it down quite yet.
+
+pl_control/var
+ PLASMA_DMG = 3
+ PLASMA_DMG_NAME = "Plasma Damage Multiplier"
+ PLASMA_DMG_DESC = "Multiplier on how much damage inhaling plasma can do."
+
+ OXY_TO_PLASMA = 1
+ OXY_TO_PLASMA_NAME = "O2/Plasma Ratio"
+ OXY_TO_PLASMA_DESC = "Multiplier for the ratio of oxygen to plasma required in fires."
+
+ CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+ CLOTH_CONTAMINATION_NAME = "Plasma - Cloth Contamination"
+ CLOTH_CONTAMINATION_RANDOM = 60
+ CLOTH_CONTAMINATION_METHOD = "Toggle"
+ CLOTH_CONTAMINATION_DESC = "If set to nonzero, plasma will contaminate cloth items (uniforms, backpacks, etc.)\
+ and cause a small amount of damage over time to anyone carrying or wearing them. Contamination can be detected\
+ with a Health Analyzer, and washed off in the washer."
+
+ ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or
+ //decontaminated.
+ ALL_ITEM_CONTAMINATION_NAME = "Plasma - Full Contamination"
+ ALL_ITEM_CONTAMINATION_RANDOM = 10
+ ALL_ITEM_CONTAMINATION_METHOD = "Toggle"
+ ALL_ITEM_CONTAMINATION_DESC = "Like Cloth Contamination, but all item types are susceptible."
+
+ PLASMAGUARD_ONLY = 0
+ PLASMAGUARD_ONLY_NAME = "Plasma - Biosuits/Spacesuits Only"
+ PLASMAGUARD_ONLY_RANDOM = 20
+ PLASMAGUARD_ONLY_METHOD = "Toggle"
+ PLASMAGUARD_ONLY_DESC = "If on, any suits that are not biosuits or space suits will not protect against contamination."
+
+ //CANISTER_CORROSION = 0 //If this is on, plasma must be stored in orange tanks and canisters,
+ //CANISTER_CORROSION_RANDOM = 20 //or it will corrode the tank.
+ //CANISTER_CORROSION_METHOD = "Toggle"
+
+ GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 10,000.
+ GENETIC_CORRUPTION_NAME = "Plasma - Genetic Corruption"
+ GENETIC_CORRUPTION_RANDOM = "PROB10/3d6"
+ GENETIC_CORRUPTION_METHOD = "Numeric"
+ GENETIC_CORRUPTION_DESC = "When set to a probability in 1000, any humans in plasma will have this chance to develop a random mutation."
+
+ SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited.
+ SKIN_BURNS_NAME = "Plasma - Skin Burns"
+ SKIN_BURNS_RANDOM = 10
+ SKIN_BURNS_METHOD = "Toggle"
+ SKIN_BURNS_DESC = "When toggled, humans with exposed skin will suffer burns (similar to mustard gas) in plasma."
+
+ //PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time.
+ //PLASMA_INJECTS_TOXINS_RANDOM = 30
+ //PLASMA_INJECTS_TOXINS_METHOD = "Toggle"
+
+ EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection.
+ EYE_BURNS_NAME = "Plasma - Eye Burns"
+ EYE_BURNS_RANDOM = 30
+ EYE_BURNS_METHOD = "Toggle"
+ EYE_BURNS_DESC = "When toggled, humans without masks that cover the eyes will suffer temporary blurriness and sight loss,\
+ and may need glasses to see again if exposed for long durations."
+
+ //N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high.
+ //N2O_REACTION_RANDOM = 5
+
+ //PLASMA_COLOR = "onturf" //Plasma can change colors yaaaay!
+ //PLASMA_COLOR_RANDOM = "PICKonturf,onturf"
+
+ //PLASMA_DMG_OFFSET = 1
+ //PLASMA_DMG_OFFSET_RANDOM = "1d5"
+ //PLASMA_DMG_QUOTIENT = 10
+ //PLASMA_DMG_QUOTIENT_RANDOM = "1d10+4"
+
+ CONTAMINATION_LOSS = 0.1
+ _NAME = "Plasma - Contamination Damage"
+ CONTAMINATION_LOSS_DESC = "A number representing the damage done per life cycle by contaminated items."
+
+ PLASMA_HALLUCINATION = 1
+ PLASMA_HALLUCINATION_NAME = "Plasma - Hallucination"
+ PLASMA_HALLUCINATION_METHOD = "Toggle"
+ PLASMA_HALLUCINATION_DESC = "If toggled, uses the remnants of the hallucination code to induce visions in those\
+ who breathe plasma."
+ N2O_HALLUCINATION = 1
+ N2O_HALLUCINATION_NAME = "Nitrous Oxide - Hallucination"
+ N2O_HALLUCINATION_METHOD = "Toggle"
+ N2O_HALLUCINATION_DESC = "If toggled, uses the remnants of the hallucination code to induce visions in those\
+ who breathe N2O."
+ //CONTAMINATION_LOSS_RANDOM = "5d5"
+//Plasma has a chance to be a different color.
+
+var/tick_multiplier = 2
+/datum/controller/air_system/var
+ //Used in /mob/carbon/human/life
+ OXYGEN_LOSS = 2
+ OXYGEN_LOSS_NAME = "Damage - Oxygen Loss"
+ OXYGEN_LOSS_DESC = "A multiplier for damage due to lack of air, CO2 poisoning, and vacuum. Does not affect oxyloss\
+ from being incapacitated or dying."
+ TEMP_DMG = 2
+ TEMP_DMG_NAME = "Damage - Temperature"
+ TEMP_DMG_DESC = "A multiplier for damage due to body temperature irregularities."
+ BURN_DMG = 6
+ BURN_DMG_NAME = "Damage - Fire"
+ BURN_DMG_DESC = "A multiplier for damage due to direct fire exposure."
+
+ AF_TINY_MOVEMENT_THRESHOLD = 50 //% difference to move tiny items.
+ AF_TINY_MOVEMENT_THRESHOLD_NAME = "Airflow - Tiny Movement Threshold %"
+ AF_TINY_MOVEMENT_THRESHOLD_DESC = "Percent of 1 Atm. at which items with the tiny weight class will move."
+ AF_SMALL_MOVEMENT_THRESHOLD = 70 //% difference to move small items.
+ AF_SMALL_MOVEMENT_THRESHOLD_NAME = "Airflow - Small Movement Threshold %"
+ AF_SMALL_MOVEMENT_THRESHOLD_DESC = "Percent of 1 Atm. at which items with the small weight class will move."
+ AF_NORMAL_MOVEMENT_THRESHOLD = 90 //% difference to move normal items.
+ AF_NORMAL_MOVEMENT_THRESHOLD_NAME = "Airflow - Normal Movement Threshold %"
+ AF_NORMAL_MOVEMENT_THRESHOLD_DESC = "Percent of 1 Atm. at which items with the normal weight class will move."
+ AF_LARGE_MOVEMENT_THRESHOLD = 100 //% difference to move large and huge items.
+ AF_LARGE_MOVEMENT_THRESHOLD_NAME = "Airflow - Large Movement Threshold %"
+ AF_LARGE_MOVEMENT_THRESHOLD_DESC = "Percent of 1 Atm. at which items with the large or huge weight class will move."
+ AF_DENSE_MOVEMENT_THRESHOLD = 120 //% difference to move dense crap and mobs.
+ AF_DENSE_MOVEMENT_THRESHOLD_NAME = "Airflow - Dense Movement Threshold %"
+ AF_DENSE_MOVEMENT_THRESHOLD_DESC = "Percent of 1 Atm. at which dense objects (canisters, etc.) will be shifted by airflow."
+ AF_MOB_MOVEMENT_THRESHOLD = 175
+ AF_MOB_MOVEMENT_THRESHOLD_NAME = "Airflow - Human Movement Threshold %"
+ AF_MOB_MOVEMENT_THRESHOLD_DESC = "Percent of 1 Atm. at which mobs will be shifted by airflow."
+
+ AF_HUMAN_STUN_THRESHOLD = 130
+ AF_HUMAN_STUN_THRESHOLD_NAME = "Airflow - Human Stun Threshold %"
+ AF_HUMAN_STUN_THRESHOLD_DESC = "Percent of 1 Atm. at which living things are stunned or knocked over."
+
+ AF_PERCENT_OF = ONE_ATMOSPHERE
+ AF_PERCENT_OF_NAME = "Airflow - 100% Pressure"
+ AF_PERCENT_OF_DESC = "Normally set to 1 Atm. in kPa, this indicates what pressure is considered 100% by the system."
+
+ AF_SPEED_MULTIPLIER = 4 //airspeed per movement threshold value crossed.
+ AF_SPEED_MULTIPLIER_NAME = "Airflow - Speed Increase per 10%"
+ AF_SPEED_MULTIPLIER_DESC = "Velocity increase of shifted items per 10% of airflow."
+ AF_DAMAGE_MULTIPLIER = 5 //Amount of damage applied per airflow_speed.
+ AF_DAMAGE_MULTIPLIER_NAME = "Airflow - Damage Per Velocity"
+ AF_DAMAGE_MULTIPLIER_DESC = "Amount of damage applied per unit of speed (1-15 units) at which mobs are thrown."
+ AF_STUN_MULTIPLIER = 1.5 //Seconds of stun applied per airflow_speed.
+ AF_STUN_MULTIPLIER_NAME = "Airflow - Stun Per Velocity"
+ AF_STUN_MULTIPLIER_DESC = "Amount of stun effect applied per unit of speed (1-15 units) at which mobs are thrown."
+ AF_SPEED_DECAY = 0.5 //Amount that flow speed will decay with time.
+ AF_SPEED_DECAY_NAME = "Airflow - Velocity Lost per Tick"
+ AF_SPEED_DECAY_DESC = "Amount of airflow speed lost per tick on a moving object."
+ AF_SPACE_MULTIPLIER = 2 //Increasing this will make space connections more DRAMATIC!
+ AF_SPACE_MULTIPLIER_NAME = "Airflow - Space Airflow Multiplier"
+ AF_SPACE_MULTIPLIER_DESC = "Increasing this multiplier will cause more powerful airflow to space."
+ AF_CANISTER_MULTIPLIER = 0.25
+ AF_CANISTER_MULTIPLIER_NAME = "Airflow - Canister Airflow Multiplier"
+ AF_CANISTER_MULTIPLIER_DESC = "Increasing this multiplier will cause more powerful airflow from single-tile sources like canisters."
+
+/datum/controller/air_system
+ var
+ list/settings = list()
+ list/bitflags = list("1","2","4","8","16","32","64","128","256","512","1024")
+ 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 plc.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"
+ if(ch in plc.settings)
+ vw = plc.vars[ch]
+ if("[ch]_METHOD" in vars)
+ how = plc.vars["[ch]_METHOD"]
+ else
+ if(isnum(vw))
+ how = "Numeric"
+ else
+ how = "Text"
+ else
+ vw = vars[ch]
+ 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 [ch] to [newvar]."
+ //user << "[which] has been changed 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)
+ ////world << "Plasma randomized."
+
+ proc/SetDefault(def)
+ switch(def)
+ if("Original")
+ plc.CLOTH_CONTAMINATION = 0 //If this is on, plasma does damage by getting into cloth.
+
+ plc.ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or
+
+ plc.PLASMAGUARD_ONLY = 0
+
+ //plc.CANISTER_CORROSION = 0 //If this is on, plasma must be stored in orange tanks and canisters,
+
+ 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.PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time.
+
+ plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection.
+
+ //plc.N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high.
+
+ //plc.PLASMA_COLOR = "onturf" //Plasma can change colors yaaaay!
+
+ //plc.PLASMA_DMG_OFFSET = 1
+ //plc.PLASMA_DMG_QUOTIENT = 10
+
+ plc.CONTAMINATION_LOSS = 0
+ if("Hazard-Low")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+
+ plc.ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or
+
+ plc.PLASMAGUARD_ONLY = 0
+
+ // plc.CANISTER_CORROSION = 0 //If this is on, plasma must be stored in orange tanks and canisters,
+
+ 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.PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time.
+
+ plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection.
+
+ // plc.N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high.
+
+ // plc.PLASMA_COLOR = "onturf" //RBPYB
+
+ //if(prob(20))
+ // plc.PLASMA_COLOR = pick("red","yellow","blue","purple")
+
+ //plc.PLASMA_DMG_OFFSET = 1.5
+ //plc.PLASMA_DMG_QUOTIENT = 8
+
+ plc.CONTAMINATION_LOSS = 0.01
+
+ var/s = pick(plc.settings)
+ plc.Randomize(s)
+
+ if("Hazard-High")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+
+ plc.ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or
+
+ plc.PLASMAGUARD_ONLY = 0
+
+ // plc.CANISTER_CORROSION = 1 //If this is on, plasma must be stored in orange tanks and canisters,
+
+ 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.PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time.
+
+ plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection.
+
+ // plc.N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high.
+
+ // plc.PLASMA_COLOR = "onturf"//pick("red","yellow","blue","purple") //RBPYB
+
+ //plc.PLASMA_DMG_OFFSET = 3
+ //plc.PLASMA_DMG_QUOTIENT = 5
+
+ plc.CONTAMINATION_LOSS = 0.05
+
+ for(var/i = rand(3,5),i>0,i--)
+ var/s = pick(plc.settings)
+ plc.Randomize(s)
+
+ if("Everything")
+ plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth.
+
+ plc.ALL_ITEM_CONTAMINATION = 1 //If this is on, any item can be contaminated, so suits and tools must be discarded or ELSE
+
+ plc.PLASMAGUARD_ONLY = 1
+
+ // plc.CANISTER_CORROSION = 1 //If this is on, plasma must be stored in orange tanks and canisters,
+
+ 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.PLASMA_INJECTS_TOXINS = 1 //Plasma damage injects the toxins chemical to do damage over time.
+
+ plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection.
+
+ // plc.N2O_REACTION = 1 //Plasma can react with N2O, making sparks and starting a fire if levels are high.
+
+ // plc.PLASMA_COLOR = "onturf" //RBPYB
+
+ //plc.PLASMA_DMG_OFFSET = 3
+ //plc.PLASMA_DMG_QUOTIENT = 5
+
+ plc.CONTAMINATION_LOSS = 0.02
+ /////world << "Plasma color updated."
+
+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)
+ //world << "Randomizing [V]"
+ var/newvalue
+ if("[V]_RANDOM" in vars)
+ if(isnum(vars["[V]_RANDOM"]))
+ newvalue = prob(vars["[V]_RANDOM"])
+ if(newvalue)
+ //world << "Probability [vars["[V]_RANDOM"]]%: Success."
+ else
+ //world << "Probability [vars["[V]_RANDOM"]]%: Failure."
+ else if(istext(vars["[V]_RANDOM"]))
+ var/txt = vars["[V]_RANDOM"]
+ if(findtextEx(txt,"PROB"))
+ //world << "Probability/Roll Combo \..."
+ txt = dd_text2list(txt,"/")
+ txt[1] = dd_replacetext(txt[1],"PROB","")
+ var/p = text2num(txt[1])
+ var/r = txt[2]
+ //world << "Prob:[p]% Roll:[r]"
+ if(prob(p))
+ newvalue = roll(r)
+ //world << "Success. New value: [newvalue]"
+ else
+ newvalue = vars[V]
+ //world << "Probability check failed."
+ else if(findtextEx(txt,"PICK"))
+ txt = dd_replacetext(txt,"PICK","")
+ //world << "Pick: [txt]"
+ txt = dd_text2list(txt,",")
+ newvalue = pick(txt)
+ //world << "Picked: [newvalue]"
+ else
+ newvalue = roll(txt)
+ //world << "Roll: [txt] - [newvalue]"
+ else
+ newvalue = vars[V]
+ vars[V] = newvalue
+ ////world << "Plasma color updated."
+
+mob/proc/Change_Airflow_Constants()
+ set category = "Debug"
+
+ var/choice = input("Which constant will you modify?","Change Airflow Constants")\
+ as null|anything in list("Movement Threshold","Speed Multiplier","Damage Multiplier","Stun Multiplier","Speed Decay")
+
+ var/n
+
+ switch(choice)
+ if("Movement Threshold")
+ n = input("What will you change it to","Change Airflow Constants",air_master.AF_DENSE_MOVEMENT_THRESHOLD) as num
+ n = max(1,n)
+ air_master.AF_DENSE_MOVEMENT_THRESHOLD = n
+ world.log << "air_master.AF_DENSE_MOVEMENT_THRESHOLD set to [n]."
+ if("Speed Multiplier")
+ n = input("What will you change it to","Change Airflow Constants",air_master.AF_SPEED_MULTIPLIER) as num
+ n = max(1,n)
+ air_master.AF_SPEED_MULTIPLIER = n
+ world.log << "air_master.AF_SPEED_MULTIPLIER set to [n]."
+ if("Damage Multiplier")
+ n = input("What will you change it to","Change Airflow Constants",air_master.AF_DAMAGE_MULTIPLIER) as num
+ air_master.AF_DAMAGE_MULTIPLIER = n
+ world.log << "AF_DAMAGE_MULTIPLIER set to [n]."
+ if("Stun Multiplier")
+ n = input("What will you change it to","Change Airflow Constants",air_master.AF_STUN_MULTIPLIER) as num
+ air_master.AF_STUN_MULTIPLIER = n
+ world.log << "AF_STUN_MULTIPLIER set to [n]."
+ if("Speed Decay")
+ n = input("What will you change it to","Change Airflow Constants",air_master.AF_SPEED_DECAY) as num
+ air_master.AF_SPEED_DECAY = n
+ world.log << "AF_SPEED_DECAY set to [n]."
+ if("Space Flow Multiplier")
+ n = input("What will you change it to","Change Airflow Constants",air_master.AF_SPEED_DECAY) as num
+ air_master.AF_SPEED_DECAY = n
+ world.log << "AF_SPEED_DECAY set to [n]."
+
+obj/var/contaminated = 0
+
+obj/item/proc
+ can_contaminate()
+ if(flags & PLASMAGUARD) return 0
+ if((flags & SUITSPACE) && !air_master.plc.PLASMAGUARD_ONLY) return 1
+ if(air_master.plc.ALL_ITEM_CONTAMINATION) return 1
+ else if(istype(src,/obj/item/clothing)) return 1
+ else if(istype(src,/obj/item/weapon/storage/backpack)) return 1
+
+/mob/living/carbon/human/proc/contaminate()
+ if(!pl_suit_protected())
+ suit_contamination()
+ else if(air_master.plc.PLASMAGUARD_ONLY)
+ if(!wear_suit.flags & PLASMAGUARD) wear_suit.contaminated = 1
+
+
+
+ if(!pl_head_protected())
+ if(wear_mask) wear_mask.contaminated = 1
+ if(prob(1)) suit_contamination() //Plasma can sometimes get through such an open suit.
+ else if(air_master.plc.PLASMAGUARD_ONLY)
+ if(!head.flags & PLASMAGUARD) head.contaminated = 1
+
+ if(istype(back,/obj/item/weapon/storage/backpack) || air_master.plc.ALL_ITEM_CONTAMINATION)
+ back.contaminated = 1
+
+ if(l_hand)
+ if(l_hand.can_contaminate()) l_hand.contaminated = 1
+ if(r_hand)
+ if(r_hand.can_contaminate()) r_hand.contaminated = 1
+ if(belt)
+ if(belt.can_contaminate()) belt.contaminated = 1
+ if(wear_id && !pl_suit_protected())
+ if(wear_id.can_contaminate()) wear_id.contaminated = 1
+ if(l_ear && !pl_head_protected())
+ if(l_ear.can_contaminate()) l_ear.contaminated = 1
+ if(r_ear && !pl_head_protected())
+ if(r_ear.can_contaminate()) r_ear.contaminated = 1
+
+/mob/living/carbon/human/proc/pl_effects()
+ if(stat >= 2)
+ return
+ if(air_master.plc.SKIN_BURNS)
+ if(!pl_head_protected() || !pl_suit_protected())
+ burn_skin(0.75)
+ if (coughedtime != 1)
+ coughedtime = 1
+ emote("gasp")
+ spawn (20)
+ coughedtime = 0
+ updatehealth()
+ if(air_master.plc.EYE_BURNS && !pl_head_protected())
+ if(!wear_mask)
+ if(prob(20)) usr << "\red Your eyes burn!"
+ eye_stat += 2.5
+ eye_blurry += 1.5
+ if (eye_stat >= 20 && !(disabilities & 1))
+ src << "\red Your eyes start to burn badly!"
+ disabilities |= 1
+ if (prob(max(0,eye_stat - 20) + 1))
+ src << "\red You are blinded!"
+ eye_blind += 20
+ eye_stat = max(eye_stat-25,0)
+ else
+ if(!(wear_mask.flags & MASKCOVERSEYES))
+ if(prob(20)) usr << "\red Your eyes burn!"
+ eye_stat += 2.5
+ eye_blurry = min(eye_blurry+1.5,50)
+ if (eye_stat >= 20 && !(disabilities & 1))
+ src << "\red Your eyes start to burn badly!"
+ disabilities |= 1
+ if (prob(max(0,eye_stat - 20) + 1) &&!eye_blind)
+ src << "\red You are blinded!"
+ eye_blind += 20
+ eye_stat = 0
+ if(air_master.plc.GENETIC_CORRUPTION)
+ if(rand(1,1000) < air_master.plc.GENETIC_CORRUPTION)
+ randmutb(src)
+ src << "\red High levels of toxins cause you to spontaneously mutate."
+ domutcheck(src,null)
+
+/mob/living/carbon/human/proc/FireBurn(mx as num)
+ //NO! NOT INTO THE PIT! IT BURRRRRNS!
+ mx *= air_master.BURN_DMG
+
+ var
+ head_exposure = 1
+ chest_exposure = 1
+ groin_exposure = 1
+ legs_exposure = 1
+ feet_exposure = 1
+ arms_exposure = 1
+ hands_exposure = 1
+ for(var/obj/item/clothing/C in src)
+ if(l_hand == C || r_hand == C) continue
+ if(C.body_parts_covered & HEAD)
+ head_exposure *= C.heat_transfer_coefficient
+ if(C.body_parts_covered & UPPER_TORSO)
+ chest_exposure *= C.heat_transfer_coefficient
+ if(C.body_parts_covered & LOWER_TORSO)
+ groin_exposure *= C.heat_transfer_coefficient
+ if(C.body_parts_covered & LEGS)
+ legs_exposure *= C.heat_transfer_coefficient
+ if(C.body_parts_covered & FEET)
+ feet_exposure *= C.heat_transfer_coefficient
+ if(C.body_parts_covered & ARMS)
+ arms_exposure *= C.heat_transfer_coefficient
+ if(C.body_parts_covered & HANDS)
+ arms_exposure *= C.heat_transfer_coefficient
+
+ mx *= 10
+
+ apply_damage("head", 0, 2.5*mx*head_exposure)
+ apply_damage("chest", 0, 2.5*mx*chest_exposure)
+ apply_damage("groin", 0, 2.0*mx*groin_exposure)
+ apply_damage("l_leg", 0, 0.6*mx*legs_exposure)
+ apply_damage("r_leg", 0, 0.6*mx*legs_exposure)
+ apply_damage("l_arm", 0, 0.4*mx*arms_exposure)
+ apply_damage("r_arm", 0, 0.4*mx*arms_exposure)
+ apply_damage("l_foot", 0, 0.25*mx*feet_exposure)
+ apply_damage("r_foot", 0, 0.25*mx*feet_exposure)
+ apply_damage("l_hand", 0, 0.25*mx*hands_exposure)
+ apply_damage("r_hand", 0, 0.25*mx*hands_exposure)
+
+/mob/living/carbon/human/proc/suit_interior()
+ . = list()
+ if(!pl_suit_protected())
+ for(var/obj/item/I in src)
+ . += I
+ return .
+ . += wear_mask
+ . += w_uniform
+ . += shoes
+ . += gloves
+ if(!pl_head_protected())
+ . += head
+
+/mob/living/carbon/human/proc/pl_head_protected()
+ if(head)
+ if(head.flags & PLASMAGUARD || head.flags & HEADSPACE) return 1
+ return 0
+
+/mob/living/carbon/human/proc/pl_suit_protected()
+ if(wear_suit)
+ if(wear_suit.flags & PLASMAGUARD || wear_suit.flags & SUITSPACE) return 1
+ return 0
+
+/mob/living/carbon/human/proc/suit_contamination()
+ if(air_master.plc.ALL_ITEM_CONTAMINATION)
+ for(var/obj/item/I in src)
+ I.contaminated = 1
+ else
+ if(wear_suit) wear_suit.contaminated = 1
+ if(w_uniform) w_uniform.contaminated = 1
+ if(shoes) shoes.contaminated = 1
+ if(gloves) gloves.contaminated = 1
+ if(wear_mask) wear_mask.contaminated = 1
+
+
+turf/Entered(obj/item/I)
+ . = ..()
+ if(istype(I))
+ var/datum/gas_mixture/env = return_air(1)
+ if(env.toxins > 0.35)
+ if(I.can_contaminate())
+ I.contaminated = 1
\ No newline at end of file
diff --git a/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm b/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm
index 0bcb010bf4c..5661f8d212d 100644
--- a/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm
+++ b/code/WorkInProgress/Cael_Aislinn/Supermatter/SuperMatter.dm
@@ -60,6 +60,11 @@
//a lot of these variables are pretty hacked, so dont rely on the comments
/obj/machinery/power/supermatter/process()
+ var/datum/gas_mixture/env = loc.return_air()
+
+ //Remove gas from surrounding area
+ var/transfer_moles = gasefficency * env.total_moles
+ var/datum/gas_mixture/removed = env.remove(transfer_moles)
//core can no longer spontaneously explode
/*
@@ -110,15 +115,15 @@
//nothing can happen in a vacuum
var/datum/gas_mixture/removed = env
var/retardation_factor = 0.5
- if(env.total_moles())
+ if(env.total_moles)
//Remove gas from surrounding area
- var/transfer_moles = gasefficency * env.total_moles()
+ var/transfer_moles = gasefficency * env.total_moles
removed = env.remove(transfer_moles)
//100% oxygen atmosphere = 100% plasma production
//100% nitrogen atmosphere = 0% plasma production
//anything else is halfway in between; an atmosphere with no nitrogen or oxygen will still be at 50% (but steadily rise as more oxygen is made)
- var/total_moles = removed.total_moles()
+ var/total_moles = removed.total_moles
if(total_moles)
retardation_factor += removed.oxygen / (total_moles * 2) - removed.nitrogen / (total_moles * 2)
else
@@ -143,6 +148,7 @@
//
produced = device_energy * OXYGEN_RELEASE_MODIFIER * retardation_factor
removed.oxygen += produced
+ removed.update_values()
//
mega_energy = 0
diff --git a/code/WorkInProgress/Ported/ZeroPoint/SuperMatter.dm b/code/WorkInProgress/Ported/ZeroPoint/SuperMatter.dm
deleted file mode 100644
index 9b77e9626b0..00000000000
--- a/code/WorkInProgress/Ported/ZeroPoint/SuperMatter.dm
+++ /dev/null
@@ -1,120 +0,0 @@
-#define NITROGEN_RETARDATION_FACTOR 4 //Higher == N2 slows reaction more
-#define THERMAL_RELEASE_MODIFIER 50 //Higher == less heat released during reaction
-#define PLASMA_RELEASE_MODIFIER 750 //Higher == less plasma released by reaction
-#define OXYGEN_RELEASE_MODIFIER 1500 //Higher == less oxygen released at high temperature/power
-#define REACTION_POWER_MODIFIER 1.1 //Higher == more overall power
-
-/obj/machinery/engine/supermatter
- name = "Supermatter"
- desc = "A strangely translucent and iridescent crystal. \red You get headaches just from looking at it."
- icon = 'engine.dmi'
- icon_state = "darkmatter"
- density = 1
- anchored = 1
-
- var/gasefficency = 0.25
-
- var/det = 0
- var/previousdet = 0
- var/const/explosiondet = 3500
-
- var/const/warningtime = 50 // Make the CORE OVERLOAD message repeat only every aprox. ?? seconds
- var/lastwarning = 0 // Time in 1/10th of seconds since the last sent warning
-
-/obj/machinery/engine/klaxon
- name = "Emergency Klaxon"
- icon = 'engine.dmi'
- icon_state = "darkmatter"
- density = 1
- anchored = 1
- var/obj/machinery/engine/supermatter/sup
-
-/obj/machinery/engine/klaxon/process()
- if(!sup)
- for(var/obj/machinery/engine/supermatter/T in world)
- sup = T
- break
- if(sup.det >= 1)
- return
-
-/obj/machinery/engine/supermatter/process()
-
- var/turf/simulated/L = loc
-
- //Ok, get the air from the turf
- var/datum/gas_mixture/env = L.return_air()
-
- //Remove gas from surrounding area
- var/transfer_moles = gasefficency * env.total_moles
- var/datum/gas_mixture/removed = env.remove(transfer_moles)
-
- previousdet = det
- det += (removed.temperature - 1000) / 150
- det = max(det, 0)
-
- if(det > 0 && removed.temperature > 1000) // while the core is still damaged and it's still worth noting its status
- if((world.realtime - lastwarning) / 10 >= warningtime)
- lastwarning = world.realtime
- if(explosiondet - det <= 300)
- radioalert("CORE EXPLOSION IMMINENT","Core control computer")
- else if(det >= previousdet) // The damage is still going up
- radioalert("CORE OVERLOAD","Core control computer")
- else // Phew, we're safe
- radioalert("Core returning to safe operating levels.","Core control computer")
-
- if(det > explosiondet)
- roundinfo.core = 1
- //proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, force = 0)
- explosion(src.loc,8,15,20,30,1)
- det = 0
-
- if (!removed)
- return 1
-
- var/power = max(round((removed.temperature - T0C) / 20), 0) //Total laser power plus an overload factor
-
- //Get the collective laser power
- for(var/dir in cardinal)
- var/turf/T = get_step(L, dir)
- for(var/obj/effect/beam/e_beam/item in T)
- power += item.power
-
- //Ok, 100% oxygen atmosphere = best reaction
- //Maxes out at 100% oxygen pressure
- var/oxygen = max(min((removed.oxygen - (removed.nitrogen * NITROGEN_RETARDATION_FACTOR)) / MOLES_CELLSTANDARD, 1), 0)
-
- var/device_energy = oxygen * power
-
- device_energy *= removed.temperature / T0C
-
- device_energy = round(device_energy * REACTION_POWER_MODIFIER)
-
- //To figure out how much temperature to add each tick, consider that at one atmosphere's worth
- //of pure oxygen, with all four lasers firing at standard energy and no N2 present, at room temperature
- //that the device energy is around 2140. At that stage, we don't want too much heat to be put out
- //Since the core is effectively "cold"
-
- //Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock
- //is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall.
- removed.temperature += max((device_energy / THERMAL_RELEASE_MODIFIER), 0)
-
- removed.temperature = min(removed.temperature, 1500)
-
- //Calculate how much gas to release
- removed.toxins += max(round(device_energy / PLASMA_RELEASE_MODIFIER), 0)
-
- removed.oxygen += max(round((device_energy + removed.temperature - T0C) / OXYGEN_RELEASE_MODIFIER), 0)
-
- env.merge(removed)
-
-//Not functional currently. -- SkyMarshal
-/*
- for(var/mob/living/carbon/l in view(src, 6)) // you have to be seeing the core to get hallucinations
- if(prob(10) && !(l.glasses && istype(l.glasses, /obj/item/clothing/glasses/meson)))
- l.hallucination = 50
-*/
- for(var/mob/living/l in view(src,3))
- l.bruteloss += 50
- l.updatehealth()
-
- return 1
\ No newline at end of file
diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm
index 83ca41f581d..ca37c394dda 100644
--- a/code/game/machinery/atmoalter/canister.dm
+++ b/code/game/machinery/atmoalter/canister.dm
@@ -289,6 +289,7 @@ Release Pressure: - - - - - - The Master Controller has fired. Automatic restart aborted."
+ M << " The Master Controller has fired. Automatic restart aborted."
\ No newline at end of file
diff --git a/code/game/objects/tank.dm b/code/game/objects/tank.dm
index ed1cdbd865b..73b890be9e7 100644
--- a/code/game/objects/tank.dm
+++ b/code/game/objects/tank.dm
@@ -244,7 +244,6 @@
src.air_contents = new /datum/gas_mixture()
src.air_contents.volume = volume //liters
src.air_contents.temperature = T20C
- air_contents.update_values()
processing_objects.Add(src)
diff --git a/code/game/objects/tanks/emergency.dm b/code/game/objects/tanks/emergency.dm
index 0d767fd2700..71ca3243f12 100644
--- a/code/game/objects/tanks/emergency.dm
+++ b/code/game/objects/tanks/emergency.dm
@@ -11,7 +11,7 @@
New()
..()
- src.air_contents.oxygen = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.adjustGases((3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
@@ -41,10 +41,7 @@
New()
..()
- src.air_contents.oxygen = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD
-
var/datum/gas/sleeping_agent/trace_gas = new()
trace_gas.moles = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD
-
- src.air_contents.trace_gases += trace_gas
+ air_contents.adjustGases((3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD, traces = list(trace_gas))
return
\ No newline at end of file
diff --git a/code/game/objects/tanks/jetpack.dm b/code/game/objects/tanks/jetpack.dm
index 7e829f4a9a0..a8b853b6b7c 100644
--- a/code/game/objects/tanks/jetpack.dm
+++ b/code/game/objects/tanks/jetpack.dm
@@ -75,7 +75,7 @@
New()
..()
- src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.adjustGases((6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/jetpack/oxygen
@@ -86,7 +86,7 @@
New()
..()
- src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.adjustGases((6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
/obj/item/weapon/tank/jetpack/carbondioxide
@@ -99,7 +99,7 @@
..()
src.ion_trail = new /datum/effect/effect/system/ion_trail_follow()
src.ion_trail.set_up(src)
- src.air_contents.carbon_dioxide = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.adjustGases(0,(6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
examine()
diff --git a/code/game/objects/tanks/oxygen.dm b/code/game/objects/tanks/oxygen.dm
index 2ce809aeaa1..3c06dd0c644 100644
--- a/code/game/objects/tanks/oxygen.dm
+++ b/code/game/objects/tanks/oxygen.dm
@@ -8,7 +8,7 @@
New()
..()
- src.air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C)
+ air_contents.adjustGases((6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
return
diff --git a/code/setup.dm b/code/setup.dm
index 77356bd1a5b..05e8e837728 100644
--- a/code/setup.dm
+++ b/code/setup.dm
@@ -138,6 +138,8 @@ var/MAX_EXPLOSION_RANGE = 14
#define BLOCKHAIR 32768 // temporarily removes the user's hair icon
+#define PLASMAGUARD 65536 //Does not get contaminated by plasma.
+
//flags for pass_flags
#define PASSTABLE 1
#define PASSGLASS 2
@@ -288,4 +290,4 @@ var/static/list/scarySounds = list('thudswoosh.ogg','Taser.ogg','armbomb.ogg','h
// Maximum and minimum character ages.
var/const/minimum_age = 20
-var/const/maximum_age = 65
+var/const/maximum_age = 65
\ No newline at end of file