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