diff --git a/baystation12.dme b/baystation12.dme index 62204e566d4..f1a49d91224 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -361,12 +361,6 @@ #include "code\defines\sd_procs\constants.dm" #include "code\defines\sd_procs\direction.dm" #include "code\defines\sd_procs\math.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\game\algorithm.dm" #include "code\game\atom_procs.dm" #include "code\game\cellautomata.dm" @@ -1210,6 +1204,17 @@ #include "code\WorkInProgress\virus2\monkeydispensor.dm" #include "code\WorkInProgress\virus2\Prob.dm" #include "code\WorkInProgress\Wrongnumber\weldbackpack.dm" +#include "code\ZAS\Connection.dm" +#include "code\ZAS\Creation.dm" +#include "code\ZAS\Definition.dm" +#include "code\ZAS\FEA_gas_mixture.dm" +#include "code\ZAS\FEA_system.dm" +#include "code\ZAS\FEA_turf_tile.dm" +#include "code\ZAS\Fire.dm" +#include "code\ZAS\Functions.dm" +#include "code\ZAS\Plasma.dm" +#include "code\ZAS\Processing.dm" +#include "code\ZAS\Variable Settings.dm" #include "interface\interface.dm" #include "interface\skin.dmf" #include "maps\tgstation.2.0.8.dmm" diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 1b63e605481..5ade345e08f 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -128,28 +128,19 @@ datum/pipeline var/datum/gas_mixture/air_sample = air.remove_ratio(mingle_volume/air.volume) air_sample.volume = mingle_volume - if(istype(target) && target.parent && target.parent.group_processing) + if(istype(target) && target.zone) //Have to consider preservation of group statuses var/datum/gas_mixture/turf_copy = new - turf_copy.copy_from(target.parent.air) - turf_copy.volume = target.parent.air.volume //Copy a good representation of the turf from parent group + turf_copy.copy_from(target.zone.air) + turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group equalize_gases(list(air_sample, turf_copy)) air.merge(air_sample) - if(target.parent.air.compare(turf_copy)) - //The new turf would be an acceptable group member so permit the integration + turf_copy.subtract(target.zone.air) - turf_copy.subtract(target.parent.air) - - target.parent.air.merge(turf_copy) - - else - //Comparison failure so dissemble group and copy turf - - target.parent.suspend_group_processing() - target.air.copy_from(turf_copy) + target.zone.air.merge(turf_copy) else var/datum/gas_mixture/turf_air = target.return_air() @@ -187,9 +178,9 @@ datum/pipeline var/delta_temperature = 0 var/sharer_heat_capacity = 0 - if(modeled_location.parent && modeled_location.parent.group_processing) - delta_temperature = (air.temperature - modeled_location.parent.air.temperature) - sharer_heat_capacity = modeled_location.parent.air.heat_capacity() + if(modeled_location.zone) + delta_temperature = (air.temperature - modeled_location.zone.air.temperature) + sharer_heat_capacity = modeled_location.zone.air.heat_capacity() else delta_temperature = (air.temperature - modeled_location.air.temperature) sharer_heat_capacity = modeled_location.air.heat_capacity() @@ -208,14 +199,8 @@ datum/pipeline air.temperature += self_temperature_delta - if(modeled_location.parent && modeled_location.parent.group_processing) - if((abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && (abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*modeled_location.parent.air.temperature)) - modeled_location.parent.suspend_group_processing() - - modeled_location.air.temperature += sharer_temperature_delta - - else - modeled_location.parent.air.temperature += sharer_temperature_delta/modeled_location.parent.air.group_multiplier + if(modeled_location.zone) + modeled_location.zone.air.temperature += sharer_temperature_delta/modeled_location.zone.air.group_multiplier else modeled_location.air.temperature += sharer_temperature_delta diff --git a/code/WorkInProgress/Cael_Aislinn/Tajara/tajara_transformation.dm b/code/WorkInProgress/Cael_Aislinn/Tajara/tajara_transformation.dm index c8906cf31df..bc1c6750dab 100644 --- a/code/WorkInProgress/Cael_Aislinn/Tajara/tajara_transformation.dm +++ b/code/WorkInProgress/Cael_Aislinn/Tajara/tajara_transformation.dm @@ -55,7 +55,7 @@ O.update_body() O.update_face() spawn(1) - O.update_clothing() + O.rebuild_appearance() O << "You are now a Tajara." spawn(0)//To prevent the proc from returning null. del(src) diff --git a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm index 1f80c190dd1..20162ef639d 100644 --- a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm +++ b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dm @@ -22,7 +22,7 @@ if(!lying_icon) lying_icon = new /icon('tajaran.dmi', "body_[g]_l") icon = stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" ..() diff --git a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi index 6e69669647f..14284506856 100644 Binary files a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi and b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran.dmi differ diff --git a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran_face.dmi b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran_face.dmi index 440983184db..c101e668feb 100644 Binary files a/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran_face.dmi and b/code/WorkInProgress/Cael_Aislinn/Tajara/tajaran_face.dmi differ diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm index a3cb01eb7ee..0e022e3c489 100644 --- a/code/ZAS/Airflow.dm +++ b/code/ZAS/Airflow.dm @@ -12,31 +12,16 @@ atom/movable/airflow_speed The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time. -CALLABLE PROCS: - -AirflowRepel(turf/T, n, per) - Causes objects to fly away from a point within a single zone. - Called manually by air releasers. T is the location of the expanding gas. - n is the pressure released. per indicates that n is a percent value if nonzero. - RETURNS: Null - -AirflowAttract(turf/T, n, per) - Causes objects to fly to a point within a single zone. - Called manually by air consumers. T is the location of the attractor. - n is the pressure consumed. per indicates that n is a percent value if nonzero. - RETURNS: Null - - OVERLOADABLE PROCS: mob/airflow_stun() Contains checks for and results of being stunned by airflow. - Called when airflow quantities exceed AF_HUMAN_STUN_THRESHOLD. + Called when airflow quantities exceed airflow_medium_pressure. 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. + n is the pressure that is flowing. RETURNS: 1 if the object moves under the air conditions, 0 if it stays put. atom/movable/airflow_hit(atom/A) @@ -47,10 +32,9 @@ atom/movable/airflow_hit(atom/A) AUTOMATIC PROCS: -Airflow(zone/A, zone/B, n) +Airflow(zone/A, zone/B) Causes objects to fly along a pressure gradient. Called by zone updates. A and B are two connected zones. - n is the pressure difference between them. AirflowSpace(zone/A) Causes objects to fly into space. @@ -64,115 +48,19 @@ atom/movable/RepelAirflowDest(n) */ -var/tick_multiplier = 2 vs_control/var - zone_update_delay = 10 - zone_update_delay_NAME = "Zone Update Delay" - zone_update_delay_DESC = "The delay in ticks between updates of zones. Increase if lag is bad seemingly because of air." + zone_share_percent = 5 - zone_share_percent = 2 - zone_share_percent_NAME = "Zone Connection Transfer %" - zone_share_percent_DESC = "Percent of gas per connected tile that is shared between zones." + airflow_lightest_pressure = 35 + airflow_light_pressure = 50 + airflow_medium_pressure = 80 + airflow_heavy_pressure = 100 + airflow_heaviest_pressure = 150 - //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." - -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",vsc.AF_DENSE_MOVEMENT_THRESHOLD) as num - n = max(1,n) - vsc.AF_DENSE_MOVEMENT_THRESHOLD = n - world.log << "vsc.AF_DENSE_MOVEMENT_THRESHOLD set to [n]." - if("Speed Multiplier") - n = input("What will you change it to","Change Airflow Constants",vsc.AF_SPEED_MULTIPLIER) as num - n = max(1,n) - vsc.AF_SPEED_MULTIPLIER = n - world.log << "vsc.AF_SPEED_MULTIPLIER set to [n]." - if("Damage Multiplier") - n = input("What will you change it to","Change Airflow Constants",vsc.AF_DAMAGE_MULTIPLIER) as num - vsc.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",vsc.AF_STUN_MULTIPLIER) as num - vsc.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",vsc.AF_SPEED_DECAY) as num - vsc.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",vsc.AF_SPEED_DECAY) as num - vsc.AF_SPEED_DECAY = n - world.log << "AF_SPEED_DECAY set to [n]." - - -//The main airflow code. Called by zone updates. -//Zones A and B are air zones. n represents the amount of air moved. + airflow_damage = 0.3 + airflow_stun = 0.15 + airflow_speed_decay = 1 mob/proc/airflow_stun() if(weakened <= 0) src << "\red The sudden rush of air knocks you over!" @@ -197,8 +85,7 @@ atom/movable/proc/check_airflow_movable(n) if(anchored && !ismob(src)) return 0 - if(!istype(src,/obj/item) && n < vsc.AF_DENSE_MOVEMENT_THRESHOLD) return 0 - if(ismob(src) && n < vsc.AF_MOB_MOVEMENT_THRESHOLD) return 0 + if(!istype(src,/obj/item) && n < vsc.airflow_heavy_pressure) return 0 return 1 @@ -213,177 +100,125 @@ obj/item/check_airflow_movable(n) . = ..() switch(w_class) if(2) - if(n < vsc.AF_SMALL_MOVEMENT_THRESHOLD) return 0 + if(n < vsc.airflow_lightest_pressure) return 0 if(3) - if(n < vsc.AF_NORMAL_MOVEMENT_THRESHOLD) return 0 + if(n < vsc.airflow_light_pressure) return 0 if(4,5) - if(n < vsc.AF_LARGE_MOVEMENT_THRESHOLD) return 0 + if(n < vsc.airflow_medium_pressure) return 0 -proc/Airflow(zone/A,zone/B,n) +//The main airflow code. Called by zone updates. +//Zones A and B are air zones. n represents the amount of air moved. - //Now n is a percent of one atm. - n = round((n/vsc.AF_PERCENT_OF)*100,0.1) +proc/Airflow(zone/A,zone/B) + + var/n = B.air.pressure - A.air.pressure //Don't go any further if n is lower than the lowest value needed for airflow. - if(abs(n) < vsc.AF_TINY_MOVEMENT_THRESHOLD) return + if(abs(n) < vsc.airflow_lightest_pressure) return //These turfs are the midway point between A and B, and will be the destination point for thrown objects. var/list/connected_turfs = A.connections[B] //Get lists of things that can be thrown across the room for each zone. - var/list/pplz = A.movables() - var/list/otherpplz = B.movables() + var/list/pplz = A.movable_objects + var/list/otherpplz = B.movable_objects for(var/atom/movable/M in pplz) + if(M.last_airflow > world.time - 150) continue + //Check for knocking people over - if(ismob(M) && n > vsc.AF_HUMAN_STUN_THRESHOLD) + if(ismob(M) && n > vsc.airflow_medium_pressure) 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 + var/list/close_turfs = list() for(var/turf/U in connected_turfs) - if(M in range(U)) fail = 0 - if(fail) continue + if(M in range(U)) close_turfs += U + if(!close_turfs.len) continue //If they're already being tossed, don't do it again. if(!M.airflow_speed) - M.airflow_dest = pick(connected_turfs) //Pick a random midpoint to fly towards. + M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - spawn M.GotoAirflowDest(abs(n) * (vsc.AF_SPEED_MULTIPLIER/10)) - //Send the object flying at a speed determined by n and AF_SPEED_MULTIPLIER. + spawn M.GotoAirflowDest(abs(n)/20) //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) > vsc.AF_HUMAN_STUN_THRESHOLD) + if(M.last_airflow > world.time - 150) continue + + if(ismob(M) && abs(n) > vsc.airflow_medium_pressure) if(M:nodamage) continue M:airflow_stun() if(M.check_airflow_movable(abs(n))) - var/fail = 1 + var/list/close_turfs = list() for(var/turf/U in connected_turfs) - if(M in range(U)) fail = 0 - if(fail) continue + if(M in range(U)) close_turfs += U + if(!close_turfs.len) continue - if(M && !M.airflow_speed) + //If they're already being tossed, don't do it again. + if(!M.airflow_speed) - M.airflow_dest = pick(connected_turfs) + M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. - spawn M.RepelAirflowDest(abs(n) * (vsc.AF_SPEED_MULTIPLIER/10)) + spawn M.RepelAirflowDest(abs(n)/20) proc/AirflowSpace(zone/A) //The space version of the Airflow(A,B,n) proc. - var/n = (A.air.oxygen + A.air.nitrogen + A.air.carbon_dioxide)*vsc.AF_SPACE_MULTIPLIER - //Here, n is determined by the space multiplier constant and the zone's air. + var/n = A.air.pressure + //Here, n is determined by only the pressure in the room. - n = round((n/vsc.AF_PERCENT_OF)*100,0.1) - - if(n < vsc.AF_TINY_MOVEMENT_THRESHOLD) return + if(n < vsc.airflow_lightest_pressure) return var/list/connected_turfs = A.space_tiles //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. + var/list/pplz = A.movable_objects //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 > vsc.AF_HUMAN_STUN_THRESHOLD) + if(M.last_airflow > world.time - 150) continue + + if(ismob(M) && n > vsc.airflow_medium_pressure) if(M:nodamage) continue M:airflow_stun() if(M.check_airflow_movable(n)) - var/fail = 1 + var/list/close_turfs = list() for(var/turf/U in connected_turfs) - if(M in range(U)) fail = 0 - if(fail) continue + if(M in range(U)) close_turfs += U + if(!close_turfs.len) continue + //If they're already being tossed, don't do it again. if(!M.airflow_speed) - M.airflow_dest = pick(connected_turfs) + + M.airflow_dest = pick(close_turfs) //Pick a random midpoint to fly towards. spawn - if(M) M.GotoAirflowDest(n * (vsc.AF_SPEED_MULTIPLIER/10)) + if(M) M.GotoAirflowDest(n/20) //Sometimes shit breaks, and M isn't there after the spawn. -proc/AirflowRepel(turf/T,n,per = 0) - - //This one is used for air escaping from canisters. - var/zone/A = T.zone - if(!A) return - - n *= vsc.AF_CANISTER_MULTIPLIER - - if(!per) - n = round((n/vsc.AF_PERCENT_OF) * 100,0.1) - - if(n < 0) return - if(abs(n) > vsc.AF_TINY_MOVEMENT_THRESHOLD) - - var/list/pplz = A.movables() - - for(var/atom/movable/M in pplz) - var/relative_n = n / max(1,get_dist(T,M)/2) - if(ismob(M) && relative_n > vsc.AF_HUMAN_STUN_THRESHOLD) - if(M:nodamage) continue - M:airflow_stun() - - if(M.check_airflow_movable(relative_n)) - - if(!(M in range(T))) continue //Recall that T is the center of the repelling force. - - if(!M.airflow_speed) - M.airflow_dest = T - spawn M.RepelAirflowDest(relative_n * (vsc.AF_SPEED_MULTIPLIER/10)) - -proc/AirflowAttract(turf/T,n,per=0) - - //Same as above, but attracts objects to the target. - - var/zone/A = T.zone - if(!A) return - - n *= vsc.AF_CANISTER_MULTIPLIER - - if(!per) - n = round((n/vsc.AF_PERCENT_OF) * 100,0.1) - - if(n < 0) return - if(abs(n) > vsc.AF_TINY_MOVEMENT_THRESHOLD) - //world << "Airflow!" - var/list/pplz = A.movables() - for(var/atom/movable/M in pplz) - //world << "[M] / \..." - - var/relative_n = n / max(1,get_dist(T,M)/2) - - if(ismob(M) && relative_n > vsc.AF_HUMAN_STUN_THRESHOLD) - if(M:nodamage) continue - M:airflow_stun() - - if(M.check_airflow_movable(relative_n)) - - if(!(M in range(T))) continue - - if(!M.airflow_speed) - M.airflow_dest = T - spawn M.GotoAirflowDest(relative_n * (vsc.AF_SPEED_MULTIPLIER/10)) - atom/movable var/turf/airflow_dest var/airflow_speed = 0 var/airflow_time = 0 + var/last_airflow = 0 proc/GotoAirflowDest(n) if(!airflow_dest) return if(airflow_speed < 0) return + if(last_airflow > world.time - 150) return if(airflow_speed) airflow_speed = n return + last_airflow = world.time if(airflow_dest == loc) step_away(src,loc) if(ismob(src)) @@ -408,7 +243,7 @@ atom/movable while(airflow_speed > 0) if(airflow_speed <= 0) return airflow_speed = min(airflow_speed,15) - airflow_speed -= vsc.AF_SPEED_DECAY + airflow_speed -= vsc.airflow_speed_decay if(airflow_speed > 7) if(airflow_time++ >= airflow_speed - 7) sleep(1 * tick_multiplier) @@ -421,17 +256,18 @@ atom/movable 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 + airflow_speed = 0 if(od) density = 0 proc/RepelAirflowDest(n) if(!airflow_dest) return if(airflow_speed < 0) return + if(last_airflow > world.time - 150) return if(airflow_speed) airflow_speed = n return + last_airflow = world.time if(airflow_dest == loc) step_away(src,loc) if(ismob(src)) @@ -456,7 +292,7 @@ atom/movable while(airflow_speed > 0) if(airflow_speed <= 0) return airflow_speed = min(airflow_speed,15) - airflow_speed -= vsc.AF_SPEED_DECAY + airflow_speed -= vsc.airflow_speed_decay if(airflow_speed > 7) if(airflow_time++ >= airflow_speed - 7) sleep(1 * tick_multiplier) @@ -469,8 +305,7 @@ atom/movable 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 + airflow_speed = 0 if(od) density = 0 @@ -482,8 +317,7 @@ atom/movable . = ..() atom/movable/proc/airflow_hit(atom/A) - airflow_speed = -1 - spawn(50 * tick_multiplier) airflow_speed = 0 + airflow_speed = 0 airflow_dest = null mob/airflow_hit(atom/A) @@ -499,8 +333,7 @@ obj/airflow_hit(atom/A) . = ..() obj/item/airflow_hit(atom/A) - airflow_speed = -1 - spawn(50 * tick_multiplier) airflow_speed = 0 + airflow_speed = 0 airflow_dest = null mob/living/carbon/human/airflow_hit(atom/A) @@ -512,76 +345,43 @@ mob/living/carbon/human/airflow_hit(atom/A) src.wear_suit.add_blood(src) if (src.w_uniform) src.w_uniform.add_blood(src) - var/b_loss = airflow_speed * vsc.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() + var/b_loss = airflow_speed * vsc.airflow_damage + + var/blocked = run_armor_check("head","melee") + apply_damage(b_loss/3, BRUTE, "head", blocked, 0, "Airflow") + + blocked = run_armor_check("chest","melee") + apply_damage(b_loss/3, BRUTE, "chest", blocked, 0, "Airflow") + + blocked = run_armor_check("groin","melee") + apply_damage(b_loss/3, BRUTE, "groin", blocked, 0, "Airflow") + if(airflow_speed > 10) - paralysis += round(airflow_speed * vsc.AF_STUN_MULTIPLIER) + paralysis += round(airflow_speed * vsc.airflow_stun) stunned = max(stunned,paralysis + 3) else - stunned += round(airflow_speed * vsc.AF_STUN_MULTIPLIER/2) + stunned += round(airflow_speed * vsc.airflow_stun/2) . = ..() -zone/proc/movables() - . = list() - for(var/turf/T in contents) - for(var/atom/A in T) - . += A +turf/simulated/Entered(atom/A) + . = ..() + if(istype(A,/atom/movable) && zone) + zone.movable_objects += 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 +turf/simulated/Exited(atom/A) + . = ..() + if(istype(A,/atom/movable) && zone) + zone.movable_objects -= A +atom/movable/New(turf/simulated/T) + . = ..() + if(istype(T)) + if(T.zone) + T.zone.movable_objects += src - 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> vsc - del F - world.log << "TWEAKS: Airflow, Plasma and Damage settings loaded." \ No newline at end of file +atom/movable/Del() + var/turf/simulated/T = loc + if(istype(T)) + if(T.zone) + T.zone.movable_objects -= src + . = ..() \ No newline at end of file diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm index bf43f90d1c0..af113cc514f 100644 --- a/code/ZAS/Connection.dm +++ b/code/ZAS/Connection.dm @@ -1,3 +1,8 @@ +/* +This object is contained within zone/var/connections. It's generated whenever two turfs from different zones are linked. +Indirect connections will not merge the two zones after they reach equilibrium. +*/ + connection var turf //The turfs involved in the connection. @@ -5,6 +10,7 @@ connection B indirect = 0 //If the connection is purely indirect, the zones should not join. last_updated //The tick at which this was last updated. + no_zone_count = 0 New(turf/T,turf/O) A = T B = O @@ -31,6 +37,8 @@ connection B.zone.connected_zones[A.zone] = 1 else B.zone.connected_zones[A.zone]++ + else + world.log << "Attempted to create connection object for non-zone tiles: [T] -> [O]" Del() if(A.zone && A.zone.connections) A.zone.connections -= src @@ -57,5 +65,14 @@ connection . = ..() proc/Cleanup() - if(A.zone == B.zone) del src - if(!A.zone || !B.zone) del src \ No newline at end of file + if(!A || !B) + world.log << "Connection removed: [A] or [B] missing entirely." + del src + if(A.zone == B.zone) + world.log << "Connection removed: Zones now merged." + del src + if(!A.zone || !B.zone) + no_zone_count++ + if(no_zone_count >= 5) + world.log << "Connection removed: [A] or [B] missing a zone." + del src \ No newline at end of file diff --git a/code/ZAS/Creation.dm b/code/ZAS/Creation.dm index 5f256767fd3..10508e0aa2f 100644 --- a/code/ZAS/Creation.dm +++ b/code/ZAS/Creation.dm @@ -1,13 +1,19 @@ zone New(turf/start) + //Get the turfs that are part of the zone using a floodfill method if(istype(start,/list)) contents = start else contents = FloodFill(start) + + //Change all the zone vars of the turfs, check for space to be added to space_tiles. for(var/turf/T in contents) T.zone = src if(istype(T,/turf/space)) AddSpace(T) + + //Generate the gas_mixture for use in this zone by using the average of the gases + //defined at startup. air = new var/members = contents.len for(var/turf/simulated/T in contents) @@ -17,8 +23,13 @@ zone air.toxins += T.toxins / members air.temperature += T.temperature / members air.group_multiplier = contents.len + air.update_values() + + //Add this zone to the global list. zones += src + Del() + //Ensuring the zone list doesn't get clogged with null values. zones -= src . = ..() @@ -30,17 +41,23 @@ proc/FloodFill(turf/start) while(open.len) for(var/turf/T in open) + //Stop if there's a door, even if it's open. These are handled by indirect connection. if(!T.HasDoor()) + for(var/d in cardinal) var/turf/O = get_step(T,d) + //Simple pass check. if(O.ZCanPass(T) && !(O in open) && !(O in closed)) open += O + open -= T closed += T return closed turf/proc/ZCanPass(turf/T) + //Fairly standard pass checks for turfs, objects and directional windows. Also stops at the edge of space. + if(istype(T,/turf/space)) return 0 else if(T.blocks_air||blocks_air) @@ -51,6 +68,7 @@ turf/proc/ZCanPass(turf/T) continue if(!obstacle.CanPass(0, T, 0, 1)) return 0 + for(var/obj/obstacle in T) if(istype(obstacle,/obj/machinery/door) && !istype(obstacle,/obj/machinery/door/window)) continue diff --git a/code/ZAS/Debug.dm b/code/ZAS/Debug.dm index 28fc3caef53..66c6a12ca33 100644 --- a/code/ZAS/Debug.dm +++ b/code/ZAS/Debug.dm @@ -1,5 +1,5 @@ -client/proc/Zone_Info(turf/T as null|turf) +client/verb/Zone_Info(turf/T as null|turf) set category = "Debug" if(T) if(T.zone) @@ -28,9 +28,10 @@ zone/proc M << "Plasma: [air.toxins]" M << "Carbon Dioxide: [air.carbon_dioxide]" M << "Temperature: [air.temperature]" - M << "Heat Energy: [air.thermal_energy()]" + M << "Heat Energy: [air.temperature * air.heat_capacity()]" M << "Pressure: [air.return_pressure()]" M << "" + M << "Space Tiles: [length(space_tiles)]" M << "Connections: [length(connections)]" for(var/connection/C in connections) @@ -40,6 +41,9 @@ zone/proc spawn(50) C.A.overlays -= 'debug_connect.dmi' C.B.overlays -= 'debug_connect.dmi' + for(var/C in connections) + if(!istype(C,/connection)) + M << "[C] (Not Connection!)" else dbg_output = 0 diff --git a/code/ZAS/Definition.dm b/code/ZAS/Definition.dm index 729d19841fc..cdf411fe68c 100644 --- a/code/ZAS/Definition.dm +++ b/code/ZAS/Definition.dm @@ -1,34 +1,17 @@ turf/var/zone/zone var/list/zones = list() -var/zone_controller/zone_master = new - -zone_controller - var - current_tick = 0 - proc - start() - set background = 1 - while(1) - current_tick++ - for(var/zone/Z in zones) - if(Z.last_update < current_tick) - Z.process() - if(Z) Z.last_update = current_tick - for(var/obj/fire/F) - F.process() - //for(var/obj/z_hotspot/H in z_hotspots) - // H.process() - sleep(max(5,vsc.zone_update_delay*tick_multiplier)) zone var dbg_output = 0 //Enables debug output. - rebuild = 0 //If 1, zone will be rebuilt on next process. + rebuild = 0 //If 1, zone will be rebuilt on next process. Not sure if used. datum/gas_mixture/air //The air contents of the zone. list/contents //All the tiles that are contained in this zone. list/connections // /connection objects which refer to connections with other zones, e.g. through a door. list/connected_zones //Parallels connections, but lists zones to which this one is connected and the number //of points they're connected at. list/space_tiles // Any space tiles in this list will cause air to flow out. - last_update = 0 \ No newline at end of file + last_update = 0 + + list/movable_objects = list() //All the things that can be tossed by airflow. \ No newline at end of file diff --git a/code/ZAS/FEA_gas_mixture.dm b/code/ZAS/FEA_gas_mixture.dm new file mode 100644 index 00000000000..24a6682381b --- /dev/null +++ b/code/ZAS/FEA_gas_mixture.dm @@ -0,0 +1,1008 @@ +/* +What are the archived variables for? + Calculations are done using the archived variables with the results merged into the regular variables. + This prevents race conditions that arise based on the order of tile processing. +*/ + +datum + gas //These are used for the "Trace Gases" stuff, but is buggy. + sleeping_agent + specific_heat = 40 + + volatile_fuel + specific_heat = 30 + + var + moles = 0 + specific_heat = 0 + + moles_archived = 0 + + gas_mixture + var //Holds the "moles" of each of the four gases. + oxygen = 0 + carbon_dioxide = 0 + nitrogen = 0 + toxins = 0 + total_moles = 0 //Updated when a reaction occurs. + pressure = 0 + + volume = CELL_VOLUME + + temperature = 0 //in Kelvin, use calculate_temperature() to modify + + group_multiplier = 1 + //Size of the group this gas_mixture is representing. + //=1 for singletons + + graphic + + list/datum/gas/trace_gases = list() //Seemed to be a good idea that was abandoned + + tmp //These are variables for use with the archived data + oxygen_archived + carbon_dioxide_archived + nitrogen_archived + toxins_archived + + temperature_archived + + 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// +/////////////////////////////// + + + proc/heat_capacity() + //Purpose: Returning the heat capacity of the gas mix + //Called by: UNKNOWN + //Inputs: None + //Outputs: Heat capacity + + var/heat_capacity = HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + heat_capacity += trace_gas.moles*trace_gas.specific_heat + + return max(MINIMUM_HEAT_CAPACITY,heat_capacity) + + proc/heat_capacity_archived() + //Purpose: Returning the archived heat capacity of the gas mix + //Called by: UNKNOWN + //Inputs: None + //Outputs: Archived heat capacity + + var/heat_capacity_archived = HEAT_CAPACITY_CALCULATION(oxygen_archived,carbon_dioxide_archived,nitrogen_archived,toxins_archived) + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + heat_capacity_archived += trace_gas.moles_archived*trace_gas.specific_heat + + return max(MINIMUM_HEAT_CAPACITY,heat_capacity_archived) + + proc/return_pressure() + //Purpose: Calculating Current Pressure + //Called by: + //Inputs: None + //Outputs: Gas pressure. + + if(volume>0) + return total_moles*R_IDEAL_GAS_EQUATION*temperature/volume + return 0 + +// proc/return_temperature() + //Purpose: + //Inputs: + //Outputs: + +// return temperature + +// proc/return_volume() + //Purpose: + //Inputs: + //Outputs: + +// return max(0, volume) + +// proc/thermal_energy() + //Purpose: + //Inputs: + //Outputs: + +// return temperature*heat_capacity() + + proc/update_values() + //Purpose: Calculating and storing values which were normally called CONSTANTLY + //Called by: Anything that changes values within a gas mix. + //Inputs: None + //Outputs: None + + total_moles = oxygen + carbon_dioxide + nitrogen + toxins + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + total_moles += trace_gas.moles + + if(volume) + pressure = total_moles*R_IDEAL_GAS_EQUATION*temperature/volume + else + pressure = 0 + return + +//////////////////////////////////////////// +//Procedures used for very specific events// +//////////////////////////////////////////// + + + proc/check_tile_graphic() + //Purpose: Calculating the graphic for a tile + //Called by: Turfs updating + //Inputs: None + //Outputs: 1 if graphic changed, 0 if unchanged + + graphic = null + if(toxins > MOLES_PLASMA_VISIBLE) + graphic = "plasma" + else if(length(trace_gases)) + var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in trace_gases + if(sleeping_agent && (sleeping_agent.moles > 1)) + graphic = "sleeping_agent" + else + graphic = null + + return graphic != graphic_archived + + proc/react(atom/dump_location) + //Purpose: Calculating if it is possible for a fire to occur in the airmix + //Called by: Air mixes updating? + //Inputs: None + //Outputs: If a fire occured + + var/reacting = 0 //set to 1 if a notable reaction occured (used by pipe_network) + + if(temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST) + if(fire() > 0) + reacting = 1 + + return reacting + + proc/fire() + //Purpose: Calculating any fire reactions. + //Called by: react() (See above) + //Inputs: None + //Outputs: How much fuel burned + + return zburn(null) + + /*var/energy_released = 0 + var/old_heat_capacity = heat_capacity() + + var/datum/gas/volatile_fuel/fuel_store = locate(/datum/gas/volatile_fuel) in trace_gases + if(fuel_store) //General volatile gas burn + var/burned_fuel = 0 + + if(oxygen < fuel_store.moles) + burned_fuel = oxygen + fuel_store.moles -= burned_fuel + oxygen = 0 + else + burned_fuel = fuel_store.moles + oxygen -= fuel_store.moles + del(fuel_store) + + energy_released += FIRE_CARBON_ENERGY_RELEASED * burned_fuel + carbon_dioxide += burned_fuel + fuel_burnt += burned_fuel + + //Handle plasma burning + if(toxins > MINIMUM_HEAT_CAPACITY) + var/plasma_burn_rate = 0 + var/oxygen_burn_rate = 0 + //more plasma released at higher temperatures + var/temperature_scale + if(temperature > PLASMA_UPPER_TEMPERATURE) + temperature_scale = 1 + else + temperature_scale = (temperature-PLASMA_MINIMUM_BURN_TEMPERATURE)/(PLASMA_UPPER_TEMPERATURE-PLASMA_MINIMUM_BURN_TEMPERATURE) + if(temperature_scale > 0) + oxygen_burn_rate = 1.4 - temperature_scale + if(oxygen > toxins*PLASMA_OXYGEN_FULLBURN) + plasma_burn_rate = (toxins*temperature_scale)/4 + else + plasma_burn_rate = (temperature_scale*(oxygen/PLASMA_OXYGEN_FULLBURN))/4 + if(plasma_burn_rate > MINIMUM_HEAT_CAPACITY) + toxins -= plasma_burn_rate + oxygen -= plasma_burn_rate*oxygen_burn_rate + carbon_dioxide += plasma_burn_rate + + energy_released += FIRE_PLASMA_ENERGY_RELEASED * (plasma_burn_rate) + + fuel_burnt += (plasma_burn_rate)*(1+oxygen_burn_rate) + + if(energy_released > 0) + var/new_heat_capacity = heat_capacity() + if(new_heat_capacity > MINIMUM_HEAT_CAPACITY) + temperature = (temperature*old_heat_capacity + energy_released)/new_heat_capacity + update_values() + + return fuel_burnt*/ + +////////////////////////////////////////////// +//Procs for general gas spread calculations.// +////////////////////////////////////////////// + + + proc/archive() + //Purpose: Archives the current gas values + //Called by: UNKNOWN + //Inputs: None + //Outputs: 1 + + oxygen_archived = oxygen + carbon_dioxide_archived = carbon_dioxide + nitrogen_archived = nitrogen + toxins_archived = toxins + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + trace_gas.moles_archived = trace_gas.moles + + temperature_archived = temperature + + graphic_archived = graphic + + return 1 + + proc/check_then_merge(datum/gas_mixture/giver) + //Purpose: Similar to merge(...) but first checks to see if the amount of air assumed is small enough + // that group processing is still accurate for source (aborts if not) + //Called by: airgroups/machinery expelling air, ? + //Inputs: The gas to try and merge + //Outputs: 1 on successful merge. 0 otherwise. + + if(!giver) + return 0 + if(((giver.oxygen > MINIMUM_AIR_TO_SUSPEND) && (giver.oxygen >= oxygen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((giver.carbon_dioxide > MINIMUM_AIR_TO_SUSPEND) && (giver.carbon_dioxide >= carbon_dioxide*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((giver.nitrogen > MINIMUM_AIR_TO_SUSPEND) && (giver.nitrogen >= nitrogen*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((giver.toxins > MINIMUM_AIR_TO_SUSPEND) && (giver.toxins >= toxins*MINIMUM_AIR_RATIO_TO_SUSPEND))) + return 0 + if(abs(giver.temperature - temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) + return 0 + + if(giver.trace_gases.len) + for(var/datum/gas/trace_gas in giver.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + if((trace_gas.moles > MINIMUM_AIR_TO_SUSPEND) && (!corresponding || (trace_gas.moles >= corresponding.moles*MINIMUM_AIR_RATIO_TO_SUSPEND))) + return 0 + + return merge(giver) + + proc/merge(datum/gas_mixture/giver) + //Purpose: Merges all air from giver into self. Deletes giver. + //Called by: Machinery expelling air, check_then_merge, ? + //Inputs: The gas to merge. + //Outputs: 1 + + if(!giver) + return 0 + + if(abs(temperature-giver.temperature)>MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity()*group_multiplier + var/giver_heat_capacity = giver.heat_capacity()*giver.group_multiplier + var/combined_heat_capacity = giver_heat_capacity + self_heat_capacity + if(combined_heat_capacity != 0) + temperature = (giver.temperature*giver_heat_capacity + temperature*self_heat_capacity)/combined_heat_capacity + + if((group_multiplier>1)||(giver.group_multiplier>1)) + oxygen += giver.oxygen*giver.group_multiplier/group_multiplier + carbon_dioxide += giver.carbon_dioxide*giver.group_multiplier/group_multiplier + nitrogen += giver.nitrogen*giver.group_multiplier/group_multiplier + toxins += giver.toxins*giver.group_multiplier/group_multiplier + else + oxygen += giver.oxygen + carbon_dioxide += giver.carbon_dioxide + nitrogen += giver.nitrogen + toxins += giver.toxins + + if(giver.trace_gases.len) + for(var/datum/gas/trace_gas in giver.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + trace_gases += corresponding + corresponding.moles += trace_gas.moles*giver.group_multiplier/group_multiplier + update_values() + + del(giver) + return 1 + + proc/remove(amount) + //Purpose: Removes a certain number of moles from the air. + //Called by: ? + //Inputs: How many moles to remove. + //Outputs: Removed air. + + var/sum = total_moles + amount = min(amount,sum) //Can not take more air than tile has! + if(amount <= 0) + return null + + var/datum/gas_mixture/removed = new + + + removed.oxygen = QUANTIZE((oxygen/sum)*amount) + removed.nitrogen = QUANTIZE((nitrogen/sum)*amount) + removed.carbon_dioxide = QUANTIZE((carbon_dioxide/sum)*amount) + removed.toxins = QUANTIZE((toxins/sum)*amount) + + oxygen -= removed.oxygen/group_multiplier + nitrogen -= removed.nitrogen/group_multiplier + carbon_dioxide -= removed.carbon_dioxide/group_multiplier + toxins -= removed.toxins/group_multiplier + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + var/datum/gas/corresponding = new trace_gas.type() + removed.trace_gases += corresponding + + corresponding.moles = (trace_gas.moles/sum)*amount + trace_gas.moles -= corresponding.moles/group_multiplier + + removed.temperature = temperature + update_values() + removed.update_values() + + return removed + + proc/remove_ratio(ratio) + //Purpose: Removes a certain ratio of the air. + //Called by: ? + //Inputs: Percentage to remove. + //Outputs: Removed air. + + if(ratio <= 0) + return null + + ratio = min(ratio, 1) + + var/datum/gas_mixture/removed = new + + removed.oxygen = QUANTIZE(oxygen*ratio) + removed.nitrogen = QUANTIZE(nitrogen*ratio) + removed.carbon_dioxide = QUANTIZE(carbon_dioxide*ratio) + removed.toxins = QUANTIZE(toxins*ratio) + + oxygen -= removed.oxygen/group_multiplier + nitrogen -= removed.nitrogen/group_multiplier + carbon_dioxide -= removed.carbon_dioxide/group_multiplier + toxins -= removed.toxins/group_multiplier + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + var/datum/gas/corresponding = new trace_gas.type() + removed.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*ratio + trace_gas.moles -= corresponding.moles/group_multiplier + + removed.temperature = temperature + update_values() + removed.update_values() + + return removed + + proc/check_then_remove(amount) + //Purpose: Similar to remove(...) but first checks to see if the amount of air removed is small enough + // that group processing is still accurate for source (aborts if not) + //Called by: ? + //Inputs: Number of moles to remove + //Outputs: Removed air or 0 if it can remove air or not. + + amount = min(amount,total_moles) //Can not take more air than tile has! + + if((amount > MINIMUM_AIR_RATIO_TO_SUSPEND) && (amount > total_moles*MINIMUM_AIR_RATIO_TO_SUSPEND)) + return 0 + + return remove(amount) + + proc/copy_from(datum/gas_mixture/sample) + //Purpose: Duplicates the sample air mixture. + //Called by: airgroups splitting, ? + //Inputs: Gas to copy + //Outputs: 1 + + oxygen = sample.oxygen + carbon_dioxide = sample.carbon_dioxide + nitrogen = sample.nitrogen + toxins = sample.toxins + total_moles = sample.total_moles + + trace_gases.len=null + if(sample.trace_gases.len > 0) + for(var/datum/gas/trace_gas in sample.trace_gases) + var/datum/gas/corresponding = new trace_gas.type() + trace_gases += corresponding + + corresponding.moles = trace_gas.moles + + temperature = sample.temperature + + return 1 + + proc/check_gas_mixture(datum/gas_mixture/sharer) + //Purpose: Telling if one or both airgroups needs to disable group processing. + //Called by: Airgroups sharing air, checking if group processing needs disabled. + //Inputs: Gas to compare from other airgroup + //Outputs: 0 if the self-check failed (local airgroup breaks?) + // then -1 if sharer-check failed (sharing airgroup breaks?) + // then 1 if both checks pass (share succesful?) + if(!istype(sharer)) + return + + var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION + var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION + var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION + var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION + + var/delta_temperature = (temperature_archived - sharer.temperature_archived) + + if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) + return 0 + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) + return 0 + + if(sharer.trace_gases.len) + for(var/datum/gas/trace_gas in sharer.trace_gases) + if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) + var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + if(corresponding) + if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4) + return 0 + else + return 0 + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) + if(!locate(trace_gas.type) in sharer.trace_gases) + return 0 + + if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= sharer.oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= sharer.carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= sharer.nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= sharer.toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) + return -1 + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) + var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases + if(corresponding) + if(trace_gas.moles_archived >= corresponding.moles_archived*MINIMUM_AIR_RATIO_TO_SUSPEND*4) + return -1 + else + return -1 + + return 1 + + proc/check_turf(turf/model) + //Purpose: Used to compare the gases in an unsimulated turf with the gas in a simulated one. + //Called by: Sharing air (mimicing) with adjacent unsimulated turfs + //Inputs: Unsimulated turf + //Outputs: 1 if safe to mimic, 0 if needs to break airgroup. + + 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) + + if(((abs(delta_oxygen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_oxygen) >= oxygen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_carbon_dioxide) >= carbon_dioxide_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_nitrogen) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_nitrogen) >= nitrogen_archived*MINIMUM_AIR_RATIO_TO_SUSPEND)) \ + || ((abs(delta_toxins) > MINIMUM_AIR_TO_SUSPEND) && (abs(delta_toxins) >= toxins_archived*MINIMUM_AIR_RATIO_TO_SUSPEND))) + return 0 + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) + return 0 + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND*4) + return 0 + + return 1 + + proc/share(datum/gas_mixture/sharer) + //Purpose: Used to transfer gas from a more pressurised tile to a less presurised tile + // (Two directional, if the other tile is more pressurised, air travels to current tile) + //Called by: Sharing air with adjacent simulated turfs + //Inputs: Air datum to share with + //Outputs: Amount of gas exchanged (Negative if lost air, positive if gained.) + + + if(!istype(sharer)) + return + + var/delta_oxygen = QUANTIZE(oxygen_archived - sharer.oxygen_archived)/TRANSFER_FRACTION + var/delta_carbon_dioxide = QUANTIZE(carbon_dioxide_archived - sharer.carbon_dioxide_archived)/TRANSFER_FRACTION + var/delta_nitrogen = QUANTIZE(nitrogen_archived - sharer.nitrogen_archived)/TRANSFER_FRACTION + var/delta_toxins = QUANTIZE(toxins_archived - sharer.toxins_archived)/TRANSFER_FRACTION + + var/delta_temperature = (temperature_archived - sharer.temperature_archived) + + var/old_self_heat_capacity = 0 + var/old_sharer_heat_capacity = 0 + + var/heat_self_to_sharer = 0 + var/heat_capacity_self_to_sharer = 0 + var/heat_sharer_to_self = 0 + var/heat_capacity_sharer_to_self = 0 + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + + var/delta_air = delta_oxygen+delta_nitrogen + if(delta_air) + var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air + if(delta_air > 0) + heat_self_to_sharer += air_heat_capacity*temperature_archived + heat_capacity_self_to_sharer += air_heat_capacity + else + heat_sharer_to_self -= air_heat_capacity*sharer.temperature_archived + heat_capacity_sharer_to_self -= air_heat_capacity + + if(delta_carbon_dioxide) + var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide + if(delta_carbon_dioxide > 0) + heat_self_to_sharer += carbon_dioxide_heat_capacity*temperature_archived + heat_capacity_self_to_sharer += carbon_dioxide_heat_capacity + else + heat_sharer_to_self -= carbon_dioxide_heat_capacity*sharer.temperature_archived + heat_capacity_sharer_to_self -= carbon_dioxide_heat_capacity + + if(delta_toxins) + var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins + if(delta_toxins > 0) + heat_self_to_sharer += toxins_heat_capacity*temperature_archived + heat_capacity_self_to_sharer += toxins_heat_capacity + else + heat_sharer_to_self -= toxins_heat_capacity*sharer.temperature_archived + heat_capacity_sharer_to_self -= toxins_heat_capacity + + old_self_heat_capacity = heat_capacity()*group_multiplier + old_sharer_heat_capacity = sharer.heat_capacity()*sharer.group_multiplier + + oxygen -= delta_oxygen/group_multiplier + sharer.oxygen += delta_oxygen/sharer.group_multiplier + + carbon_dioxide -= delta_carbon_dioxide/group_multiplier + sharer.carbon_dioxide += delta_carbon_dioxide/sharer.group_multiplier + + nitrogen -= delta_nitrogen/group_multiplier + sharer.nitrogen += delta_nitrogen/sharer.group_multiplier + + toxins -= delta_toxins/group_multiplier + sharer.toxins += delta_toxins/sharer.group_multiplier + + var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins) + + var/list/trace_types_considered = list() + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + + var/datum/gas/corresponding = locate(trace_gas.type) in sharer.trace_gases + var/delta = 0 + + if(corresponding) + delta = QUANTIZE(trace_gas.moles_archived - corresponding.moles_archived)/TRANSFER_FRACTION + else + corresponding = new trace_gas.type() + sharer.trace_gases += corresponding + + delta = trace_gas.moles_archived/TRANSFER_FRACTION + + trace_gas.moles -= delta/group_multiplier + corresponding.moles += delta/sharer.group_multiplier + + if(delta) + var/individual_heat_capacity = trace_gas.specific_heat*delta + if(delta > 0) + heat_self_to_sharer += individual_heat_capacity*temperature_archived + heat_capacity_self_to_sharer += individual_heat_capacity + else + heat_sharer_to_self -= individual_heat_capacity*sharer.temperature_archived + heat_capacity_sharer_to_self -= individual_heat_capacity + + moved_moles += delta + + trace_types_considered += trace_gas.type + + + if(sharer.trace_gases.len) + for(var/datum/gas/trace_gas in sharer.trace_gases) + if(trace_gas.type in trace_types_considered) continue + else + var/datum/gas/corresponding + var/delta = 0 + + corresponding = new trace_gas.type() + trace_gases += corresponding + + delta = trace_gas.moles_archived/TRANSFER_FRACTION + + trace_gas.moles -= delta/sharer.group_multiplier + corresponding.moles += delta/group_multiplier + + //Guaranteed transfer from sharer to self + var/individual_heat_capacity = trace_gas.specific_heat*delta + heat_sharer_to_self += individual_heat_capacity*sharer.temperature_archived + heat_capacity_sharer_to_self += individual_heat_capacity + + moved_moles += -delta + update_values() + sharer.update_values() + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/new_self_heat_capacity = old_self_heat_capacity + heat_capacity_sharer_to_self - heat_capacity_self_to_sharer + var/new_sharer_heat_capacity = old_sharer_heat_capacity + heat_capacity_self_to_sharer - heat_capacity_sharer_to_self + + if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY) + temperature = (old_self_heat_capacity*temperature - heat_capacity_self_to_sharer*temperature_archived + heat_capacity_sharer_to_self*sharer.temperature_archived)/new_self_heat_capacity + + if(new_sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) + sharer.temperature = (old_sharer_heat_capacity*sharer.temperature-heat_capacity_sharer_to_self*sharer.temperature_archived + heat_capacity_self_to_sharer*temperature_archived)/new_sharer_heat_capacity + + if(abs(old_sharer_heat_capacity) > MINIMUM_HEAT_CAPACITY) + if(abs(new_sharer_heat_capacity/old_sharer_heat_capacity - 1) < 0.10) // <10% change in sharer heat capacity + temperature_share(sharer, OPEN_HEAT_TRANSFER_COEFFICIENT) + + if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) + var/delta_pressure = temperature_archived*(total_moles + moved_moles) - sharer.temperature_archived*(sharer.total_moles - moved_moles) + return delta_pressure*R_IDEAL_GAS_EQUATION/volume + + else + return 0 + + proc/mimic(turf/model, border_multiplier) + //Purpose: Used transfer gas from a more pressurised tile to a less presurised unsimulated tile. + //Called by: "sharing" from unsimulated to simulated turfs. + //Inputs: Unsimulated turf, Multiplier for gas transfer (optional) + //Outputs: Amount of gas exchanged + + 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) + + var/heat_transferred = 0 + var/old_self_heat_capacity = 0 + var/heat_capacity_transferred = 0 + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + + var/delta_air = delta_oxygen+delta_nitrogen + if(delta_air) + var/air_heat_capacity = SPECIFIC_HEAT_AIR*delta_air + heat_transferred -= air_heat_capacity*model.temperature + heat_capacity_transferred -= air_heat_capacity + + if(delta_carbon_dioxide) + var/carbon_dioxide_heat_capacity = SPECIFIC_HEAT_CDO*delta_carbon_dioxide + heat_transferred -= carbon_dioxide_heat_capacity*model.temperature + heat_capacity_transferred -= carbon_dioxide_heat_capacity + + if(delta_toxins) + var/toxins_heat_capacity = SPECIFIC_HEAT_TOXIN*delta_toxins + heat_transferred -= toxins_heat_capacity*model.temperature + heat_capacity_transferred -= toxins_heat_capacity + + old_self_heat_capacity = heat_capacity()*group_multiplier + + if(border_multiplier) + oxygen -= delta_oxygen*border_multiplier/group_multiplier + carbon_dioxide -= delta_carbon_dioxide*border_multiplier/group_multiplier + nitrogen -= delta_nitrogen*border_multiplier/group_multiplier + toxins -= delta_toxins*border_multiplier/group_multiplier + else + oxygen -= delta_oxygen/group_multiplier + carbon_dioxide -= delta_carbon_dioxide/group_multiplier + nitrogen -= delta_nitrogen/group_multiplier + toxins -= delta_toxins/group_multiplier + + var/moved_moles = (delta_oxygen + delta_carbon_dioxide + delta_nitrogen + delta_toxins) + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + var/delta = 0 + + delta = trace_gas.moles_archived/TRANSFER_FRACTION + + if(border_multiplier) + trace_gas.moles -= delta*border_multiplier/group_multiplier + else + trace_gas.moles -= delta/group_multiplier + + var/heat_cap_transferred = delta*trace_gas.specific_heat + heat_transferred += heat_cap_transferred*temperature_archived + heat_capacity_transferred += heat_cap_transferred + moved_moles += delta + update_values() + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/new_self_heat_capacity = old_self_heat_capacity - heat_capacity_transferred + if(new_self_heat_capacity > MINIMUM_HEAT_CAPACITY) + if(border_multiplier) + temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity + else + temperature = (old_self_heat_capacity*temperature - heat_capacity_transferred*border_multiplier*temperature_archived)/new_self_heat_capacity + + temperature_mimic(model, model.thermal_conductivity, border_multiplier) + + if((delta_temperature > MINIMUM_TEMPERATURE_TO_MOVE) || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE) + var/delta_pressure = temperature_archived*(total_moles + moved_moles) - model.temperature*(model.oxygen+model.carbon_dioxide+model.nitrogen+model.toxins) + return delta_pressure*R_IDEAL_GAS_EQUATION/volume + else + return 0 + + proc/check_both_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient) + var/delta_temperature = (temperature_archived - sharer.temperature_archived) + + var/self_heat_capacity = heat_capacity_archived() + var/sharer_heat_capacity = sharer.heat_capacity_archived() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) + sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier) + else + return 1 + + if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ + && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) + return 0 + + if((abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ + && (abs(sharer_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*sharer.temperature_archived)) + return -1 + + temperature += self_temperature_delta + sharer.temperature += sharer_temperature_delta + + return 1 + //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency + + proc/check_me_then_temperature_share(datum/gas_mixture/sharer, conduction_coefficient) + var/delta_temperature = (temperature_archived - sharer.temperature_archived) + + var/self_heat_capacity = heat_capacity_archived() + var/sharer_heat_capacity = sharer.heat_capacity_archived() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) + sharer_temperature_delta = heat/(sharer_heat_capacity*sharer.group_multiplier) + else + return 1 + + if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ + && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) + return 0 + + temperature += self_temperature_delta + sharer.temperature += sharer_temperature_delta + + return 1 + //Logic integrated from: temperature_share(sharer, conduction_coefficient) for efficiency + + proc/check_me_then_temperature_turf_share(turf/simulated/sharer, conduction_coefficient) + var/delta_temperature = (temperature_archived - sharer.temperature) + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity_archived() + + if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity)) + + self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) + sharer_temperature_delta = heat/sharer.heat_capacity + else + return 1 + + if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ + && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) + return 0 + + temperature += self_temperature_delta + sharer.temperature += sharer_temperature_delta + + return 1 + //Logic integrated from: temperature_turf_share(sharer, conduction_coefficient) for efficiency + + proc/check_me_then_temperature_mimic(turf/model, conduction_coefficient) + var/delta_temperature = (temperature_archived - model.temperature) + var/self_temperature_delta = 0 + + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity_archived() + + if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity)) + + self_temperature_delta = -heat/(self_heat_capacity*group_multiplier) + + if((abs(self_temperature_delta) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) \ + && (abs(self_temperature_delta) > MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND*temperature_archived)) + return 0 + + temperature += self_temperature_delta + + return 1 + //Logic integrated from: temperature_mimic(model, conduction_coefficient) for efficiency + + proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient) + + var/delta_temperature = (temperature_archived - sharer.temperature_archived) + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity_archived() + var/sharer_heat_capacity = sharer.heat_capacity_archived() + if(!group_multiplier) + message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!") + return + + if((sharer_heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*sharer_heat_capacity/(self_heat_capacity+sharer_heat_capacity)) + + temperature -= heat/(self_heat_capacity*group_multiplier) + sharer.temperature += heat/(sharer_heat_capacity*sharer.group_multiplier) + + proc/temperature_mimic(turf/model, conduction_coefficient, border_multiplier) + var/delta_temperature = (temperature - model.temperature) + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity()//_archived() + if(!group_multiplier) + message_admins("Error! The gas mixture (ref \ref[src]) has no group multiplier!") + return + + if((model.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*model.heat_capacity/(self_heat_capacity+model.heat_capacity)) + + if(border_multiplier) + temperature -= heat*border_multiplier/(self_heat_capacity*group_multiplier) + else + temperature -= heat/(self_heat_capacity*group_multiplier) + + proc/temperature_turf_share(turf/simulated/sharer, conduction_coefficient) + var/delta_temperature = (temperature_archived - sharer.temperature) + if(abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER) + var/self_heat_capacity = heat_capacity() + + if((sharer.heat_capacity > MINIMUM_HEAT_CAPACITY) && (self_heat_capacity > MINIMUM_HEAT_CAPACITY)) + var/heat = conduction_coefficient*delta_temperature* \ + (self_heat_capacity*sharer.heat_capacity/(self_heat_capacity+sharer.heat_capacity)) + + temperature -= heat/(self_heat_capacity*group_multiplier) + sharer.temperature += heat/sharer.heat_capacity + + proc/compare(datum/gas_mixture/sample) + //Purpose: Compares sample to self to see if within acceptable ranges that group processing may be enabled + //Called by: Airgroups trying to rebuild + //Inputs: Gas mix to compare + //Outputs: 1 if can rebuild, 0 if not. + if(!sample) return 0 + + if((abs(oxygen-sample.oxygen) > MINIMUM_AIR_TO_SUSPEND) && \ + ((oxygen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.oxygen))) + return 0 + if((abs(nitrogen-sample.nitrogen) > MINIMUM_AIR_TO_SUSPEND) && \ + ((nitrogen < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen) || (nitrogen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.nitrogen))) + return 0 + if((abs(carbon_dioxide-sample.carbon_dioxide) > MINIMUM_AIR_TO_SUSPEND) && \ + ((carbon_dioxide < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide) || (oxygen > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.carbon_dioxide))) + return 0 + if((abs(toxins-sample.toxins) > MINIMUM_AIR_TO_SUSPEND) && \ + ((toxins < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins) || (toxins > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*sample.toxins))) + return 0 + + if(total_moles > MINIMUM_AIR_TO_SUSPEND) + if((abs(temperature-sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND) && \ + ((temperature < (1-MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature) || (temperature > (1+MINIMUM_TEMPERATURE_RATIO_TO_SUSPEND)*sample.temperature))) + //world << "temp fail [temperature] & [sample.temperature]" + return 0 + + if(sample.trace_gases.len) + for(var/datum/gas/trace_gas in sample.trace_gases) + if(trace_gas.moles_archived > MINIMUM_AIR_TO_SUSPEND) + var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + if(corresponding) + if((abs(trace_gas.moles - corresponding.moles) > MINIMUM_AIR_TO_SUSPEND) && \ + ((corresponding.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles) || (corresponding.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*trace_gas.moles))) + return 0 + else + return 0 + + if(trace_gases.len) + for(var/datum/gas/trace_gas in trace_gases) + if(trace_gas.moles > MINIMUM_AIR_TO_SUSPEND) + var/datum/gas/corresponding = locate(trace_gas.type) in sample.trace_gases + if(corresponding) + if((abs(trace_gas.moles - corresponding.moles) > MINIMUM_AIR_TO_SUSPEND) && \ + ((trace_gas.moles < (1-MINIMUM_AIR_RATIO_TO_SUSPEND)*corresponding.moles) || (trace_gas.moles > (1+MINIMUM_AIR_RATIO_TO_SUSPEND)*corresponding.moles))) + return 0 + else + return 0 + return 1 + + + proc/subtract(datum/gas_mixture/right_side) + //Purpose: Subtracts right_side from air_mixture. Used to help turfs mingle + //Called by: Pipelines ending in a break (or something) + //Inputs: Gas mix to remove + //Outputs: 1 + + oxygen -= right_side.oxygen + carbon_dioxide -= right_side.carbon_dioxide + nitrogen -= right_side.nitrogen + toxins -= right_side.toxins + + if((trace_gases.len > 0)||(right_side.trace_gases.len > 0)) + for(var/datum/gas/trace_gas in right_side.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + trace_gases += corresponding + + corresponding.moles -= trace_gas.moles + update_values() + return 1 \ No newline at end of file diff --git a/code/ZAS/FEA_system.dm b/code/ZAS/FEA_system.dm new file mode 100644 index 00000000000..dd2fca6ef21 --- /dev/null +++ b/code/ZAS/FEA_system.dm @@ -0,0 +1,178 @@ +/* +Overview: + The air_master global variable is the workhorse for the system. + +Why are you archiving data before modifying it? + The general concept with archiving data and having each tile keep track of when they were last updated is to keep everything symmetric + and totally independent of the order they are read in an update cycle. + This prevents abnormalities like air/fire spreading rapidly in one direction and super slowly in the other. + +Why not just archive everything and then calculate? + Efficiency. While a for-loop that goes through all tils and groups to archive their information before doing any calculations seems simple, it is + slightly less efficient than the archive-before-modify/read method. + +Why is there a cycle check for calculating data as well? + This ensures that every connection between group-tile, tile-tile, and group-group is only evaluated once per loop. + + + + +Important variables: + air_master.groups_to_rebuild (list) + A list of air groups that have had their geometry occluded and thus may need to be split in half. + A set of adjacent groups put in here will join together if validly connected. + This is done before air system calculations for a cycle. + air_master.tiles_to_update (list) + Turfs that are in this list have their border data updated before the next air calculations for a cycle. + Place turfs in this list rather than call the proc directly to prevent race conditions + + turf/simulated.archive() and datum/air_group.archive() + This stores all data for. + If you modify, make sure to update the archived_cycle to prevent race conditions and maintain symmetry + + atom/CanPass(atom/movable/mover, turf/target, height, air_group) + returns 1 for allow pass and 0 for deny pass + Turfs automatically call this for all objects/mobs in its turf. + This is called both as source.CanPass(target, height, air_group) + and target.CanPass(source, height, air_group) + + Cases for the parameters + 1. This is called with args (mover, location, height>0, air_group=0) for normal objects. + 2. This is called with args (null, location, height=0, air_group=0) for flowing air. + 3. This is called with args (null, location, height=?, air_group=1) for determining group boundaries. + + Cases 2 and 3 would be different for doors or other objects open and close fairly often. + (Case 3 would return 0 always while Case 2 would return 0 only when the door is open) + This prevents the necessity of re-evaluating group geometry every time a door opens/closes. + + +Important Procedures + air_master.process() + This first processes the air_master update/rebuild lists then processes all groups and tiles for air calculations + + +*/ + +var/kill_air = 0 +var/tick_multiplier = 2 + +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 + CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0) + if(!target) return 0 + + if(istype(mover)) // turf/Enter(...) will perform more advanced checks + return !density + + else // Now, doing more detailed checks for air movement and air group formation + if(target.blocks_air||blocks_air) + return 0 + + for(var/obj/obstacle in src) + if(!obstacle.CanPass(mover, target, height, air_group)) + return 0 + for(var/obj/obstacle in target) + if(!obstacle.CanPass(mover, src, height, air_group)) + return 0 + + return 1 + + +var/global/datum/controller/air_system/air_master + +datum + controller + air_system + //Geoemetry lists + var/list/turf/simulated/active_singletons = list() + + //Special functions lists + var/list/turf/simulated/active_super_conductivity = list() + var/list/turf/simulated/high_pressure_delta = list() + + //Geometry updates lists + var/list/turf/simulated/tiles_to_update = list() + 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. + var/failed_ticks = 0 //How many ticks have runtimed? + + +/* process() + //Call this to process air movements for a cycle + + process_rebuild_select_groups() + //Used by process() + //Warning: Do not call this + + 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 + */ + + + 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) + + var/start_time = world.timeofday + + for(var/turf/simulated/S in world) + if(S.z < 5) + if(!S.blocks_air && !S.zone && S.z < 5) // Added last check to force skipping asteroid z-levels -- TLE + new/zone(S) + + S.update_air_properties() + + world << "\red \b Geometry processed in [time2text(world.timeofday-start_time, "mm:ss")] minutes!" + spawn start() + + 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. + + set background = 1 + while(1) + if(!kill_air) + current_cycle++ + var/success = tick() //Changed so that a runtime does not crash the ticker. + if(!success) //Runtimed. + failed_ticks++ + if(failed_ticks > 20) + world << "ERROR IN ATMOS TICKER. Killing air simulation!" + kill_air = 1 + sleep(max(5,update_delay*tick_multiplier)) + + proc/tick() + + 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/zone/Z in zones) + if(Z.last_update < current_cycle) + Z.process() + if(Z) Z.last_update = current_cycle + + for(var/obj/fire/F) + F.process() + + return 1 \ No newline at end of file diff --git a/code/ZAS/FEA_turf_tile.dm b/code/ZAS/FEA_turf_tile.dm new file mode 100644 index 00000000000..c8dda862530 --- /dev/null +++ b/code/ZAS/FEA_turf_tile.dm @@ -0,0 +1,205 @@ +atom/var/pressure_resistance = ONE_ATMOSPHERE +turf + assume_air(datum/gas_mixture/giver) //use this for machines to adjust air + del(giver) + return 0 + + return_air() + //Create gas mixture to hold data for passing + var/datum/gas_mixture/GM = new + + GM.oxygen = oxygen + GM.carbon_dioxide = carbon_dioxide + GM.nitrogen = nitrogen + GM.toxins = toxins + + GM.temperature = temperature + GM.update_values() + + return GM + + remove_air(amount as num) + var/datum/gas_mixture/GM = new + + var/sum = oxygen + carbon_dioxide + nitrogen + toxins + if(sum>0) + GM.oxygen = (oxygen/sum)*amount + GM.carbon_dioxide = (carbon_dioxide/sum)*amount + GM.nitrogen = (nitrogen/sum)*amount + GM.toxins = (toxins/sum)*amount + + GM.temperature = temperature + GM.update_values() + + return GM + + +turf + simulated + + var/current_graphic = null + + var/tmp + datum/gas_mixture/air + + processing = 1 + group_border = 0 + length_space_border = 0 + + air_check_directions = 0 //Do not modify this, just add turf to air_master.tiles_to_update + + archived_cycle = 0 + current_cycle = 0 + + obj/fire/active_hotspot + + temperature_archived //USED ONLY FOR SOLIDS + being_superconductive = 0 + + + proc + process_cell() + update_air_properties() + archive() + + mimic_air_with_tile(turf/model) + share_air_with_tile(turf/simulated/sharer) + + mimic_temperature_with_tile(turf/model) + share_temperature_with_tile(turf/simulated/sharer) + + + super_conduct() + + update_visuals() + overlays = null + + var/siding_icon_state = return_siding_icon_state() + if(siding_icon_state) + overlays += image('floors.dmi',siding_icon_state) + var/datum/gas_mixture/model = return_air() + switch(model.graphic) + if("plasma") + overlays.Add(plmaster) + if("sleeping_agent") + overlays.Add(slmaster) + + + + New() + ..() + + if(!blocks_air) + air = new + + air.oxygen = oxygen + air.carbon_dioxide = carbon_dioxide + air.nitrogen = nitrogen + air.toxins = toxins + + air.temperature = temperature + air.update_values() + + if(air_master) + air_master.tiles_to_update.Add(src) + + find_zone() + +// air.parent = src //TODO DEBUG REMOVE + + else + if(air_master) + for(var/direction in cardinal) + var/turf/simulated/floor/target = get_step(src,direction) + if(istype(target)) + air_master.tiles_to_update.Add(target) + + Del() + if(active_hotspot) + del(active_hotspot) + if(blocks_air) + for(var/direction in list(NORTH, SOUTH, EAST, WEST)) + var/turf/simulated/tile = get_step(src,direction) + if(istype(tile) && !tile.blocks_air) + air_master.tiles_to_update.Add(tile) + ..() + + assume_air(datum/gas_mixture/giver) + if(!giver) return 0 + if(zone) + zone.air.merge(giver) + return 1 + else + return ..() + + archive() + if(air) //For open space like floors + air.archive() + + temperature_archived = temperature + archived_cycle = air_master.current_cycle + + share_air_with_tile(turf/simulated/T) + return air.share(T.air) + + mimic_air_with_tile(turf/T) + return air.mimic(T) + + return_air() + if(zone) + return zone.air + else if(air) + return air + + else + return ..() + + remove_air(amount as num) + if(zone) + var/datum/gas_mixture/removed = null + removed = zone.air.remove(amount) + return removed + else if(air) + var/datum/gas_mixture/removed = null + removed = air.remove(amount) + + if(air.check_tile_graphic()) + update_visuals(air) + return removed + + else + return ..() + + update_air_properties()//OPTIMIZE + air_check_directions = 0 + + for(var/direction in cardinal) + if(CanPass(null, get_step(src,direction), 0, 0)) + air_check_directions |= direction + + if(zone) + for(var/direction in cardinal) + if(air_check_directions&direction) + + var/turf/simulated/T = get_step(src,direction) + if(T) + ZConnect(src,T) + else + var/turf/simulated/T = get_step(src,direction) + if(T) + ZDisconnect(src,T) + else + + for(var/direction in cardinal) + if(air_check_directions&direction) + var/turf/simulated/T = get_step(src,direction) + if(T.zone) + T.zone.AddTurf(src) + break + if(!zone) + new/zone(src) + + if(air_check_directions) + processing = 1 + else + processing = 0 \ No newline at end of file diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm index f265c45877a..a45b7fa3b3b 100644 --- a/code/ZAS/Fire.dm +++ b/code/ZAS/Fire.dm @@ -1,13 +1,16 @@ vs_control/var/IgnitionLevel = 10 //Moles of oxygen+plasma - co2 needed to burn. -#define OXYGEN +//Some legacy definitions so fires can be started. atom/proc/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) return null + turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0) + + turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) - if(fire_protection) return + if(fire_protection > world.time-300) return var/datum/gas_mixture/air_contents = return_air(1) if(!air_contents) return 0 @@ -25,19 +28,11 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) return 1 var/datum/gas/volatile_fuel/fuel = locate() in air_contents.trace_gases var/obj/liquid_fuel/liquid = locate() in src - var/fuel_level = 0 - var/liquid_level = 0 - if(fuel) fuel_level = fuel.moles - if(liquid) liquid_level = liquid.amount - var/total_fuel = air_contents.toxins + fuel_level + liquid_level - if((air_contents.oxygen + air_contents.toxins + fuel_level*1.5 + liquid_level*1.5) - (air_contents.carbon_dioxide*0.25) > vsc.IgnitionLevel && total_fuel > 0.5) + if(air_contents.calculate_firelevel(liquid) > vsc.IgnitionLevel && (fuel || liquid || air_contents.toxins > 0.5)) igniting = 1 if(air_contents.oxygen < 0.5) return 0 - if(parent&&parent.group_processing) - parent.suspend_group_processing() - if(! (locate(/obj/fire) in src)) var/obj/fire/F = new(src,1000) F.temperature = exposed_temperature @@ -48,112 +43,9 @@ turf/simulated/hotspot_expose(exposed_temperature, exposed_volume, soh) return igniting -obj/effect/hotspot - //Icon for fire on turfs, also helps for nurturing small fires until they are full tile - - anchored = 1 - - mouse_opacity = 0 - - //luminosity = 3 - - icon = 'fire.dmi' - icon_state = "1" - - layer = TURF_LAYER - - var - volume = 125 - temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - - just_spawned = 1 - - bypassing = 0 - -obj/effect/hotspot/proc/perform_exposure() - var/turf/simulated/floor/location = loc - if(!istype(location)) - return 0 - - if(volume > CELL_VOLUME*0.95) - bypassing = 1 - else bypassing = 0 - - if(bypassing) - if(!just_spawned) - volume = location.air.fuel_burnt*FIRE_GROWTH_RATE - temperature = location.air.temperature - else - var/datum/gas_mixture/affected = location.air.remove_ratio(volume/location.air.volume) - - affected.temperature = temperature - - affected.react() - - temperature = affected.temperature - volume = affected.fuel_burnt*FIRE_GROWTH_RATE - - location.assume_air(affected) - - for(var/atom/item in loc) - item.temperature_expose(null, temperature, volume) - -obj/effect/hotspot/process(turf/simulated/list/possible_spread) - if(just_spawned) - just_spawned = 0 - return 0 - - var/turf/simulated/floor/location = loc - if(!istype(location)) - del(src) - - if((temperature < FIRE_MINIMUM_TEMPERATURE_TO_EXIST) || (volume <= 1)) - del(src) - - if(location.air.toxins < 0.5 || location.air.oxygen < 0.5) - del(src) - - - perform_exposure() - - if(location.wet) location.wet = 0 - - if(bypassing) - icon_state = "3" - location.burn_tile() - - //Possible spread due to radiated heat - if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD) - var/radiated_temperature = location.air.temperature*FIRE_SPREAD_RADIOSITY_SCALE - - for(var/turf/simulated/possible_target in possible_spread) - if(!locate(/obj/effect/hotspot) in possible_target) - possible_target.hotspot_expose(radiated_temperature, CELL_VOLUME/4) - - else - if(volume > CELL_VOLUME*0.4) - icon_state = "2" - else - icon_state = "1" - - return 1 - -obj/effect/hotspot/New() - ..() - dir = pick(cardinal) - sd_SetLuminosity(3) - -obj/effect/hotspot/Del() - src.sd_SetLuminosity(0) - loc = null - ..() - -var - fire_ratio_1 = 0.05 - obj fire - //Icon for fire on turfs, also helps for nurturing small fires until they are full tile + //Icon for fire on turfs. anchored = 1 mouse_opacity = 0 @@ -168,84 +60,82 @@ obj var volume = CELL_VOLUME temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - firelevel = 10000 + firelevel = 10000 //Calculated by gas_mixture.calculate_firelevel() archived_firelevel = 0 process() + if(firelevel > vsc.IgnitionLevel) + var/turf/simulated/floor/S = loc - if(!S.zone) del src - //src.temperature += (src.firelevel/FireTempDivider+FireOffset - src.temperature) / FireRate + if(!S.zone) del src //Cannot exist where zones are broken. + if(istype(S,/turf/simulated/floor)) var datum/gas_mixture/air_contents = S.return_air() + //Get whatever trace fuels are in the area datum/gas/volatile_fuel/fuel = locate(/datum/gas/volatile_fuel/) in air_contents.trace_gases - fuel_level = 0 + //Also get liquid fuels on the ground. obj/liquid_fuel/liquid = locate() in S - liquid_level = 0 - if(fuel) fuel_level = fuel.moles - if(liquid) - liquid_level = liquid.amount - if(liquid.amount <= 0) - del liquid - liquid_level = 0 + firelevel = air_contents.calculate_firelevel(liquid) - firelevel = (air_contents.oxygen + air_contents.toxins + fuel_level*1.5 + liquid_level*1.5) - (air_contents.carbon_dioxide*0.25) + //Ensure that there is an appropriate amount of fuel and O2 here. + if(firelevel > 25 && (air_contents.toxins || fuel || liquid)) - firelevel = min(firelevel,vsc.IgnitionLevel*5) - - if(firelevel > vsc.IgnitionLevel * 1.5 && (air_contents.toxins || fuel_level || liquid_level)) for(var/direction in cardinal) if(S.air_check_directions&direction) //Grab all valid bordering tiles - var/turf/simulated/enemy_tile = get_step(S, direction) - if(istype(enemy_tile)) - if(enemy_tile.fire_protection) - firelevel -= vsc.IgnitionLevel - continue - if(!(locate(/obj/fire) in enemy_tile)) - if( prob( firelevel/(vsc.IgnitionLevel*0.1) ) ) - new/obj/fire(enemy_tile,firelevel) - // else - // world << "Spread Probability: [firelevel/(vsc.IgnitionLevel*0.1)]%." - // else - // world << "There's a fire there bitch." - // else - // world << "[enemy_tile] cannot be spread to." - //else - // world << "Not enough firelevel to spread: [firelevel]/[vsc.IgnitionLevel*1.5]" - var/datum/gas_mixture/flow = air_contents.remove_ratio(0.5) - //n = PV/RT, taking the volume of a single tile from the gas. + var/turf/simulated/enemy_tile = get_step(S, direction) + + if(istype(enemy_tile)) + //If extinguisher mist passed over the turf it's trying to spread to, don't spread and + //reduce firelevel. + if(enemy_tile.fire_protection > world.time-30) + firelevel -= 150 + continue + + //Spread the fire. + if(!(locate(/obj/fire) in enemy_tile)) + if( prob( firelevel/2.5 ) ) + new/obj/fire(enemy_tile,firelevel) + + var/datum/gas_mixture/flow = air_contents.remove_ratio(0.5) //Take half the air from the room I guess. if(flow) - if(flow.oxygen > 0.3 && (flow.toxins || fuel_level || liquid)) + //Ensure adequate oxygen and fuel. + if(flow.oxygen > 0.3 && (flow.toxins || fuel || liquid)) + //Change icon depending on the fuel, and thus temperature. icon_state = "1" - if(firelevel > vsc.IgnitionLevel * 2) + if(firelevel > 25) icon_state = "2" - if(firelevel > vsc.IgnitionLevel * 3.5) + if(firelevel > 100) icon_state = "3" + + //Ensure flow temperature is higher than minimum fire temperatures. flow.temperature = max(PLASMA_MINIMUM_BURN_TEMPERATURE+0.1,flow.temperature) + + //Burn the gas mixture. flow.zburn(liquid) else + del src - S.assume_air(flow) + S.assume_air(flow) //Then put it back where you found it. + else - //world << "No air at all." del src else del src else - //world << "Insufficient fire level for ignition: [firelevel]/[IgnitionLevel]" del src for(var/mob/living/carbon/human/M in loc) - M.FireBurn(firelevel/(vsc.IgnitionLevel*10)) + M.FireBurn(min(max(0.1,firelevel / 20),10)) //Burn the humans! New(newLoc,fl) @@ -254,7 +144,7 @@ obj sd_SetLuminosity(3) firelevel = fl for(var/mob/living/carbon/human/M in loc) - M.FireBurn(firelevel/(vsc.IgnitionLevel*10)) + M.FireBurn(min(max(0.1,firelevel / 20),10)) //Burn the humans! Del() if (istype(loc, /turf/simulated)) @@ -265,74 +155,161 @@ obj ..() obj/liquid_fuel - icon = 'effects.dmi' - icon_state = "slube" + //Liquid fuel is used for things that used to rely on volatile fuels or plasma being contained to a couple tiles. + icon = 'icons/effects/effects.dmi' + icon_state = "fuel" layer = TURF_LAYER+0.2 anchored = 1 - var/amount = 1 + var/amount = 1 //Basically moles. - New(newLoc) + New(newLoc,amt=1) + src.amount = amt + + //Be absorbed by any other liquid fuel in the tile. for(var/obj/liquid_fuel/other in newLoc) if(other != src) other.amount += src.amount + spawn other.Spread() del src + return + + Spread() . = ..() -vs_control/var/switch_fire = 1 + proc/Spread() + //Allows liquid fuels to sometimes flow into other tiles. + if(amount < 0.5) return + var/turf/simulated/S = loc + if(!istype(S)) return + for(var/d in cardinal) + if(S.air_check_directions & d) + if(rand(25)) + var/turf/simulated/O = get_step(src,d) + new/obj/liquid_fuel(O,amount*0.25) + amount *= 0.75 -turf/simulated/var/fire_protection = 0 + flamethrower_fuel + icon_state = "mustard" + anchored = 0 + New(newLoc, amt = 1, d = 0) + dir = d //Setting this direction means you won't get torched by your own flamethrower. + . = ..() + Spread() + //The spread for flamethrower fuel is much more precise, to create a wide fire pattern. + if(amount < 0.1) return + var/turf/simulated/S = loc + if(!istype(S)) return + + for(var/d in list(turn(dir,90),turn(dir,-90))) + if(S.air_check_directions & d) + var/turf/simulated/O = get_step(S,d) + new/obj/liquid_fuel/flamethrower_fuel(O,amount*0.25,d) + O.hotspot_expose((T20C*2) + 380,500) //Light flamethrower fuel on fire immediately. + + amount *= 0.5 + + +turf/simulated/var/fire_protection = 0 //Protects newly extinguished tiles from being overrun again. +turf/simulated/proc/apply_fire_protection() + fire_protection = world.time datum/gas_mixture/proc/zburn(obj/liquid_fuel/liquid) - if(vsc.switch_fire) - . = fire() - if(liquid && liquid.amount > 0) - oxygen -= fire_ratio_1 - liquid.amount = max(liquid.amount-fire_ratio_1,0) - carbon_dioxide += fire_ratio_1 - if(liquid.amount <= 0) - del liquid - return + //This proc is similar to fire(), but uses a simple logarithm to calculate temp, and is thus more stable with ZAS. if(temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) var - fuel_level = 0 + total_fuel = toxins + fuel_sources = 0 //We'll divide by this later so that fuel is consumed evenly. datum/gas/volatile_fuel/fuel = locate() in trace_gases - liquid_level = 0 - if(fuel) fuel_level = fuel.moles - if(liquid) liquid_level = liquid.amount - if(liquid && liquid_level <= 0) - del liquid - liquid_level = 0 - if(oxygen > 0.3 && (toxins || fuel_level || liquid_level)) - if(toxins && temperature < PLASMA_UPPER_TEMPERATURE) - temperature += (FIRE_PLASMA_ENERGY_RELEASED*fire_ratio_1) / heat_capacity() - if((fuel_level || liquid_level) && temperature < PLASMA_UPPER_TEMPERATURE) - temperature += (FIRE_CARBON_ENERGY_RELEASED*fire_ratio_1) / heat_capacity() + if(fuel) + //Volatile Fuel + total_fuel += fuel.moles + fuel_sources++ - if(toxins > fire_ratio_1) - oxygen -= vsc.plc.OXY_TO_PLASMA*fire_ratio_1 - toxins -= fire_ratio_1 - carbon_dioxide += fire_ratio_1 - else if(toxins) - oxygen -= toxins * vsc.plc.OXY_TO_PLASMA - carbon_dioxide += toxins - toxins = 0 + if(liquid) + //Liquid Fuel + if(liquid.amount <= 0) + del liquid + else + total_fuel += liquid.amount + fuel_sources++ - if(fuel_level > fire_ratio_1/1.5) - oxygen -= vsc.plc.OXY_TO_PLASMA*fire_ratio_1 - fuel.moles -= fire_ratio_1 - carbon_dioxide += fire_ratio_1 + //Toxins + if(toxins > 0.3) fuel_sources++ - else if(fuel_level) - oxygen -= fuel.moles * vsc.plc.OXY_TO_PLASMA - carbon_dioxide += fuel.moles - fuel.moles = 0 + if(!fuel_sources) return 0 //If there's no fuel, there's no burn. Can't divide by zero anyway. - if(liquid_level > 0) - oxygen -= fire_ratio_1 - liquid.amount = max(liquid.amount-fire_ratio_1,0) - carbon_dioxide += fire_ratio_1 - if(liquid.amount <= 0) - del liquid - return 1 - return 0 \ No newline at end of file + if(oxygen > 0.3) + + //Calculate the firelevel. + var/firelevel = calculate_firelevel(liquid) + + //Reaches a maximum practical temperature of around 2750. + + temperature = 1000*log(0.016*firelevel + 1.45) + + //Consume some gas. + var/consumed_gas = min(oxygen,0.002*firelevel,total_fuel) / fuel_sources + + oxygen -= consumed_gas + + toxins = max(0,toxins-consumed_gas) + + if(fuel) + fuel.moles -= consumed_gas + if(fuel.moles <= 0) del fuel + + if(liquid) + liquid.amount -= consumed_gas + if(liquid.amount <= 0) del liquid + + update_values() + return consumed_gas*fuel_sources + return 0 + +datum/gas_mixture/proc/calculate_firelevel(obj/liquid_fuel/liquid) + //Calculates the firelevel based on one equation instead of having to do this multiple times in different areas. + var + datum/gas/volatile_fuel/fuel = locate() in trace_gases + fuel_level = 0 + liquid_level = 0 + + if(fuel) fuel_level = fuel.moles + if(liquid) liquid_level = liquid.amount + return oxygen + toxins + liquid_level*15 + fuel_level*5 + +/mob/living/carbon/human/proc/FireBurn(mx as num) + //Burns mobs due to fire. Respects heat transfer coefficients on various body parts. + + var + head_exposure = 1 + chest_exposure = 1 + groin_exposure = 1 + legs_exposure = 1 + arms_exposure = 1 + + //Get heat transfer coefficients for clothing. + 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 & ARMS) + arms_exposure *= C.heat_transfer_coefficient + + //Always check these damage procs first if fire damage isn't working. They're probably what's wrong. + + apply_damage(2.5*mx*head_exposure, BURN, "head", 0, 0, "Fire") + apply_damage(2.5*mx*chest_exposure, BURN, "chest", 0, 0, "Fire") + apply_damage(2.0*mx*groin_exposure, BURN, "groin", 0, 0, "Fire") + apply_damage(0.6*mx*legs_exposure, BURN, "l_leg", 0, 0, "Fire") + apply_damage(0.6*mx*legs_exposure, BURN, "r_leg", 0, 0, "Fire") + apply_damage(0.4*mx*arms_exposure, BURN, "l_arm", 0, 0, "Fire") + apply_damage(0.4*mx*arms_exposure, BURN, "r_arm", 0, 0, "Fire") + + flash_pain() \ No newline at end of file diff --git a/code/ZAS/Functions.dm b/code/ZAS/Functions.dm index ab52842dd1f..b9d88a84f22 100644 --- a/code/ZAS/Functions.dm +++ b/code/ZAS/Functions.dm @@ -1,43 +1,34 @@ zone proc AddTurf(turf/T) + //Adds the turf to contents, increases the size of the zone, and sets the zone var. if(T in contents) return contents += T air.group_multiplier++ T.zone = src RemoveTurf(turf/T) + //Same, but in reverse. if(!(T in contents)) return contents -= T air.group_multiplier-- T.zone = null - DivideAir(ratio) - ratio = min(1,max(0,ratio)) - air.oxygen *= ratio - air.oxygen = QUANTIZE(air.oxygen) - air.nitrogen *= ratio - air.nitrogen = QUANTIZE(air.nitrogen) - air.toxins *= ratio - air.toxins = QUANTIZE(air.toxins) - air.carbon_dioxide *= ratio - air.carbon_dioxide = QUANTIZE(air.carbon_dioxide) - if(air.trace_gases.len) - for(var/datum/gas/trace_gas in air.trace_gases) - trace_gas.moles *= ratio - trace_gas.moles = QUANTIZE(trace_gas.moles) - air.temperature = air.temperature*ratio + TCMB*(1-ratio) - air.temperature = QUANTIZE(air.temperature) - AddSpace(turf/space/S) + //Adds a space tile to the list, and creates the list if null. if(istype(S,/turf/space)) if(!space_tiles) space_tiles = list() space_tiles += S + RemoveSpace(turf/space/S) + //Removes a space tile from the list, and deletes the list if length is 0. if(space_tiles) space_tiles -= S if(!space_tiles.len) space_tiles = null turf/proc/HasDoor(turf/O) + //Checks for the presence of doors, used for zone spreading and connection. + //A positive numerical argument checks only for closed doors. + //Another turf as an argument checks for windoors between here and there. for(var/obj/machinery/door/D in src) if(isnum(O) && O) if(!D.density) continue @@ -48,110 +39,155 @@ turf/proc/HasDoor(turf/O) return 1 turf/proc/find_zone() + //Allows newly generated turfs to join up with a nearby zone. + if(world.time < 10) return for(var/d in cardinal) var/turf/T = get_step(src,d) - if(!T || !T.zone) continue + if(!T || !T.zone || !T.ZCanPass(src)) continue if(!zone) zone = T.zone zone.AddTurf(src) else if(T.zone != zone) ZConnect(src,T) +turf/proc/check_connections() + //Checks for new connections that can be made. + for(var/d in cardinal) + var/turf/T = get_step(src,d) + if(!T || !T.zone || !T.CanPass(0,src,0,0)) continue + if(T.zone != zone) + ZConnect(src,T) + +turf/proc/check_for_space() + //Checks for space around the turf. + for(var/d in cardinal) + var/turf/T = get_step(src,d) + if(istype(T,/turf/space) && T.CanPass(0,src,0,0)) + zone.AddSpace(T) + proc ZMerge(zone/A,zone/B) - //world << "Merge occured." + //Merges two zones so that they are one. var a_size = A.air.group_multiplier b_size = B.air.group_multiplier c_size = a_size + b_size new_contents = A.contents + B.contents + //Set air multipliers to one so air represents gas per tile. A.air.group_multiplier = 1 B.air.group_multiplier = 1 + //Remove some air proportional to the size of this zone. A.air.remove_ratio(a_size/c_size) B.air.remove_ratio(b_size/c_size) + + //Merge the gases and set the multiplier to the sum of the old ones. A.air.merge(B.air) A.air.group_multiplier = c_size + //Check for connections to merge into the new zone. for(var/connection/C in B.connections) if((C.A in new_contents) && (C.B in new_contents)) del C continue + if(!A.connections) A.connections = list() A.connections += C + + //Add space tiles. A.space_tiles += B.space_tiles + + //Add contents. A.contents = new_contents + + //Set all the zone vars. for(var/turf/T in B.contents) T.zone = A + del B ZConnect(turf/A,turf/B) + //Connects two zones by forming a connection object representing turfs A and B. + + //Make sure that if it's space, it gets added to space_tiles instead. if(istype(B,/turf/space)) if(A.zone) A.zone.AddSpace(B) - //world << "Space added." return if(istype(A,/turf/space)) if(B.zone) - B.zone.AddSpace(B) - //world << "Space added." + B.zone.AddSpace(A) return + + //Make some preliminary checks to see if the connection is valid. if(!A.zone || !B.zone) return if(A.zone == B.zone) return if(!A.CanPass(0,B,0,0)) return + + //Ensure the connection isn't already made. for(var/connection/C in A.zone.connections) if((C.A == A && C.B == B) || (C.A == B && C.B == A)) return - var/connection/C = new(A,B) - if(A.HasDoor(B) || B.HasDoor(A)) C.indirect = 1 - //world << "Connection Formed: [A] --> [B] [(C.indirect?"Indirect":"Direct")]" - //A.overlays += 'zone_connection_A.dmi' - //B.overlays += 'zone_connection_B.dmi' - //spawn(10) - // A.overlays -= 'zone_connection_A.dmi' - // B.overlays -= 'zone_connection_B.dmi' + var/connection/C = new(A,B) + + //Ensure zones separated by doors do not merge. + if(A.HasDoor(B) || B.HasDoor(A)) C.indirect = 1 ZDisconnect(turf/A,turf/B) + //Removes a zone connection. Can split zones in the case of a permanent barrier. + + //If one of them doesn't have a zone, it might be space, so check for that. if(A.zone && B.zone) + //If the two zones are different, just remove a connection. if(A.zone != B.zone) for(var/connection/C in A.zone.connections) if((C.A == A && C.B == B) || (C.A == B && C.B == A)) - //world << "Connection Dissolved: [A] -/-> [B] [(C.indirect?"Indirect":"Direct")]" - /*A.overlays += 'zone_connection_A.dmi' - B.overlays += 'zone_connection_B.dmi' - spawn(10) - A.overlays -= 'zone_connection_A.dmi' - B.overlays -= 'zone_connection_B.dmi'*/ del C - /*else + //If they're the same, split the zone at this line. + else + //Preliminary checks to prevent stupidity. if(A == B) return if(A.CanPass(0,B,0,0)) return if(A.HasDoor(B) || B.HasDoor(A)) return + + //Do a test fill. If turf B is still in the floodfill, then the zone isn't really split. var/zone/oldzone = A.zone var/list/test = FloodFill(A) if(B in test) return + else - var/zone/Z = new(test,oldzone.air) + var/zone/Z = new(test,oldzone.air) //Create a new zone based on the old air and the test fill. + + //Add connections from the old zone. for(var/connection/C in oldzone.connections) if((A in Z.contents) || (B in Z.contents)) if(!Z.connections) Z.connections = list() Z.connections += C + + //Check for space. + for(var/turf/T in test) + T.check_for_space() + + //Make a new, identical air mixture for the other zone. var/datum/gas_mixture/Y_Air = new Y_Air.copy_from(oldzone.air) - var/zone/Y = new(B,Y_Air) + + var/zone/Y = new(B,Y_Air) //Make a new zone starting at B and using Y_Air. + + //Add relevant connections from old zone. for(var/connection/C in oldzone.connections) if((A in Y.contents) || (B in Y.contents)) if(!Y.connections) Y.connections = list() Y.connections += C + + //Add the remaining space tiles to this zone. + for(var/turf/space/T in oldzone.space_tiles) + if(!(T in Z.space_tiles)) + Y.AddSpace(T) + oldzone.air = null del oldzone - world << "Zone Split: [A] / [B]" - A.overlays += 'zone_connection_A.dmi' - B.overlays += 'zone_connection_B.dmi' - spawn(10) - A.overlays -= 'zone_connection_A.dmi' - B.overlays -= 'zone_connection_B.dmi'*/ else if(istype(A,/turf/space) && B.zone) B.zone.RemoveSpace(A) diff --git a/code/ZAS/Plasma.dm b/code/ZAS/Plasma.dm index df25a974578..858799a2729 100644 --- a/code/ZAS/Plasma.dm +++ b/code/ZAS/Plasma.dm @@ -1,252 +1,139 @@ 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 = 0 //If this is on, plasma does damage by getting into cloth. - 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" + PLASMAGUARD_ONLY = 0 //If this is on, only biosuits and spacesuits protect against contamination and ill effects. 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 = 1 //Plasma burns the eyes of anyone not wearing eye protection. - 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." + CONTAMINATION_LOSS = 0.01 - //N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high. - //N2O_REACTION_RANDOM = 5 + PLASMA_HALLUCINATION = 0 - //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. + obj/var/contaminated = 0 obj/item/proc can_contaminate() + //Clothing and backpacks can be contaminated. if(flags & PLASMAGUARD) return 0 - if((flags & SUITSPACE) && !vsc.plc.PLASMAGUARD_ONLY) return 1 - if(vsc.plc.ALL_ITEM_CONTAMINATION) return 1 + if(flags & SUITSPACE) return 0 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() + contaminate() + //Do a contamination overlay? Temporary measure to keep contamination less deadly than it was. + if(!contaminated) + contaminated = 1 + overlays += 'icons/effects/contamination.dmi' + + decontaminate() + contaminated = 0 + overlays -= 'icons/effects/contamination.dmi' + +/mob/proc/contaminate() + +/mob/living/carbon/human/contaminate() + //See if anything can be contaminated. + if(!pl_suit_protected()) suit_contamination() - else if(vsc.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(vsc.plc.PLASMAGUARD_ONLY) - if(!head.flags & PLASMAGUARD) head.contaminated = 1 - if(istype(back,/obj/item/weapon/storage/backpack) || vsc.plc.ALL_ITEM_CONTAMINATION) - back.contaminated = 1 + if(istype(back,/obj/item/weapon/storage/backpack)) + back.contaminate() - 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/proc/pl_effects() -/mob/living/carbon/human/proc/pl_effects() +/mob/living/carbon/human/pl_effects() + //Handles all the bad things plasma can do. + + //Contamination + if(vsc.plc.CLOTH_CONTAMINATION) contaminate() + + //Anything else requires them to not be dead. if(stat >= 2) return + + //Burn skin if exposed. if(vsc.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 + if(prob(20)) src << "\red Your skin burns!" updatehealth() - if(vsc.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) + + //Burn eyes if exposed. + if(vsc.plc.EYE_BURNS) + if(!head) + if(!wear_mask) + burn_eyes() + else + if(!(wear_mask.flags & MASKCOVERSEYES)) + burn_eyes() 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(!(head.flags & HEADCOVERSEYES)) + if(!wear_mask) + burn_eyes() + else + if(!(wear_mask.flags & MASKCOVERSEYES)) + burn_eyes() + + //Genetic Corruption if(vsc.plc.GENETIC_CORRUPTION) - if(rand(1,1000) < vsc.plc.GENETIC_CORRUPTION) + if(rand(1,10000) < vsc.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 *= vsc.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/burn_eyes() + //The proc that handles eye burning. + if(prob(20)) src << "\red Your eyes burn!" + eye_stat += 2.5 + eye_blurry = min(eye_blurry+1.5,50) + if (prob(max(0,eye_stat - 20) + 1) &&!eye_blind) + src << "\red You are blinded!" + eye_blind += 20 + eye_stat = 0 /mob/living/carbon/human/proc/pl_head_protected() + //Checks if the head is adequately sealed. if(head) - if(head.flags & PLASMAGUARD || head.flags & HEADSPACE) return 1 + if(vsc.plc.PLASMAGUARD_ONLY) + if(head.flags & PLASMAGUARD || head.flags & HEADSPACE) return 1 + else + if(head.flags & HEADCOVERSEYES) return 1 return 0 /mob/living/carbon/human/proc/pl_suit_protected() + //Checks if the suit is adequately sealed. if(wear_suit) - if(wear_suit.flags & PLASMAGUARD || wear_suit.flags & SUITSPACE) return 1 + if(vsc.plc.PLASMAGUARD_ONLY) + if(wear_suit.flags & PLASMAGUARD || wear_suit.flags & SUITSPACE) return 1 + else + if(wear_suit.flags_inv & HIDEJUMPSUIT) return 1 return 0 /mob/living/carbon/human/proc/suit_contamination() - if(vsc.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 + //Runs over the things that can be contaminated and does so. + if(w_uniform) w_uniform.contaminate() + if(shoes) shoes.contaminate() + if(gloves) gloves.contaminate() turf/Entered(obj/item/I) . = ..() - if(istype(I)) + //Items that are in plasma, but not on a mob, can still be contaminated. + if(istype(I) && vsc.plc.CLOTH_CONTAMINATION) var/datum/gas_mixture/env = return_air(1) - if(env.toxins > 0.35) + if(env.toxins > MOLES_PLASMA_VISIBLE + 1) if(I.can_contaminate()) - I.contaminated = 1 \ No newline at end of file + I.contaminate() \ No newline at end of file diff --git a/code/ZAS/Processing.dm b/code/ZAS/Processing.dm index 4de6c939860..7bd3c61ecf8 100644 --- a/code/ZAS/Processing.dm +++ b/code/ZAS/Processing.dm @@ -2,19 +2,26 @@ var/explosion_halt = 0 zone proc/process() + //Does rebuilding stuff. Not sure if used. if(rebuild) + //Deletes zone if empty. if(!contents.len) del src return 0 + + //Choose a random turf and regenerate the zone from it. var turf/sample = pick(contents) list/new_contents = FloodFill(sample) problem = 0 + + //If something isn't carried over, there was a complication. for(var/turf/T in contents) if(!(T in new_contents)) problem = 1 if(problem) + //Build some new zones for stuff that wasn't included. var/list/rebuild_turfs = list() for(var/turf/T in contents - new_contents) contents -= T @@ -26,74 +33,181 @@ zone Z.air.copy_from(air) rebuild = 0 - var/total_space = 0 - var/turf/space/space + //Sometimes explosions will cause the air to be deleted for some reason. + if(!air) + air = new() + air.adjustGases(MOLES_O2STANDARD, 0, MOLES_N2STANDARD, 0, list()) + world.log << "Air object lost in zone. Regenerating." - if(length(connected_zones)) - for(var/zone/Z in connected_zones) - total_space += length(Z.space_tiles) - if(length(Z.space_tiles)) - space = Z.space_tiles[1] + //Counting up space. + var/total_space = 0 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] + total_space = length(space_tiles) - 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) - if(dbg_output) world << "Space removed [MOLES_CELLSTANDARD*(air.group_multiplier/40)*total_space] moles of air." - var/p_diff = old_pressure - air.return_pressure() - if(p_diff > vsc.AF_TINY_MOVEMENT_THRESHOLD) AirflowSpace(src,p_diff) + //Add checks to ensure that we're not sucking air out of an empty room. + if(total_space && air.total_moles > 0.1 && air.temperature > TCMB+0.5) + //If there is space, air should flow out of the zone. + //if(abs(air.pressure) > vsc.airflow_lightest_pressure) + // AirflowSpace(src) + ShareSpace(air,total_space*(vsc.zone_share_percent/100)) + //React the air here. air.react(null,0) + + //Check the graphic. var/check = air.check_tile_graphic() - for(var/turf/T in contents) - if(T.zone && T.zone != src) - RemoveTurf(T) - if(dbg_output) world << "Removed invalid turf." - if(air.group_multiplier <= 0) // No more turfs belong to this zone, so we can get rid of it - del(src) - else // Turf was valid, so we can handle it - if(!T.zone) - T.zone = src - if(istype(T,/turf/simulated)) - var/turf/simulated/S = T - if(S.fire_protection) S.fire_protection-- - if(check) - if(S.HasDoor(1)) - S.update_visuals() - else - S.update_visuals(air) + //Only run through the individual turfs if there's reason to. + if(check || air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST) - if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST) - for(var/atom/movable/item in S) - item.temperature_expose(air, air.temperature, CELL_VOLUME) - S.temperature_expose(air, air.temperature, CELL_VOLUME) + for(var/turf/simulated/S in contents) + //Update overlays. + if(check) + if(S.HasDoor(1)) + S.update_visuals() + else + S.update_visuals(air) + //Expose stuff to extreme heat. + if(air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_EXIST) + for(var/atom/movable/item in S) + item.temperature_expose(air, air.temperature, CELL_VOLUME) + S.temperature_expose(air, air.temperature, CELL_VOLUME) + + //Archive graphic so we can know if it's different. air.graphic_archived = air.graphic + //Ensure temperature does not reach absolute zero. air.temperature = max(TCMB,air.temperature) + //Handle connections to other zones. if(length(connections)) for(var/connection/C in connections) + //Check if the connection is valid first. C.Cleanup() - if(C && !C.indirect) - if(C.A.zone.air.compare(C.B.zone.air)) + //Do merging if conditions are met. Specifically, if there's a non-door connection + //to somewhere with space, the zones are merged regardless of equilibrium, to speed + //up spacing in areas with double-plated windows. + if(C && !C.indirect && C.A.zone && C.B.zone) + if(C.A.zone.air.compare(C.B.zone.air) || total_space) ZMerge(C.A.zone,C.B.zone) + + //Share some for(var/zone/Z in connected_zones) - 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)) + //Ensure we're not doing pointless calculations on equilibrium zones. + if(abs(air.total_moles - Z.air.total_moles) > 0.1 || abs(air.temperature - Z.air.temperature) > 0.1) + //if(abs(Z.air.pressure - air.pressure) > vsc.airflow_lightest_pressure) + // Airflow(src,Z) + ShareRatio(air,Z.air,connected_zones[Z]*(vsc.zone_share_percent/100)) + +proc/ShareRatio(datum/gas_mixture/A, datum/gas_mixture/B, ratio) + //Shares a specific ratio of gas between mixtures using simple weighted averages. + var + size = max(1,A.group_multiplier) + share_size = max(1,B.group_multiplier) + + full_oxy = A.oxygen * size + full_nitro = A.nitrogen * size + full_co2 = A.carbon_dioxide * size + full_plasma = A.toxins * size + + full_thermal = A.temperature * A.heat_capacity() * size + + s_full_oxy = B.oxygen * share_size + s_full_nitro = B.nitrogen * share_size + s_full_co2 = B.carbon_dioxide * share_size + s_full_plasma = B.toxins * share_size + + s_full_thermal = B.temperature * B.heat_capacity() * share_size + + oxy_avg = (full_oxy + s_full_oxy) / (size + share_size) + nit_avg = (full_nitro + s_full_nitro) / (size + share_size) + co2_avg = (full_co2 + s_full_co2) / (size + share_size) + plasma_avg = (full_plasma + s_full_plasma) / (size + share_size) + + thermal_avg = (full_thermal + s_full_thermal) / (size+share_size) + + A.oxygen = (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg + A.nitrogen = (A.nitrogen - nit_avg) * (1-ratio) + nit_avg + A.carbon_dioxide = (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg + A.toxins = (A.toxins - plasma_avg) * (1-ratio) + plasma_avg + + B.oxygen = (B.oxygen - oxy_avg) * (1-ratio) + oxy_avg + B.nitrogen = (B.nitrogen - nit_avg) * (1-ratio) + nit_avg + B.carbon_dioxide = (B.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg + B.toxins = (B.toxins - plasma_avg) * (1-ratio) + plasma_avg + + var + thermal = (full_thermal/size - thermal_avg) * (1-ratio) + thermal_avg + sharer_thermal = (s_full_thermal/share_size - thermal_avg) * (1-ratio) + thermal_avg + + A.temperature = thermal / (A.heat_capacity() == 0 ? MINIMUM_HEAT_CAPACITY : A.heat_capacity()) + + B.temperature = sharer_thermal / (B.heat_capacity() == 0 ? MINIMUM_HEAT_CAPACITY : B.heat_capacity()) + + for(var/datum/gas/G in A.trace_gases) + var/datum/gas/H = locate(G.type) in B.trace_gases + if(H) + var/G_avg = (G.moles*size + H.moles*share_size) / (size+share_size) + G.moles = (G.moles - G_avg) * (1-ratio) + G_avg + H.moles = (H.moles - G_avg) * (1-ratio) + G_avg + else + H = new G.type + B.trace_gases += H + var/G_avg = (G.moles*size) / (size+share_size) + G.moles = (G.moles - G_avg) * (1-ratio) + G_avg + H.moles = (H.moles - G_avg) * (1-ratio) + G_avg + + A.update_values() + B.update_values() + + if(A.compare(B)) return 1 + else return 0 + +proc/ShareSpace(datum/gas_mixture/A, ratio) + //A modified version of ShareRatio for spacing gas at the same rate as if it were going into a huge airless room. + var + size = max(1,A.group_multiplier) + share_size = 2000 //A huge value because space is huge. + + full_oxy = A.oxygen * size + full_nitro = A.nitrogen * size + full_co2 = A.carbon_dioxide * size + full_plasma = A.toxins * size + + full_thermal = A.temperature * A.heat_capacity() * size + + oxy_avg = (full_oxy + 0) / (size + share_size) + nit_avg = (full_nitro + 0.2) / (size + share_size) + co2_avg = (full_co2 + 0) / (size + share_size) + plasma_avg = (full_plasma + 0) / (size + share_size) + + thermal_avg = (full_thermal + MINIMUM_HEAT_CAPACITY) / (size+share_size) + + A.oxygen = (A.oxygen - oxy_avg) * (1-ratio) + oxy_avg + A.nitrogen = (A.nitrogen - nit_avg) * (1-ratio) + nit_avg + A.carbon_dioxide = (A.carbon_dioxide - co2_avg) * (1-ratio) + co2_avg + A.toxins = (A.toxins - plasma_avg) * (1-ratio) + plasma_avg + + var/thermal = (full_thermal/size - thermal_avg) * (1-ratio) + thermal_avg + + A.temperature = thermal / (A.heat_capacity() == 0 ? MINIMUM_HEAT_CAPACITY : A.heat_capacity()) + + for(var/datum/gas/G in A.trace_gases) + var/G_avg = (G.moles*size + 0) / (size+share_size) + G.moles = (G.moles - G_avg) * (1-ratio) + G_avg + + A.update_values() + + return 1 zone/proc connected_zones() + //A legacy proc for getting connected zones. . = list() for(var/connection/C in connections) var/zone/Z diff --git a/code/ZAS/Variable Settings.dm b/code/ZAS/Variable Settings.dm index 188f885ae9b..70f186613b2 100644 --- a/code/ZAS/Variable Settings.dm +++ b/code/ZAS/Variable Settings.dm @@ -119,8 +119,6 @@ vs_control 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, @@ -133,6 +131,8 @@ vs_control plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection. + plc.PLASMA_HALLUCINATION = 0 + //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! @@ -141,10 +141,8 @@ vs_control //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 + if("Hazard - Low") + plc.CLOTH_CONTAMINATION = 0 //If this is on, plasma does damage by getting into cloth. plc.PLASMAGUARD_ONLY = 0 @@ -152,7 +150,7 @@ vs_control 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.SKIN_BURNS = 1 //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. @@ -168,27 +166,22 @@ vs_control //plc.PLASMA_DMG_OFFSET = 1.5 //plc.PLASMA_DMG_QUOTIENT = 8 - plc.CONTAMINATION_LOSS = 0.01 + plc.CONTAMINATION_LOSS = 0 - var/s = pick(plc.settings) - plc.Randomize(s) - - if("Hazard-High") + 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.SKIN_BURNS = 1 //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.EYE_BURNS = 1 //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. @@ -197,17 +190,9 @@ vs_control //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") + if("Oh Shit!") 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, @@ -227,7 +212,6 @@ vs_control //plc.PLASMA_DMG_OFFSET = 3 //plc.PLASMA_DMG_QUOTIENT = 5 - plc.CONTAMINATION_LOSS = 0.02 /////world << "Plasma color updated." pl_control diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 6f0bbe3aebc..8b93f01736d 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -52,7 +52,7 @@ //must succeed in most cases proc/setTeleatom(atom/movable/ateleatom) - if(istype(ateleatom, /obj/effect)) + if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) del(ateleatom) return 0 if(istype(ateleatom)) diff --git a/code/defines/atom.dm b/code/defines/atom.dm index d725f656503..1a1a79b94f8 100644 --- a/code/defines/atom.dm +++ b/code/defines/atom.dm @@ -17,7 +17,7 @@ // replaced by OPENCONTAINER flags and atom/proc/is_open_container() ///Chemistry. - proc/assume_air(datum/air_group/giver) + proc/assume_air(datum/gas_mixture/giver) del(giver) return null @@ -48,7 +48,7 @@ */ obj - assume_air(datum/air_group/giver) + assume_air(datum/gas_mixture/giver) if(loc) return loc.assume_air(giver) else diff --git a/code/defines/mob/living/living.dm b/code/defines/mob/living/living.dm index 03d77d4670b..dc767fce085 100644 --- a/code/defines/mob/living/living.dm +++ b/code/defines/mob/living/living.dm @@ -5,4 +5,14 @@ var/t_n2 = null var/now_pushing = null var/cameraFollow = null - var/age = null \ No newline at end of file + var/age = null + + +// lists used to cache overlays +/mob/living/var/list/body_overlays_standing = list() +/mob/living/var/list/body_overlays_lying = list() +/mob/living/var/list/clothing_overlays = list() + +// This var describes whether the mob visually appears to be lying. +// This will be used to only update lying status when necessary. +/mob/living/var/visual_lying = 0 \ No newline at end of file diff --git a/code/defines/obj/vending.dm b/code/defines/obj/vending.dm index 28f05cb1303..0f3c1fb7473 100755 --- a/code/defines/obj/vending.dm +++ b/code/defines/obj/vending.dm @@ -126,9 +126,9 @@ name = "Robust Softdrinks" desc = "A softdrink vendor provided by Robust Industries, LLC." icon_state = "Cola_Machine" - product_paths = "/obj/item/weapon/reagent_containers/food/drinks/cola;/obj/item/weapon/reagent_containers/food/drinks/space_mountain_wind;/obj/item/weapon/reagent_containers/food/drinks/dr_gibb;/obj/item/weapon/reagent_containers/food/drinks/starkist;/obj/item/weapon/reagent_containers/food/drinks/space_up" - product_amounts = "10;10;10;10;10" - product_prices = "1;1;1;1;1" + product_paths = "/obj/item/weapon/reagent_containers/food/drinks/cola;/obj/item/weapon/reagent_containers/food/drinks/space_mountain_wind;/obj/item/weapon/reagent_containers/food/drinks/dr_gibb;/obj/item/weapon/reagent_containers/food/drinks/starkist;/obj/item/weapon/reagent_containers/food/drinks/space_up;/obj/item/weapon/reagent_containers/food/drinks/waterbottle" +// product_amounts = "10;10;10;10;10" + product_prices = "1;1;1;1;1;1" product_slogans = "Robust Softdrinks: More robust then a toolbox to the head!" product_hidden = "/obj/item/weapon/reagent_containers/food/drinks/thirteenloko" product_hideamt = "5" diff --git a/code/defines/procs/helpers.dm b/code/defines/procs/helpers.dm index 8a2d3311421..7f93f300c90 100644 --- a/code/defines/procs/helpers.dm +++ b/code/defines/procs/helpers.dm @@ -1555,19 +1555,13 @@ proc/listclearnulls(list/list) for(var/turf/simulated/T1 in toupdate) for(var/obj/machinery/door/D2 in T1) doors += D2 - if(T1.parent) - air_master.groups_to_rebuild += T1.parent - else - air_master.tiles_to_update += T1 + air_master.tiles_to_update += T1 if(fromupdate.len) for(var/turf/simulated/T2 in fromupdate) for(var/obj/machinery/door/D2 in T2) doors += D2 - if(T2.parent) - air_master.groups_to_rebuild += T2.parent - else - air_master.tiles_to_update += T2 + air_master.tiles_to_update += T2 for(var/obj/O in doors) O:update_nearby_tiles(1) @@ -1722,10 +1716,7 @@ proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) for(var/turf/simulated/T1 in toupdate) for(var/obj/machinery/door/D2 in T1) doors += D2 - if(T1.parent) - air_master.groups_to_rebuild += T1.parent - else - air_master.tiles_to_update += T1 + air_master.tiles_to_update += T1 for(var/obj/O in doors) O:update_nearby_tiles(1) diff --git a/code/defines/turf.dm b/code/defines/turf.dm index 4fc3dfa963c..3eb764e5c6a 100644 --- a/code/defines/turf.dm +++ b/code/defines/turf.dm @@ -341,8 +341,6 @@ proc/TemperatureAct(temperature) for(var/turf/simulated/floor/target_tile in range(2,loc)) - if(target_tile.parent && target_tile.parent.group_processing) - target_tile.parent.suspend_group_processing() var/datum/gas_mixture/napalm = new diff --git a/code/game/atom_procs.dm b/code/game/atom_procs.dm index 065e56b265b..9142c152ce9 100644 --- a/code/game/atom_procs.dm +++ b/code/game/atom_procs.dm @@ -338,7 +338,7 @@ del(fingerprints) if(istype(src, /mob/living/carbon/human)) var/mob/living/carbon/human/M = src - M.update_clothing() + M.rebuild_appearance() // both clothes and hands need to be cleaned, so just rebuild all return /atom/MouseDrop(atom/over_object as mob|obj|turf|area) diff --git a/code/game/dna.dm b/code/game/dna.dm index f10a2f50ced..b5b1b96f979 100644 --- a/code/game/dna.dm +++ b/code/game/dna.dm @@ -547,7 +547,7 @@ if (W==H.w_uniform) // will be teared continue H.drop_from_slot(W) - M.update_clothing() + M.rebuild_appearance() M.monkeyizing = 1 M.canmove = 0 M.icon = null @@ -604,7 +604,7 @@ O.a_intent = "hurt" O.flavor_text = M.flavor_text O.warn_flavor_changed() - O.update_clothing() + O.rebuild_appearance() del(M) return @@ -615,7 +615,7 @@ if(!connected) for(var/obj/item/W in (Mo.contents)) Mo.drop_from_slot(W) - M.update_clothing() + M.rebuild_appearance() M.monkeyizing = 1 M.canmove = 0 M.icon = null @@ -681,12 +681,12 @@ O.stat = M.stat O.flavor_text = M.flavor_text O.warn_flavor_changed() - O.update_clothing() + O.rebuild_appearance() del(M) return //////////////////////////////////////////////////////////// Monkey Block if (M) - M.update_clothing() + M.rebuild_appearance() return null /////////////////////////// DNA MISC-PROCS @@ -790,13 +790,7 @@ /obj/machinery/dna_scannernew/proc/go_out() if ((!( src.occupant ) || src.locked)) return -/* -// it's like this was -just- here to break constructed dna scanners -Pete -// if that's not the case, slap my shit and uncomment this. -// for(var/obj/O in src) -// O.loc = src.loc -*/ - //Foreach goto(30) + if (src.occupant.client) src.occupant.client.eye = src.occupant.client.mob src.occupant.client.perspective = MOB_PERSPECTIVE diff --git a/code/game/gamemodes/changeling/changeling_powers.dm b/code/game/gamemodes/changeling/changeling_powers.dm index 0d0bea60a79..a843cb12441 100644 --- a/code/game/gamemodes/changeling/changeling_powers.dm +++ b/code/game/gamemodes/changeling/changeling_powers.dm @@ -260,7 +260,7 @@ if (istype(W, /obj/item/weapon/implant)) implants += W - usr.update_clothing() + usr.rebuild_appearance() usr.monkeyizing = 1 usr.canmove = 0 usr.icon = null @@ -346,7 +346,7 @@ for (var/obj/item/weapon/implant/I in usr) //Still preserving implants implants += I - usr.update_clothing() + usr.rebuild_appearance() usr.monkeyizing = 1 usr.canmove = 0 usr.icon = null @@ -414,7 +414,7 @@ return for(var/obj/item/W in src) usr.drop_from_slot(W) - usr.update_clothing() + usr.rebuild_appearance() usr.monkeyizing = 1 usr.canmove = 0 usr.icon = null diff --git a/code/game/hud.dm b/code/game/hud.dm index 5539c8c3c40..c276b260c9f 100644 --- a/code/game/hud.dm +++ b/code/game/hud.dm @@ -35,19 +35,19 @@ #define ui_monkey_back "6:14,1:5" //monkey //Lower right, persistant menu -#define ui_dropbutton "13:26,1:5" -#define ui_throw "13:26,1:5" -#define ui_pull "13:26,2:7" -#define ui_acti "12:24,1:5" +#define ui_dropbutton "11:22,1:5" +#define ui_throw "11:22,1:5" +#define ui_pull "12:24,1:5" +#define ui_movi "12:24,1:5" -#define ui_movi "13:26,2:7" +#define ui_acti "13:26,1:5" #define ui_zonesel "14:28,1:5" //Gun buttons -#define ui_gun1 "11:22,2:7" -#define ui_gun2 "10:20,2:7" -#define ui_gun3 "9:18,2:7" -#define ui_gun_select "11:22,1:5" +#define ui_gun1 "13:26,2:5" +#define ui_gun2 "12:24,2:5" +#define ui_gun3 "11:22,2:5" +#define ui_gun_select "14:28,2:5" //Middle right (damage indicators) #define ui_pressure "14:28,6:13" @@ -67,12 +67,15 @@ #define ui_health "14:28,7:15" #define ui_internal "14:28,8:17" +//For non-human compatibility //borgs #define ui_borg_health "14:28,6:13" //borgs have the health display where humans have the pressure damage indicator. #define ui_alien_health "14:28,6:13" //aliens have the health display where humans have the pressure damage indicator. #define ui_throw_old "13:26,2:7" #define ui_movi_old "12:24,2:7" #define ui_pull_old "14:28,2:7" +#define ui_dropbutton_old "12:24,1:5" +#define ui_pull_borg "12:24,1:5" //Pop-up inventory #define ui_shoes "2:8,1:5" @@ -92,10 +95,10 @@ //Intent small buttons -#define ui_help_small "12:16,1:-3" -#define ui_disarm_small "12:16,1:12" -#define ui_grab_small "12:32,1:12" -#define ui_harm_small "12:32,1:-3" +#define ui_help_small "13:18,1:-3" +#define ui_disarm_small "13:18,1:12" +#define ui_grab_small "13:34,1:12" +#define ui_harm_small "13:34,1:-3" diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index ed4cd490aa6..a12c54feece 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -52,6 +52,12 @@ if(H.mind.role_alt_title && H.mind.role_alt_title == "Virologist") H.equip_if_possible(new /obj/item/clothing/under/rank/virologist(H), H.slot_w_uniform) H.equip_if_possible(new /obj/item/clothing/suit/storage/labcoat/virologist(H), H.slot_wear_suit) + else if(H.mind.role_alt_title && H.mind.role_alt_title == "Emergency Physician") + H.equip_if_possible(new /obj/item/clothing/under/rank/medical(H), H.slot_w_uniform) + H.equip_if_possible(new /obj/item/clothing/suit/storage/labcoat/fr_jacket(H), H.slot_wear_suit) + else if(H.mind.role_alt_title && H.mind.role_alt_title == "Surgeon") + H.equip_if_possible(new /obj/item/clothing/under/rank/medical/blue(H), H.slot_w_uniform) + H.equip_if_possible(new /obj/item/clothing/suit/storage/labcoat(H), H.slot_wear_suit) else H.equip_if_possible(new /obj/item/clothing/under/rank/medical(H), H.slot_w_uniform) H.equip_if_possible(new /obj/item/clothing/suit/storage/labcoat(H), H.slot_wear_suit) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 429fe4be8d2..f519b02625d 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -105,7 +105,7 @@ icon_state = "table2-active" return 1 if(victim) - victim.update_clothing() + victim.update_lying() victim = null if(updatesicon) icon_state = "table2-idle" @@ -134,7 +134,7 @@ if(updatesicon) icon_state = "table2-active" src.victim = M - M.update_clothing() + M.update_lying() processing_objects.Add(src) del(W) return diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index fae6eb72e4e..6c9d35414c8 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -409,7 +409,6 @@ "\red You hear a whine as \the [src]'s is hit by something dense.") H.UpdateDamageIcon() H.updatehealth() - H.update_clothing() playsound(src.loc, 'tablehit1.ogg', 50, 1, -3) else //Lets do REAL DAMAGE, YEAH! G.affecting.attack_log += text("\[[time_stamp()]\] Has been smashed on a table by [G.assailant.name] ([G.assailant.ckey])") @@ -448,7 +447,6 @@ "\red You hear the nauseating crunch of bone and gristle on solid metal.") H.UpdateDamageIcon() H.updatehealth() - H.update_clothing() playsound(src.loc, 'tablehit1.ogg', 50, 1, -3) return G.affecting.loc = src.loc diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index fb7b3c6a1b3..aa51784a615 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -267,12 +267,12 @@ if (src.operating) return src.operating = 1 - + var/X = src:x var/Y = src:y var/Z = src:z var/held = 1 - + if(src.holdopen && !forcecrush) while(held == 1 && holdopen && !forcecrush) //If it is no longer hold open, it should close. sleep(held? 10:40) @@ -288,25 +288,25 @@ spawn(4) if(!istype(src, /obj/machinery/door/window)) for(var/mob/living/L in src.loc) // Crush mobs and move them out of the way - + if(src.forcecrush) // Save an AI, crush a limb var/limbname = pick("l_arm", "r_arm", "l_hand","r_hand", "l_foot", "r_foot") var/limbdisplay - + for(var/organ in L:organs) var/datum/organ/external/temp = L:organs["[organ]"] if (istype(temp, /datum/organ/external) && temp.name == limbname) limbdisplay = temp.display_name // Take the name for down below temp.take_damage(rand(50,80), 0) //OH GOD IT HURTS break - + L << "\red The airlock crushes your [limbdisplay]!" for(var/mob/O in viewers(L, null)) if(O == L) continue O.show_message("\red The airlock crushes [L.name]'s [limbdisplay]!", 1) sleep(rand(2,8)) - + L << "\red The airlock forces you out of the way!" //Lucky you for(var/mob/O in viewers(L, null)) O.show_message("\red The airlock pushes [L.name] out of the way!", 1) @@ -314,7 +314,7 @@ var/turf/T = get_random_turf(L, lst) if(T) L.loc = T - + if(!src.forcecrush) for(var/obj/item/I in src.loc) // Move items out of the way if(!I.anchored) @@ -322,10 +322,10 @@ var/turf/T = get_random_turf(I, lst) if(T) I.loc = T - + sleep(6) update_icon() - + if(src.visible && (!src.glass)) src.sd_SetOpacity(1) if(operating == 1) @@ -343,38 +343,11 @@ var/turf/simulated/east = get_step(source,EAST) var/turf/simulated/west = get_step(source,WEST) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(north)) - if(north.parent) - air_master.groups_to_rebuild += north.parent - else - air_master.tiles_to_update += north - if(istype(south)) - if(south.parent) - air_master.groups_to_rebuild += south.parent - else - air_master.tiles_to_update += south - if(istype(east)) - if(east.parent) - air_master.groups_to_rebuild += east.parent - else - air_master.tiles_to_update += east - if(istype(west)) - if(west.parent) - air_master.groups_to_rebuild += west.parent - else - air_master.tiles_to_update += west - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(north)) air_master.tiles_to_update += north - if(istype(south)) air_master.tiles_to_update += south - if(istype(east)) air_master.tiles_to_update += east - if(istype(west)) air_master.tiles_to_update += west + if(istype(source)) air_master.tiles_to_update += source + if(istype(north)) air_master.tiles_to_update += north + if(istype(south)) air_master.tiles_to_update += south + if(istype(east)) air_master.tiles_to_update += east + if(istype(west)) air_master.tiles_to_update += west return 1 diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 41462f0b0ce..efb86ae369e 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -110,18 +110,6 @@ var/turf/simulated/source = loc var/turf/simulated/destination = get_step(source,dir) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(destination)) - if(destination.parent) - air_master.groups_to_rebuild += destination.parent - else - air_master.tiles_to_update += destination - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(destination)) air_master.tiles_to_update += destination + if(istype(source)) air_master.tiles_to_update += source + if(istype(destination)) air_master.tiles_to_update += destination return 1 \ No newline at end of file diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 50baa203d0d..27967591d0d 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -15,20 +15,8 @@ var/turf/simulated/source = loc var/turf/simulated/target = get_step(source,dir) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(target)) - if(target.parent) - air_master.groups_to_rebuild += target.parent - else - air_master.tiles_to_update += target - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(target)) air_master.tiles_to_update += target + if(istype(source)) air_master.tiles_to_update += source + if(istype(target)) air_master.tiles_to_update += target return 1 diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index 9bb05f46583..6050615cdcc 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -408,38 +408,11 @@ var/turf/simulated/east = get_step(source,EAST) var/turf/simulated/west = get_step(source,WEST) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(north)) - if(north.parent) - air_master.groups_to_rebuild += north.parent - else - air_master.tiles_to_update += north - if(istype(south)) - if(south.parent) - air_master.groups_to_rebuild += south.parent - else - air_master.tiles_to_update += south - if(istype(east)) - if(east.parent) - air_master.groups_to_rebuild += east.parent - else - air_master.tiles_to_update += east - if(istype(west)) - if(west.parent) - air_master.groups_to_rebuild += west.parent - else - air_master.tiles_to_update += west - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(north)) air_master.tiles_to_update += north - if(istype(south)) air_master.tiles_to_update += south - if(istype(east)) air_master.tiles_to_update += east - if(istype(west)) air_master.tiles_to_update += west + if(istype(source)) air_master.tiles_to_update += source + if(istype(north)) air_master.tiles_to_update += north + if(istype(south)) air_master.tiles_to_update += south + if(istype(east)) air_master.tiles_to_update += east + if(istype(west)) air_master.tiles_to_update += west return 1 diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 82b19052d4a..1fec2e76069 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -130,10 +130,6 @@ O.show_message("\red Failure: Cannot authenticate locked on coordinates. Please reinstate coordinate matrix.") return if (istype(M, /atom/movable)) - if (istype(M, /obj/effect/dummy/chameleon)) - for(var/mob/C in M) - C << "\red The projection field prevents you from entering!" - return if(prob(5) && !accurate) //oh dear a problem, put em in deep space do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy - 5), 3), 2) else diff --git a/code/game/magic/cultist/runes.dm b/code/game/magic/cultist/runes.dm index 93b76fcece9..61dc166ea35 100644 --- a/code/game/magic/cultist/runes.dm +++ b/code/game/magic/cultist/runes.dm @@ -795,7 +795,7 @@ var/list/sacrificed = list() return fizzle() cultist.loc = src.loc cultist.lying = 1 - cultist.update_clothing() + cultist.update_lying() for(var/mob/living/carbon/human/C in orange(1,src)) if(iscultist(C)) C.say("N'ath reth sh'yro eth d'rekkathnor!") diff --git a/code/game/objects/alien/facehugger.dm b/code/game/objects/alien/facehugger.dm index 3c35491fa39..1d8dac0c224 100644 --- a/code/game/objects/alien/facehugger.dm +++ b/code/game/objects/alien/facehugger.dm @@ -150,7 +150,7 @@ var/const layer = 20 target.wear_mask = src - target.update_clothing() + target.rebuild_appearance() GoIdle() //so it doesn't jump the people that tear it off diff --git a/code/game/objects/closets/firecloset.dm b/code/game/objects/closets/firecloset.dm index be3571a83f1..c3a6ad2fbc5 100644 --- a/code/game/objects/closets/firecloset.dm +++ b/code/game/objects/closets/firecloset.dm @@ -8,7 +8,7 @@ new /obj/item/device/flashlight(src) new /obj/item/weapon/tank/oxygen/red(src) new /obj/item/weapon/extinguisher(src) - new /obj/item/clothing/head/helmet/hardhat/red(src) + new /obj/item/clothing/head/helmet/space/fire_helmet(src) /obj/structure/closet/firecloset/New() ..() @@ -17,7 +17,7 @@ new /obj/item/clothing/mask/gas(src) new /obj/item/weapon/tank/oxygen/red(src) new /obj/item/weapon/extinguisher(src) - new /obj/item/clothing/head/helmet/hardhat/red(src) + new /obj/item/clothing/head/helmet/space/fire_helmet(src) /*switch (pickweight(list("nothing" = 5, "bare-bones" = 35, "basic" = 40, "pickpocketed" = 10, "untouched" = 10))) if ("nothing") diff --git a/code/game/objects/effect_system.dm b/code/game/objects/effect_system.dm index 727b4ab103a..639b553821e 100644 --- a/code/game/objects/effect_system.dm +++ b/code/game/objects/effect_system.dm @@ -1050,38 +1050,11 @@ steam.start() -- spawns the effect var/turf/simulated/east = get_step(source,EAST) var/turf/simulated/west = get_step(source,WEST) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(north)) - if(north.parent) - air_master.groups_to_rebuild += north.parent - else - air_master.tiles_to_update += north - if(istype(south)) - if(south.parent) - air_master.groups_to_rebuild += south.parent - else - air_master.tiles_to_update += south - if(istype(east)) - if(east.parent) - air_master.groups_to_rebuild += east.parent - else - air_master.tiles_to_update += east - if(istype(west)) - if(west.parent) - air_master.groups_to_rebuild += west.parent - else - air_master.tiles_to_update += west - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(north)) air_master.tiles_to_update += north - if(istype(south)) air_master.tiles_to_update += south - if(istype(east)) air_master.tiles_to_update += east - if(istype(west)) air_master.tiles_to_update += west + if(istype(source)) air_master.tiles_to_update += source + if(istype(north)) air_master.tiles_to_update += north + if(istype(south)) air_master.tiles_to_update += south + if(istype(east)) air_master.tiles_to_update += east + if(istype(west)) air_master.tiles_to_update += west return 1 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 2065a50acd7..51a56b40af1 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -8,7 +8,6 @@ /obj/item/weapon/bedsheet/attack_self(mob/user as mob) user.drop_item() src.layer = 5 - user.update_clothing() add_fingerprint(user) return diff --git a/code/game/objects/items/clothing.dm b/code/game/objects/items/clothing.dm index 8978aefcd6c..7eb200596e4 100644 --- a/code/game/objects/items/clothing.dm +++ b/code/game/objects/items/clothing.dm @@ -398,10 +398,16 @@ THERMAL GLASSES usr << "You unbutton the labcoat." else if(src.icon_state == "fr_jacket_open") src.icon_state = "fr_jacket" - usr << "You button up the labcoat." + usr << "You button up the jacket." else if(src.icon_state == "fr_jacket") src.icon_state = "fr_jacket_open" - usr << "You unbutton the labcoat." + usr << "You unbutton the jacket." + else if(src.icon_state == "fr_sleeve_open") + src.icon_state = "fr_sleeve" + usr << "You button up the jacket." + else if(src.icon_state == "fr_sleeve") + src.icon_state = "fr_sleeve_open" + usr << "You unbutton the jacket." else usr << "Sorry! The suit you're wearing doesn't have buttons!" usr.update_clothing() diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 7ed926c801d..f976d3ee999 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -236,8 +236,10 @@ /obj/item/weapon/flamethrower/proc/ignite_turf(turf/target) //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... //Transfer 5% of current tank air contents to turf - var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.05*throw_amount/100) - air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is retarded and I could probably make it work without fucking it up like this, but there you have it. -- TLE + var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100)) + //air_transfer.toxins = air_transfer.toxins * 5 // This is me not comprehending the air system. I realize this is retarded and I could probably make it work without fucking it up like this, but there you have it. -- TLE + new/obj/liquid_fuel/flamethrower_fuel(target,air_transfer.toxins,get_dir(loc,target)) + air_transfer.toxins = 0 target.assume_air(air_transfer) //Burn it based on transfered gas //target.hotspot_expose(part4.air_contents.temperature*2,300) diff --git a/code/game/objects/mineral_doors.dm b/code/game/objects/mineral_doors.dm index 310cdfd8805..f99a8d395fe 100644 --- a/code/game/objects/mineral_doors.dm +++ b/code/game/objects/mineral_doors.dm @@ -145,38 +145,11 @@ var/turf/simulated/east = get_step(source,EAST) var/turf/simulated/west = get_step(source,WEST) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(north)) - if(north.parent) - air_master.groups_to_rebuild += north.parent - else - air_master.tiles_to_update += north - if(istype(south)) - if(south.parent) - air_master.groups_to_rebuild += south.parent - else - air_master.tiles_to_update += south - if(istype(east)) - if(east.parent) - air_master.groups_to_rebuild += east.parent - else - air_master.tiles_to_update += east - if(istype(west)) - if(west.parent) - air_master.groups_to_rebuild += west.parent - else - air_master.tiles_to_update += west - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(north)) air_master.tiles_to_update += north - if(istype(south)) air_master.tiles_to_update += south - if(istype(east)) air_master.tiles_to_update += east - if(istype(west)) air_master.tiles_to_update += west + if(istype(source)) air_master.tiles_to_update += source + if(istype(north)) air_master.tiles_to_update += north + if(istype(south)) air_master.tiles_to_update += south + if(istype(east)) air_master.tiles_to_update += east + if(istype(west)) air_master.tiles_to_update += west return 1 @@ -225,8 +198,6 @@ proc/TemperatureAct(temperature) for(var/turf/simulated/floor/target_tile in range(2,loc)) - if(target_tile.parent && target_tile.parent.group_processing) - target_tile.parent.suspend_group_processing() var/datum/gas_mixture/napalm = new @@ -305,4 +276,4 @@ isSwitchingStates = 0 Dismantle(devastated = 0) - del(src) + del(src) diff --git a/code/game/objects/stool.dm b/code/game/objects/stool.dm index 00849d24d2c..6a4a9b1caeb 100644 --- a/code/game/objects/stool.dm +++ b/code/game/objects/stool.dm @@ -184,7 +184,7 @@ if ((!( istype(M, /mob) ) || get_dist(src, user) > 1 || M.loc != src.loc || user.restrained() || usr.stat || M.buckled || istype(usr, /mob/living/silicon/pai))) return M.pixel_y = 6 - M.update_clothing() + M.update_lying() density = 1 icon_state = "up" ..() diff --git a/code/game/objects/tables_racks.dm b/code/game/objects/tables_racks.dm index 507541284b5..d9da59b841b 100644 --- a/code/game/objects/tables_racks.dm +++ b/code/game/objects/tables_racks.dm @@ -111,16 +111,6 @@ TABLE AND RACK OBJECT INTERATIONS new /obj/item/weapon/table_parts( src.loc ) src.density = 0 del(src) -/* if (user.mutations2 & mSmallsize) - step(user, get_dir(user, src)) - if (user.loc == src.loc) - user.layer = TURF_LAYER - user.update_clothing() - for(var/mob/M in viewers(user, null)) - M.show_message("[user] hides under the table!", 1) - //Foreach goto(69) - */ -//Googol never tested this shit. return @@ -189,7 +179,6 @@ TABLE AND RACK OBJECT INTERATIONS "\red You hear the nauseating crunch of bone and gristle on solid metal.") H.UpdateDamageIcon() H.updatehealth() - H.update_clothing() playsound(src.loc, 'tablehit1.ogg', 50, 1, -3) return G.affecting.loc = src.loc diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm index daafe770ef3..06d523e4128 100644 --- a/code/game/objects/weapons.dm +++ b/code/game/objects/weapons.dm @@ -30,8 +30,6 @@ for (var/turf/simulated/floor/target in range(1,src)) if(!target.blocks_air) - if(target.parent) - target.parent.suspend_group_processing() var/datum/gas_mixture/payload = new var/datum/gas/sleeping_agent/trace_gas = new @@ -48,8 +46,6 @@ /obj/effect/mine/proc/triggerplasma(obj) for (var/turf/simulated/floor/target in range(1,src)) if(!target.blocks_air) - if(target.parent) - target.parent.suspend_group_processing() var/datum/gas_mixture/payload = new diff --git a/code/game/objects/window.dm b/code/game/objects/window.dm index 662cf0321a3..b773c95c493 100644 --- a/code/game/objects/window.dm +++ b/code/game/objects/window.dm @@ -348,20 +348,8 @@ var/turf/simulated/source = loc var/turf/simulated/target = get_step(source,dir) - if(need_rebuild) - if(istype(source)) //Rebuild/update nearby group geometry - if(source.parent) - air_master.groups_to_rebuild += source.parent - else - air_master.tiles_to_update += source - if(istype(target)) - if(target.parent) - air_master.groups_to_rebuild += target.parent - else - air_master.tiles_to_update += target - else - if(istype(source)) air_master.tiles_to_update += source - if(istype(target)) air_master.tiles_to_update += target + if(istype(source)) air_master.tiles_to_update += source + if(istype(target)) air_master.tiles_to_update += target return 1 diff --git a/code/game/response_team.dm b/code/game/response_team.dm index 2ab9c5416c9..b53bbd155b2 100644 --- a/code/game/response_team.dm +++ b/code/game/response_team.dm @@ -27,7 +27,7 @@ client/verb/JoinResponseTeam() new_commando.mind.key = usr.key new_commando.key = usr.key - new_commando << "\blue You are [!leader_selected?"a member":"the LEADER"] of an armed response team in CentComm's service. Something went down on [station_name()] and they're now on code red. Go in there and fix the problem." + new_commando << "\blue You are [!leader_selected?"a member":"the LEADER"] of an Emergency Response Team under CentComm's service. There is a code red alert on [station_name()], you are tasked to go and fix the problem." new_commando << "You should first gear up and discuss a plan with your team. More members may be joining, don't move out before you're ready." del(L) @@ -81,7 +81,7 @@ proc/trigger_armed_response_team(var/force = 0) if(!nuke) nuke = locate() in world var/obj/item/weapon/paper/P = new - P.info = "Your orders, Commander, are to use all necessary tools given to return the station to a survivable condition.
To this end, you have been provided with the best tools we can give in the three areas of Medical Engineering and Security. The nuclear authorization code is: [ nuke ? nuke.r_code : "AHH, THE NUKE IS GONE!"]. Be warned, if you detonate this without good reason, we will hold you to account for damages. Memorise this code, and then burn this message." + P.info = "Your orders, Commander, are to use all means necessary to return the station to a survivable condition.
To this end, you have been provided with the best tools we can give in the three areas of Medicine, Engineering, and Security. The nuclear authorization code is: [ nuke ? nuke.r_code : "AHH, THE NUKE IS GONE!"]. Be warned, if you detonate this without good reason, we will hold you to account for damages. Memorise this code, and then burn this message." P.name = "Emergency Nuclear Code, and ERT Orders" for (var/obj/effect/landmark/A in world) if (A.name == "nukecode") @@ -170,9 +170,7 @@ proc/trigger_armed_response_team(var/force = 0) M.gender = MALE else M.gender = FEMALE - M.update_body() - M.update_face() - M.update_clothing() + M.rebuild_appearance() M.real_name = commando_name M.name = commando_name diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index bc32786d082..f7eec938d0e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -145,7 +145,7 @@ verbs += /client/proc/voting verbs += /client/proc/hide_verbs verbs += /client/proc/general_report - verbs += /client/proc/air_report + //verbs += /client/proc/air_report verbs += /client/proc/deadmin_self //verbs += /client/proc/cmd_admin_prison --Merged with player panel //verbs += /obj/admins/proc/unprison --Merged with player panel @@ -249,7 +249,7 @@ // verbs += /client/proc/mapload verbs += /client/proc/check_words verbs += /client/proc/drop_bomb - verbs += /client/proc/kill_airgroup + //verbs += /client/proc/kill_airgroup //verbs += /client/proc/cmd_admin_drop_everything --Merged with view variables verbs += /client/proc/make_sound verbs += /client/proc/play_local_sound @@ -280,7 +280,7 @@ verbs += /client/proc/Force_Event_admin verbs += /client/proc/radioalert verbs += /client/proc/CarbonCopy - verbs += /client/proc/jump_to_dead_group + //verbs += /client/proc/jump_to_dead_group else return //Game Master @@ -405,7 +405,7 @@ verbs -= /client/proc/voting verbs -= /client/proc/hide_verbs verbs -= /client/proc/general_report - verbs -= /client/proc/air_report + //verbs -= /client/proc/air_report verbs -= /client/proc/cmd_admin_say verbs -= /client/proc/cmd_admin_gib_self verbs -= /client/proc/restartcontroller @@ -456,13 +456,13 @@ //verbs -= /obj/admins/proc/unprison --Merged with player panel //verbs -= /client/proc/cmd_switch_radio --removed because tcommsat is staying verbs -= /client/proc/togglebuildmodeself - verbs -= /client/proc/kill_airgroup + //verbs -= /client/proc/kill_airgroup verbs -= /client/proc/debug_master_controller verbs -= /client/proc/make_tajaran verbs -= /client/proc/admin_deny_shuttle verbs -= /client/proc/cmd_admin_christmas verbs -= /client/proc/editappear - verbs -= /client/proc/jump_to_dead_group + //verbs -= /client/proc/jump_to_dead_group verbs -= /client/proc/playernotes verbs -= /obj/admins/proc/show_skills verbs -= /client/proc/enable_debug_verbs @@ -867,8 +867,7 @@ return src.admin_invis =! src.admin_invis if(src.mob) - var/mob/m = src.mob//probably don't need this cast, but I'm too lazy to check if /client.mob is of type /mob or not - m.update_clothing() + mob.rebuild_appearance() log_admin("[key_name(usr)] has turned their invisibility [src.admin_invis ? "ON" : "OFF"]") message_admins("[key_name_admin(usr)] has turned their invisibility [src.admin_invis ? "ON" : "OFF"]", 1) @@ -975,9 +974,7 @@ M.gender = MALE else M.gender = FEMALE - M.update_body() - M.update_face() - M.update_clothing() + M.rebuild_appearance() M.check_dna(M) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index fa68b836d90..dc8c6371f53 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -22,7 +22,7 @@ usr << browse(output,"window=generalreport") //feedback_add_details("admin_verb","SGR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - air_report() +/* air_report() set category = "Debug" set name = "Show Air Report" @@ -83,7 +83,7 @@ for(var/datum/gas/trace_gas in GM.trace_gases) usr << "[trace_gas.type]: [trace_gas.moles]" //feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - +*/ fix_next_move() set category = "Debug" set name = "Unfreeze Everyone" @@ -183,7 +183,7 @@ //feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - jump_to_dead_group() + /*jump_to_dead_group() set name = "Jump to dead group" set category = "Debug" if(!holder) @@ -221,7 +221,7 @@ else usr << "Local airgroup is unsimulated!" //feedback_add_details("admin_verb","KLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - +*/ tension_report() set category = "Debug" diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index a899f823ddf..f319ebe5019 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -130,14 +130,14 @@ var/intercom_range_display_status = 0 src.verbs += /client/proc/camera_view //-errorage src.verbs += /client/proc/sec_camera_report //-errorage src.verbs += /client/proc/intercom_view //-errorage - src.verbs += /client/proc/air_status //Air things + //src.verbs += /client/proc/air_status //Air things src.verbs += /client/proc/Cell //More air things src.verbs += /client/proc/atmosscan //check plumbing src.verbs += /client/proc/powerdebug //check power src.verbs += /client/proc/count_objects_on_z_level src.verbs += /client/proc/count_objects_all src.verbs += /client/proc/cmd_assume_direct_control //-errorage - src.verbs += /client/proc/jump_to_dead_group + //src.verbs += /client/proc/jump_to_dead_group src.verbs += /client/proc/startSinglo src.verbs += /client/proc/ticklag //allows you to set the ticklag. src.verbs += /client/proc/cmd_admin_grantfullaccess diff --git a/code/modules/chemical/Chemistry-Reagents.dm b/code/modules/chemical/Chemistry-Reagents.dm index 86d28577e4c..c94cd145e75 100644 --- a/code/modules/chemical/Chemistry-Reagents.dm +++ b/code/modules/chemical/Chemistry-Reagents.dm @@ -261,23 +261,25 @@ datum for(var/mob/living/carbon/metroid/M in T) M.adjustToxLoss(rand(15,20)) - var/hotspot = (locate(/obj/effect/hotspot) in T) + var/hotspot = (locate(/obj/fire) in T) if(hotspot && !istype(T, /turf/space)) var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles ) lowertemp.temperature = max( min(lowertemp.temperature-2000,lowertemp.temperature / 2) ,0) lowertemp.react() T.assume_air(lowertemp) + T.apply_fire_protection() del(hotspot) return reaction_obj(var/obj/O, var/volume) src = null var/turf/T = get_turf(O) - var/hotspot = (locate(/obj/effect/hotspot) in T) + var/hotspot = (locate(/obj/fire) in T) if(hotspot && !istype(T, /turf/space)) var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles ) lowertemp.temperature = max( min(lowertemp.temperature-2000,lowertemp.temperature / 2) ,0) lowertemp.react() T.assume_air(lowertemp) + T.apply_fire_protection() del(hotspot) if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/monkeycube)) var/obj/item/weapon/reagent_containers/food/snacks/monkeycube/cube = O @@ -2142,12 +2144,13 @@ datum if(T.wet_overlay) T.overlays -= T.wet_overlay T.wet_overlay = null - var/hotspot = (locate(/obj/effect/hotspot) in T) + var/hotspot = (locate(/obj/fire) in T) if(hotspot) var/datum/gas_mixture/lowertemp = T.remove_air( T:air:total_moles ) lowertemp.temperature = max( min(lowertemp.temperature-2000,lowertemp.temperature / 2) ,0) lowertemp.react() T.assume_air(lowertemp) + T.apply_fire_protection() del(hotspot) enzyme diff --git a/code/modules/chemical/Chemistry-Recipes.dm b/code/modules/chemical/Chemistry-Recipes.dm index ad825ca068d..af599eced07 100644 --- a/code/modules/chemical/Chemistry-Recipes.dm +++ b/code/modules/chemical/Chemistry-Recipes.dm @@ -350,8 +350,6 @@ datum on_reaction(var/datum/reagents/holder, var/created_volume) var/turf/location = get_turf(holder.my_atom.loc) for(var/turf/simulated/floor/target_tile in range(0,location)) - if(target_tile.parent && target_tile.parent.group_processing) - target_tile.parent.suspend_group_processing() var/datum/gas_mixture/napalm = new diff --git a/code/modules/chemical/Chemistry-Tools.dm b/code/modules/chemical/Chemistry-Tools.dm index 318fbcf71f1..849b4b6041f 100644 --- a/code/modules/chemical/Chemistry-Tools.dm +++ b/code/modules/chemical/Chemistry-Tools.dm @@ -1588,6 +1588,7 @@ del(src) spawn(5) user.update_clothing() + playsound(M.loc,'eatfood.ogg', rand(10,50), 1) return 1 else if(istype(M, /mob/living/simple_animal/livestock)) @@ -2916,6 +2917,15 @@ src.pixel_x = rand(-10.0, 10) src.pixel_y = rand(-10.0, 10) +/obj/item/weapon/reagent_containers/food/drinks/waterbottle + name = "water bottle" + desc = "Straight from the ice lakes of Mars!" + icon_state = "waterbottle" + New() + ..() + reagents.add_reagent("water", 30) + src.pixel_x = rand(-10.0, 10) + src.pixel_y = rand(-10.0, 10) /obj/item/weapon/reagent_containers/food/drinks/sillycup name = "Paper Cup" diff --git a/code/modules/clothing/suits/fire.dm b/code/modules/clothing/suits/fire.dm index 42dc200379a..7f03617caa5 100644 --- a/code/modules/clothing/suits/fire.dm +++ b/code/modules/clothing/suits/fire.dm @@ -28,6 +28,18 @@ protective_temperature = 10000 slowdown = 1.5 +/obj/item/clothing/head/helmet/space/fire_helmet + name = "Fire Helmet" + desc = "A helmet designed to protect against fires in sealed areas, which often create extreme pressures." + flags = FPRINT | TABLEPASS | HEADSPACE | HEADCOVERSEYES | BLOCKHAIR + see_face = 0.0 + permeability_coefficient = 0.01 + heat_transfer_coefficient = 0.01 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 50) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + icon_state = "hazmat_firered" + item_state = "hazhat_firered" + /obj/item/clothing/head/radiation diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 5a2a9e2a5d5..f1ebd585db9 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -48,5 +48,11 @@ /obj/item/clothing/suit/storage/labcoat/fr_jacket name = "first responder jacket" - desc = "\"The first moments are the most crucial.\"" + desc = "A high-visibility jacket worn by medical first responders." icon_state = "fr_jacket_open" + item_state = "fr_jacket" + +/obj/item/clothing/suit/storage/labcoat/fr_jacket/sleeve + name = "first responder jacket" + desc = "A high-visibility jacket worn by medical first responders. Has rolled up sleeves." + icon_state = "fr_sleeve_open" diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm index bd80e25149c..c9fcec5e234 100644 --- a/code/modules/mining/mine_turfs.dm +++ b/code/modules/mining/mine_turfs.dm @@ -316,6 +316,8 @@ icon_plating = "asteroid_dug" icon_state = "asteroid_dug" return + else + return else ..(W,user) if ((istype(W,/obj/item/weapon/pickaxe/drill))) @@ -337,6 +339,9 @@ dug = 1 icon_plating = "asteroid_dug" icon_state = "asteroid_dug" + return + else + return else ..(W,user) @@ -359,6 +364,9 @@ dug = 1 icon_plating = "asteroid_dug" icon_state = "asteroid_dug" + return + else + return else ..(W,user) diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm index 64274d21912..ceb8536feab 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm @@ -13,7 +13,7 @@ src.resting_icon = new /icon('alien.dmi', "aliend_sleep") src.running_icon = new /icon('alien.dmi', "aliend_running") src.icon = src.stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" //Drones use the same base as generic humanoids. diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index 0c156b04320..953dd4a9814 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -12,7 +12,7 @@ src.resting_icon = new /icon('alien.dmi', "alienh_sleep") src.running_icon = new /icon('alien.dmi', "alienh_running") src.icon = src.stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm index 3c4e0a5a17d..b0222d60c9e 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm @@ -12,7 +12,7 @@ src.resting_icon = new /icon('alien.dmi', "aliens_sleep") src.running_icon = new /icon('alien.dmi', "aliens_running") src.icon = src.stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" diff --git a/code/modules/mob/living/carbon/alien/humanoid/hud.dm b/code/modules/mob/living/carbon/alien/humanoid/hud.dm index 67223c85fb2..7b2accc6227 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/hud.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/hud.dm @@ -146,7 +146,7 @@ using.name = "drop" using.icon = 'screen1_alien.dmi' using.icon_state = "act_drop" - using.screen_loc = ui_dropbutton + using.screen_loc = ui_dropbutton_old using.layer = 19 src.adding += using diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 0ded1e87667..397b68839a5 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -13,7 +13,7 @@ resting_icon = new /icon('alien.dmi', "alienh_sleep") running_icon = new /icon('alien.dmi', "alienh_running") icon = stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" ..() diff --git a/code/modules/mob/living/carbon/alien/humanoid/life.dm b/code/modules/mob/living/carbon/alien/humanoid/life.dm index edef1ab43a4..7a386ef417f 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/life.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/life.dm @@ -183,7 +183,7 @@ if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) return var/datum/gas_mixture/environment = loc.return_air() - var/datum/air_group/breath + var/datum/gas_mixture/breath // HACK NEED CHANGING LATER if(src.health < 0) src.losebreath++ diff --git a/code/modules/mob/living/carbon/alien/humanoid/login.dm b/code/modules/mob/living/carbon/alien/humanoid/login.dm index 52359b7e59b..188785a9bbe 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/login.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/login.dm @@ -1,7 +1,7 @@ /mob/living/carbon/alien/humanoid/Login() ..() - update_clothing() + rebuild_appearance() if (!isturf(src.loc)) src.client.eye = src.loc diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 2924f5b9b89..a4a620eb91b 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -15,7 +15,7 @@ src.resting_icon = new /icon('alien.dmi', "queen_sleep") src.running_icon = new /icon('alien.dmi', "queen_running") src.icon = src.stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" diff --git a/code/modules/mob/living/carbon/alien/larva/hud.dm b/code/modules/mob/living/carbon/alien/larva/hud.dm index 9da16a0b3bf..2614a992bf2 100644 --- a/code/modules/mob/living/carbon/alien/larva/hud.dm +++ b/code/modules/mob/living/carbon/alien/larva/hud.dm @@ -56,7 +56,7 @@ using.dir = SOUTHWEST using.icon = 'screen1_alien.dmi' using.icon_state = (mymob.m_intent == "run" ? "running" : "walking") - using.screen_loc = ui_movi_old + using.screen_loc = ui_acti using.layer = 20 src.adding += using move_intent = using diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index fd53c50f56d..76406ea8a87 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -7,7 +7,7 @@ name = text("alien larva ([rand(1, 1000)])") real_name = name spawn (1) - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" // spawn(1200) grow() Grow after 120 seconds -- TLE Commented out because life.dm has better version -- Urist ..() diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm index 23e9a6ca067..b35b9735434 100644 --- a/code/modules/mob/living/carbon/alien/larva/life.dm +++ b/code/modules/mob/living/carbon/alien/larva/life.dm @@ -158,7 +158,7 @@ if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) return var/datum/gas_mixture/environment = loc.return_air() - var/datum/air_group/breath + var/datum/gas_mixture/breath // HACK NEED CHANGING LATER if(health < 0) losebreath++ diff --git a/code/modules/mob/living/carbon/alien/larva/login.dm b/code/modules/mob/living/carbon/alien/larva/login.dm index e23cb0c122e..132915a70fa 100644 --- a/code/modules/mob/living/carbon/alien/larva/login.dm +++ b/code/modules/mob/living/carbon/alien/larva/login.dm @@ -1,7 +1,7 @@ /mob/living/carbon/alien/larva/Login() ..() - update_clothing() + rebuild_appearance() if (!isturf(src.loc)) src.client.eye = src.loc diff --git a/code/modules/mob/living/carbon/alien/special/_main.dm b/code/modules/mob/living/carbon/alien/special/_main.dm index 3d06fe82ac3..eb5594f3c9e 100644 --- a/code/modules/mob/living/carbon/alien/special/_main.dm +++ b/code/modules/mob/living/carbon/alien/special/_main.dm @@ -25,7 +25,7 @@ remove_special_verbs() - update_clothing() + rebuild_appearance() death(gibbed) ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 26767b93681..984be44a3ec 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -133,14 +133,13 @@ stand_icon = new /icon('human.dmi', "body_[g]_s") lying_icon = new /icon('human.dmi', "body_[g]_l") icon = stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" spawn(10) // Failsafe for.. weirdness. - update_clothing() - update_body() + rebuild_appearance() vessel = new/datum/reagents(600) vessel.my_atom = src @@ -151,8 +150,7 @@ ..() spawn(5) // Failsafe for.. weirdness. - update_clothing() - update_body() + rebuild_appearance() /*var/known_languages = list() known_languages.Add("english")*/ @@ -861,30 +859,38 @@ M.UpdateFeed(src) return -/mob/living/carbon/human/update_clothing() - ..() - - if (monkeyizing) - return - - overlays = null +/mob/living/carbon/human/proc/rebuild_body_overlays() + // This proc REBUILDS the body overlays. These are the overlays displayed under the clothing. + // Builds the body overlays both for lying and standing states. + // + // This is necessary whenever something about the mob's body appearance changes, for example: + // - When the mutantrace changes + // - When a visible wound is added to the mob's skin + // - When the mob loses a limb + body_overlays_standing.Cut() + body_overlays_lying.Cut() if (mutations & HULK) - overlays += image("icon" = 'genetics.dmi', "icon_state" = "hulk_[gender][!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "hulk_[gender]_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "hulk_[gender]_l") if (mutations & COLD_RESISTANCE) - overlays += image("icon" = 'genetics.dmi', "icon_state" = "fire[!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "fire_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "fire_l") if (mutations & TK) - overlays += image("icon" = 'genetics.dmi', "icon_state" = "telekinesishead[!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "telekinesishead_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "telekinesishead_l") if (mutations & LASER) - overlays += image("icon" = 'genetics.dmi', "icon_state" = "lasereyes[!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "lasereyes_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "lasereyes_l") if (mutantrace) switch(mutantrace) if("golem","metroid") - overlays += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace][!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_l") if(face_standing) del(face_standing) if(face_lying) @@ -894,7 +900,8 @@ if(lying_icon) del(lying_icon) if("lizard") - overlays += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_[gender][!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_[gender]_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_[gender]_l") if(face_standing) del(face_standing) if(face_lying) @@ -905,9 +912,11 @@ del(lying_icon) if("plant") if(stat != 2) //if not dead, that is - overlays += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_[gender][!lying ? "_s" : "_l"]") + body_overlays_standing += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_[gender]_s") + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_[gender]_l") else - overlays += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_d") + // not sure why this doesn't need separate lying/standing states + body_overlays_lying += image("icon" = 'genetics.dmi', "icon_state" = "[mutantrace]_d") if(face_standing) del(face_standing) if(face_lying) @@ -917,64 +926,54 @@ if(lying_icon) del(lying_icon) else + // TODO: rewrite update_face() and update_body() if necessary if(!face_standing || !face_lying) update_face() if(!stand_icon || !lying_icon) update_body() - if(buckled) - if(istype(buckled, /obj/structure/stool/bed/chair)) - lying = 0 - else - lying = 1 + // Other procs(probably update_face() and update_body() ) also rebuild their own + // kinds of overlay lists. Think about merging those procs into this proc. + body_overlays_lying += body_lying + body_overlays_standing += body_standing - //if (zone_sel) - // zone_sel.overlays = null - // zone_sel.overlays += body_standing - // zone_sel.overlays += image("icon" = 'zone_sel.dmi', "icon_state" = text("[]", zone_sel.selecting)) + // face_lying and face_standing are the face icons, not a flag + body_overlays_lying += face_standing // yes, the vars are named wrong, I didn't do it -- CIB + body_overlays_standing += face_lying - if (lying) - icon = lying_icon - overlays += body_lying - if (face_lying) - overlays += face_lying - else - icon = stand_icon +/mob/living/carbon/human/proc/rebuild_clothing_overlays() + // This proc REBUILDS the clothing overlays entirely. + // This is necessary whenever clothing changes, or when the mob switches from laying + // into standing state or vice verca. - overlays += body_standing + // This should possibly be split into two categories: + // - "under", which is stuff that is worn at the lowest clothing layer and doesn't change often, + // such as jumpsuits, gloves, headset, etc. + // - "over", which is stuff that is worn at the highest clothing layer and changes very often, + // such as stuff in the user's hand, space suits, helmets, etc. - if (face_standing) - overlays += face_standing + // Delete all items from the overlay lists + clothing_overlays.Cut() // Uniform if(w_uniform) + // What is screen_loc stuff doing here?! w_uniform.screen_loc = ui_iclothing - + if(istype(w_uniform, /obj/item/clothing/under)) var/t1 = w_uniform.color if (!t1) t1 = icon_state - overlays += image("icon" = 'uniform.dmi', "icon_state" = text("[][]",t1, (!(lying) ? "_s" : "_l")), "layer" = MOB_LAYER) + clothing_overlays += image("icon" = 'uniform.dmi', "icon_state" = text("[][]",t1, (!(lying) ? "_s" : "_l")), "layer" = MOB_LAYER) if (w_uniform.blood_DNA) var/icon/stain_icon = icon('blood.dmi', "uniformblood[!lying ? "" : "2"]") - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) - - else // Automatically drop anything in store / id / belt if you're not wearing a uniform. - for (var/obj/item/thing in list(r_store, l_store, wear_id, belt)) - if (thing) - u_equip(thing) - if (client) - client.screen -= thing - if (thing) - thing.loc = loc - thing.dropped(src) - thing.layer = initial(thing.layer) + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) if (wear_id) if(wear_id.over_jumpsuit) - overlays += image("icon" = 'mob.dmi', "icon_state" = "id[!lying ? null : "2"]", "layer" = MOB_LAYER) + clothing_overlays += image("icon" = 'mob.dmi', "icon_state" = "id[!lying ? null : "2"]", "layer" = MOB_LAYER) if (client) client.screen -= hud_used.intents @@ -997,34 +996,34 @@ gloves_icon.Blend(new /icon('limb_mask.dmi', "right_[lying?"l":"s"]"), ICON_MULTIPLY) if(ro.destroyed) gloves_icon.Blend(new /icon('limb_mask.dmi', "left_[lying?"l":"s"]"), ICON_MULTIPLY) - overlays += image(gloves_icon, "layer" = MOB_LAYER) + clothing_overlays += image(gloves_icon, "layer" = MOB_LAYER) if (gloves.blood_DNA) var/icon/stain_icon = icon('blood.dmi', "bloodyhands[!lying ? "" : "2"]") if(lo.destroyed) stain_icon.Blend(new /icon('limb_mask.dmi', "right_[lying?"l":"s"]"), ICON_MULTIPLY) else if(ro.destroyed) stain_icon.Blend(new /icon('limb_mask.dmi', "left_[lying?"l":"s"]"), ICON_MULTIPLY) - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) else if (blood_DNA) var/icon/stain_icon = icon('blood.dmi', "bloodyhands[!lying ? "" : "2"]") if(lo.destroyed) stain_icon.Blend(new /icon('limb_mask.dmi', "right_[lying?"l":"s"]"), ICON_MULTIPLY) else if(ro.destroyed) stain_icon.Blend(new /icon('limb_mask.dmi', "left_[lying?"l":"s"]"), ICON_MULTIPLY) - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) // Glasses if (glasses) var/t1 = glasses.icon_state - overlays += image("icon" = 'eyes.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + clothing_overlays += image("icon" = 'eyes.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) // Ears if (l_ear) var/t1 = l_ear.icon_state - overlays += image("icon" = 'ears.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + clothing_overlays += image("icon" = 'ears.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) if (r_ear) var/t1 = r_ear.icon_state - overlays += image("icon" = 'ears.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + clothing_overlays += image("icon" = 'ears.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) // Shoes lo = organs["l_foot"] @@ -1036,26 +1035,156 @@ shoes_icon.Blend(new /icon('limb_mask.dmi', "right[lying?"_l":""]"), ICON_MULTIPLY) else if(ro.destroyed && !lying) shoes_icon.Blend(new /icon('limb_mask.dmi', "left[lying?"_l":""]"), ICON_MULTIPLY) - overlays += image(shoes_icon, "layer" = MOB_LAYER) + clothing_overlays += image(shoes_icon, "layer" = MOB_LAYER) if (shoes.blood_DNA) var/icon/stain_icon = icon('blood.dmi', "shoeblood[!lying ? "" : "2"]") if(lo.destroyed) stain_icon.Blend(new /icon('limb_mask.dmi', "right_[lying?"l":"s"]"), ICON_MULTIPLY) else if(ro.destroyed) stain_icon.Blend(new /icon('limb_mask.dmi', "left_[lying?"l":"s"]"), ICON_MULTIPLY) - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) // Radio - -/* if (w_radio) - overlays += image("icon" = 'ears.dmi', "icon_state" = "headset[!lying ? "" : "2"]", "layer" = MOB_LAYER) */ + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) // Radio if (s_store) var/t1 = s_store.item_state if (!t1) t1 = s_store.icon_state if(!istype(wear_suit, /obj/item/clothing/suit/storage/armoredundersuit)) - overlays += image("icon" = 'belt_mirror.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + clothing_overlays += image("icon" = 'belt_mirror.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) s_store.screen_loc = ui_sstore1 + + if (wear_suit) + if (istype(wear_suit, /obj/item/clothing/suit)) + var/t1 = wear_suit.icon_state + clothing_overlays += image("icon" = 'suit.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + + if (wear_suit) + if (wear_suit.blood_DNA) + var/icon/stain_icon = null + if (istype(wear_suit, /obj/item/clothing/suit/armor/vest || /obj/item/clothing/suit/storage/wcoat)) + stain_icon = icon('blood.dmi', "armorblood[!lying ? "" : "2"]") + else if (istype(wear_suit, /obj/item/clothing/suit/storage/det_suit || /obj/item/clothing/suit/storage/labcoat)) + stain_icon = icon('blood.dmi', "coatblood[!lying ? "" : "2"]") + else + stain_icon = icon('blood.dmi', "suitblood[!lying ? "" : "2"]") + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) + wear_suit.screen_loc = ui_oclothing + + if (istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) + if (handcuffed) + handcuffed.loc = loc + handcuffed.layer = initial(handcuffed.layer) + handcuffed = null + if ((l_hand || r_hand)) + // I don't know what this does, but it looks horrible + var/h = hand + hand = 1 + drop_item() + hand = 0 + drop_item() + hand = h + + if (wear_mask) + if (istype(wear_mask, /obj/item/clothing/mask)) + var/t1 = wear_mask.icon_state + clothing_overlays += image("icon" = 'mask.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + if (!istype(wear_mask, /obj/item/clothing/mask/cigarette)) + if (wear_mask.blood_DNA) + var/icon/stain_icon = icon('blood.dmi', "maskblood[!lying ? "" : "2"]") + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) + wear_mask.screen_loc = ui_mask + + // Head + if (head) + var/t1 = head.icon_state + var/icon/head_icon = icon('head.dmi', text("[][]", t1, (!( lying ) ? null : "2"))) + if(istype(head,/obj/item/clothing/head/kitty)) + head_icon = (( lying ) ? head:mob2 : head:mob) + clothing_overlays += image("icon" = head_icon, "layer" = MOB_LAYER) + if(gimmick_hat) + clothing_overlays += image("icon" = icon('gimmick_head.dmi', "[gimmick_hat][!lying ? "" : "2"]"), "layer" = MOB_LAYER) + if (head.blood_DNA) + var/icon/stain_icon = icon('blood.dmi', "helmetblood[!lying ? "" : "2"]") + clothing_overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) + head.screen_loc = ui_head + + // Belt + if (belt) + var/t1 = belt.item_state + if (!t1) + t1 = belt.icon_state + clothing_overlays += image("icon" = 'belt.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + belt.screen_loc = ui_belt + + if (wear_id) + wear_id.screen_loc = ui_id + + if (l_store) + l_store.screen_loc = ui_storage1 + + if (r_store) + r_store.screen_loc = ui_storage2 + + if (back) + var/t1 = back.icon_state + clothing_overlays += image("icon" = 'back.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) + back.screen_loc = ui_back + + if(client) hud_used.other_update() //Update the screenloc of the items on the 'other' inventory bar + //to hide / show them. + // WHAT IS THIS DOING IN UPDATE_CLOTHING(), AHHHHHHHHHHHHH + if (handcuffed) + pulling = null + var/h1 = handcuffed.icon_state + if (!lying) + clothing_overlays += image("icon" = 'mob.dmi', "icon_state" = "[h1]1", "layer" = MOB_LAYER) + else + clothing_overlays += image("icon" = 'mob.dmi', "icon_state" = "[h1]2", "layer" = MOB_LAYER) + + + if (r_hand) + clothing_overlays += image("icon" = 'items_righthand.dmi', "icon_state" = r_hand.item_state ? r_hand.item_state : r_hand.icon_state, "layer" = MOB_LAYER+1) + + r_hand.screen_loc = ui_rhand + + if (l_hand) + clothing_overlays += image("icon" = 'items_lefthand.dmi', "icon_state" = l_hand.item_state ? l_hand.item_state : l_hand.icon_state, "layer" = MOB_LAYER+1) + + l_hand.screen_loc = ui_lhand + + var/shielded = 0 + for (var/obj/item/weapon/cloaking_device/S in src) + if (S.active) + shielded = 2 + break + + if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)&&wear_suit:s_active) + shielded = 3 + + if(shielded == 1) + clothing_overlays += image("icon" = 'effects.dmi', "icon_state" = "shield", "layer" = MOB_LAYER+1) + + + if(client && client.admin_invis) + invisibility = 100 + else if (shielded == 2) + invisibility = 2 + else + invisibility = 0 + if(targeted_by && target_locked) + clothing_overlays += target_locked + else if(targeted_by) + target_locked = new /obj/effect/target_locked(src) + clothing_overlays += target_locked + else if(!targeted_by && target_locked) + del(target_locked) + + last_b_state = stat + +/mob/living/carbon/human/proc/misc_clothing_updates() + // Temporary proc to shove stuff in that was put into update_clothing() + // for questionable reasons + if (client) if (i_select) if (intent) @@ -1076,167 +1205,75 @@ else m_select.screen_loc = null - - if (wear_suit) - if (istype(wear_suit, /obj/item/clothing/suit)) - var/t1 = wear_suit.icon_state - overlays += image("icon" = 'suit.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) - - if (wear_suit) - if (wear_suit.blood_DNA) - var/icon/stain_icon = null - if (istype(wear_suit, /obj/item/clothing/suit/armor/vest || /obj/item/clothing/suit/storage/wcoat)) - stain_icon = icon('blood.dmi', "armorblood[!lying ? "" : "2"]") - else if (istype(wear_suit, /obj/item/clothing/suit/storage/det_suit || /obj/item/clothing/suit/storage/labcoat)) - stain_icon = icon('blood.dmi', "coatblood[!lying ? "" : "2"]") - else - stain_icon = icon('blood.dmi', "suitblood[!lying ? "" : "2"]") - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) - wear_suit.screen_loc = ui_oclothing - - if (istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) - if (handcuffed) - handcuffed.loc = loc - handcuffed.layer = initial(handcuffed.layer) - handcuffed = null - if ((l_hand || r_hand)) - var/h = hand - hand = 1 - drop_item() - hand = 0 - drop_item() - hand = h - - - if (lying) - if (face_lying) - overlays += face_lying - else - if (face_standing) - overlays += face_standing - - if (wear_mask) - if (istype(wear_mask, /obj/item/clothing/mask)) - var/t1 = wear_mask.icon_state - overlays += image("icon" = 'mask.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) - if (!istype(wear_mask, /obj/item/clothing/mask/cigarette)) - if (wear_mask.blood_DNA) - var/icon/stain_icon = icon('blood.dmi', "maskblood[!lying ? "" : "2"]") - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) - wear_mask.screen_loc = ui_mask - - // Head - if (head) - var/t1 = head.icon_state - var/icon/head_icon = icon('head.dmi', text("[][]", t1, (!( lying ) ? null : "2"))) - if(istype(head,/obj/item/clothing/head/kitty)) - head_icon = (( lying ) ? head:mob2 : head:mob) - overlays += image("icon" = head_icon, "layer" = MOB_LAYER) - if(gimmick_hat) - overlays += image("icon" = icon('gimmick_head.dmi', "[gimmick_hat][!lying ? "" : "2"]"), "layer" = MOB_LAYER) - if (head.blood_DNA) - var/icon/stain_icon = icon('blood.dmi', "helmetblood[!lying ? "" : "2"]") - overlays += image("icon" = stain_icon, "layer" = MOB_LAYER) - head.screen_loc = ui_head - - // Belt - if (belt) - var/t1 = belt.item_state - if (!t1) - t1 = belt.icon_state - overlays += image("icon" = 'belt.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) - belt.screen_loc = ui_belt - - - name = get_visible_name() - - if (wear_id) - wear_id.screen_loc = ui_id - - if (l_store) - l_store.screen_loc = ui_storage1 - - if (r_store) - r_store.screen_loc = ui_storage2 - - if (back) - var/t1 = back.icon_state - overlays += image("icon" = 'back.dmi', "icon_state" = text("[][]", t1, (!( lying ) ? null : "2")), "layer" = MOB_LAYER) - back.screen_loc = ui_back - - if(client) hud_used.other_update() //Update the screenloc of the items on the 'other' inventory bar - //to hide / show them. - if (handcuffed) - pulling = null - var/h1 = handcuffed.icon_state - if (!lying) - overlays += image("icon" = 'mob.dmi', "icon_state" = "[h1]1", "layer" = MOB_LAYER) - else - overlays += image("icon" = 'mob.dmi', "icon_state" = "[h1]2", "layer" = MOB_LAYER) - + // Probably a lazy way to make sure all items are on the screen exactly once if (client) client.screen -= contents client.screen += contents - if (r_hand) - overlays += image("icon" = 'items_righthand.dmi', "icon_state" = r_hand.item_state ? r_hand.item_state : r_hand.icon_state, "layer" = MOB_LAYER+1) +/mob/living/carbon/human/rebuild_appearance() + // Lazy method: Just rebuild everything. + // This can be called when the mob is created, but on other occasions, rebuild_body_overlays(), + // rebuild_clothing_overlays() etc. should be called individually. - r_hand.screen_loc = ui_rhand + handle_clothing() // nonvisual stuff + misc_clothing_updates() // silly stuff - if (l_hand) - overlays += image("icon" = 'items_lefthand.dmi', "icon_state" = l_hand.item_state ? l_hand.item_state : l_hand.icon_state, "layer" = MOB_LAYER+1) + rebuild_body_overlays() // rebuild the body itself, that is mutant race traits, hair, limbs, etc. + rebuild_clothing_overlays() // rebuild the entire clothing - l_hand.screen_loc = ui_lhand + update_overlays_from_lists() - var/shielded = 0 - for (var/obj/item/weapon/cloaking_device/S in src) - if (S.active) - shielded = 2 - break +/mob/living/carbon/human/proc/update_overlays_from_lists() + // This will simply update the overlays without actually + // rebuilding anything. Can be called often without much + // effect on performance. - if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)&&wear_suit:s_active) - shielded = 3 - switch(shielded) - if(1) - overlays += image("icon" = 'effects.dmi', "icon_state" = "shield", "layer" = MOB_LAYER+1) - if(2) - invisibility = 2 - //New stealth. Hopefully doesn't lag too much. /N - if(istype(loc, /turf))//If they are standing on a turf. - AddCamoOverlay(loc)//Overlay camo. - if(3) - if(istype(loc, /turf)) - //Ninjas may flick into view once in a while if they are stealthed. - if(prob(90)) - NinjaStealthActive(loc) - else - NinjaStealthMalf() - else - invisibility = 0 + // Update the actual overlays from our prebuilt lists + overlays = null - if(client && client.admin_invis) - invisibility = 100 - else if (shielded == 2) - invisibility = 2 - else - invisibility = 0 - if(targeted_by && target_locked) - overlays += target_locked - else if(targeted_by) - target_locked = new /obj/effect/target_locked(src) - overlays += target_locked - else if(!targeted_by && target_locked) - del(target_locked) + if (monkeyizing) // while monkeyizing we do not wish to display any overlays + return -/* - for (var/mob/M in viewers(1, src))//For the love of god DO NOT REFRESH EVERY SECOND - Mport - if ((M.client && M.machine == src)) - spawn (0) - show_inv(M) - return -*/ - last_b_state = stat + if(lying) + icon = lying_icon + overlays += body_overlays_standing + + if(!lying) + icon = stand_icon + + overlays += body_overlays_lying + + + overlays += clothing_overlays + +/mob/living/carbon/human/update_body_appearance() + // Should be called whenever something about the body appearance itself changes. + + rebuild_body_overlays() + update_overlays_from_lists() + +/mob/living/carbon/human/update_lying() + // Should be called whenever something about the lying status of the mob might have changed. + + if(visual_lying != lying) + visual_lying = lying + rebuild_clothing_overlays() + update_overlays_from_lists() + +/mob/living/carbon/human/update_clothing() + // Should be called only when something about the clothing itself changed + // which is not handled by the equip code. + rebuild_appearance() + + return + + // TODO: once I have replaced the udpate_clothing() calls with update_lying() or + // update_body_appearance() calls where suitable, the following code should + // be used. furthermore, handle_clothes() should be moved to Life() + + rebuild_clothing_overlays() + update_overlays_from_lists() /mob/living/carbon/human/hand_p(mob/M as mob) var/dam_zone = pick("chest", "l_hand", "r_hand", "l_leg", "r_leg") @@ -2664,9 +2701,7 @@ It can still be worn/put on as normal. gender = MALE else gender = FEMALE - update_body() - update_face() - update_clothing() + rebuild_appearance() check_dna() for(var/mob/M in view()) diff --git a/code/modules/mob/living/carbon/human/human_attackalien.dm b/code/modules/mob/living/carbon/human/human_attackalien.dm index 1f82eb28591..09cb279b540 100644 --- a/code/modules/mob/living/carbon/human/human_attackalien.dm +++ b/code/modules/mob/living/carbon/human/human_attackalien.dm @@ -70,5 +70,4 @@ for(var/mob/O in viewers(src, null)) if ((O.client && !( O.blinded ))) O.show_message(text("\red [] has tried to disarm []!", M, src), 1) - update_clothing() return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 55f60c1555a..b5ced7ef8bb 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -213,7 +213,6 @@ playsound(loc, 'punchmiss.ogg', 25, 1, -1) visible_message("\red [M] attempted to disarm [src]!") - update_clothing() return /mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params) diff --git a/code/modules/mob/living/carbon/human/human_attackpaw.dm b/code/modules/mob/living/carbon/human/human_attackpaw.dm index 568ca9969d5..8d01ca3e8f2 100644 --- a/code/modules/mob/living/carbon/human/human_attackpaw.dm +++ b/code/modules/mob/living/carbon/human/human_attackpaw.dm @@ -20,5 +20,4 @@ src = null src = H.monkeyize() contract_disease(D,1,0) - update_clothing() return diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index bfa163f82e6..528dc9028f3 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -68,5 +68,4 @@ UpdateDamageIcon() updatehealth() - update_clothing() return 1 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index ead97f7ece0..0f3eaa3e508 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -198,5 +198,4 @@ emp_act if(src.wear_suit) src.wear_suit.add_blood(src) if(src.w_uniform) src.w_uniform.add_blood(src) - UpdateDamageIcon() - update_clothing() \ No newline at end of file + UpdateDamageIcon() diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index df90a16d75f..f62546d1aae 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -29,18 +29,18 @@ //Being buckled to a chair or bed check_if_buckled() + handle_clothing() - // Update clothing -// update_clothing() + // TODO: this check and the lyingcheck variable should probably be removed in favor of the visual_lying check if((lyingcheck != lying) || (buckle_check != (buckled ? 1 : 0))) //This is a fix for falling down / standing up not updating icons. Instead of going through and changing every spawn(5) - update_clothing() //instance in the code where lying is modified, I've just added a new variable "lyingcheck" which will be compared + update_lying() //instance in the code where lying is modified, I've just added a new variable "lyingcheck" which will be compared lyingcheck = lying //to lying, so if lying ever changes, update_clothing() will run like normal. if(stat == 2) - if((!lying || !lyingcheck) && !buckled) + if(!lying && !buckled) lying = 1 - update_clothing() + update_lying() return life_tick++ @@ -137,7 +137,7 @@ if (stat != 0) if(!lying) lying = 1 //Seriously, stay down :x - update_clothing() + update_lying() @@ -359,7 +359,7 @@ if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) return var/datum/gas_mixture/environment = loc.return_air() - var/datum/air_group/breath + var/datum/gas_mixture/breath // HACK NEED CHANGING LATER if(isbreathing && health < config.health_threshold_crit) @@ -420,6 +420,16 @@ smoke.reagents.copy_to(src, 10) // I dunno, maybe the reagents enter the blood stream through the lungs? break // If they breathe in the nasty stuff once, no need to continue checking + if(istype(wear_mask, /obj/item/clothing/mask/gas)) + var/datum/gas_mixture/filtered = new() + filtered.toxins = breath.toxins + filtered.trace_gases = breath.trace_gases.Copy() + filtered.update_values() + breath.toxins = 0 + breath.trace_gases = list() + breath.update_values() + loc.assume_air(filtered) + else //Still give containing object the chance to interact if(istype(loc, /obj/)) var/obj/location_as_object = loc @@ -469,7 +479,7 @@ var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa //var/safe_oxygen_max = 140 // Maximum safe partial pressure of O2, in kPa (Not used for now) var/safe_co2_max = 10 // Yes it's an arbitrary value who cares? - var/safe_toxins_max = 0.005 + var/safe_toxins_max = 0.0025 var/SA_para_min = 1 var/SA_sleep_min = 5 var/oxygen_used = 0 @@ -649,7 +659,7 @@ //Account for massive pressure differences. Done by Polymorph var/pressure = environment.return_pressure() - if(!istype(wear_suit, /obj/item/clothing/suit/space)) + if(!istype(wear_suit, /obj/item/clothing/suit/space) && !istype(wear_suit, /obj/item/clothing/suit/fire)) /*if(pressure < 20) if(prob(25)) src << "You feel the splittle on your lips and the fluid on your eyes boiling away, the capillteries in your skin breaking." @@ -658,6 +668,9 @@ if(pressure > HAZARD_HIGH_PRESSURE) adjustBruteLoss(min((10+(round(pressure/(HIGH_STEP_PRESSURE)-2)*5)),MAX_PRESSURE_DAMAGE)) + if(environment.toxins > MOLES_PLASMA_VISIBLE) + pl_effects() + return //TODO: DEFERRED adjust_body_temperature(current, loc_temp, boost) @@ -1501,4 +1514,54 @@ else if(shock_stage == 40) src << ""+pick("The pain is excrutiating!", "Please, just end the pain!", "Your whole body is going numb!") else if(shock_stage == 80) - src << ""+pick("You see a light at the end of the tunnel!", "You feel like you could die any moment now.", "You're about to lose consciousness.") \ No newline at end of file + src << ""+pick("You see a light at the end of the tunnel!", "You feel like you could die any moment now.", "You're about to lose consciousness.") + + +/mob/living/carbon/human/proc/handle_clothing() + // Non-visual stuff that was in update_clothing() + + if(buckled) + if(istype(buckled, /obj/structure/stool/bed/chair)) + lying = 0 + else + lying = 1 + + if(!w_uniform) + // Automatically drop anything in store / id / belt if you're not wearing a uniform. + for (var/obj/item/thing in list(r_store, l_store, wear_id, belt)) + if (thing) + u_equip(thing) + if (client) + client.screen -= thing + if (thing) + thing.loc = loc + thing.dropped(src) + thing.layer = initial(thing.layer) + + // Why this stuff was in handle_clothing() is beyond me + var/shielded = 0 + for (var/obj/item/weapon/cloaking_device/S in src) + if (S.active) + shielded = 2 + break + + if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)&&wear_suit:s_active) + shielded = 3 + switch(shielded) + if(1) + if(2) + invisibility = 2 + //New stealth. Hopefully doesn't lag too much. /N + if(istype(loc, /turf))//If they are standing on a turf. + AddCamoOverlay(loc)//Overlay camo. + if(3) + if(istype(loc, /turf)) + //Ninjas may flick into view once in a while if they are stealthed. + if(prob(90)) + NinjaStealthActive(loc) + else + NinjaStealthMalf() + else + invisibility = 0 + + name = get_visible_name() diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm index 6bbbc532d27..78cf259a7cd 100644 --- a/code/modules/mob/living/carbon/human/login.dm +++ b/code/modules/mob/living/carbon/human/login.dm @@ -1,7 +1,7 @@ /mob/living/carbon/human/Login() ..() - update_clothing() + rebuild_appearance() if (!isturf(src.loc)) src.client.eye = src.loc diff --git a/code/modules/mob/living/carbon/metroid/login.dm b/code/modules/mob/living/carbon/metroid/login.dm index 6b696c8a516..e954e353e76 100644 --- a/code/modules/mob/living/carbon/metroid/login.dm +++ b/code/modules/mob/living/carbon/metroid/login.dm @@ -1,7 +1,7 @@ /mob/living/carbon/metroid/Login() ..() - update_clothing() + rebuild_appearance() if (!isturf(src.loc)) src.client.eye = src.loc diff --git a/code/modules/mob/living/carbon/metroid/metroid.dm b/code/modules/mob/living/carbon/metroid/metroid.dm index 85590d06d21..fc3ff215c6d 100644 --- a/code/modules/mob/living/carbon/metroid/metroid.dm +++ b/code/modules/mob/living/carbon/metroid/metroid.dm @@ -9,7 +9,7 @@ real_name = name spawn (1) - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" ..() diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm index 70489c1117a..e4baace6694 100644 --- a/code/modules/mob/living/carbon/monkey/life.dm +++ b/code/modules/mob/living/carbon/monkey/life.dm @@ -187,7 +187,7 @@ if(!loc) return //probably ought to make a proper fix for this, but :effort: --NeoFite var/datum/gas_mixture/environment = loc.return_air() - var/datum/air_group/breath + var/datum/gas_mixture/breath if(losebreath>0) //Suffocating so do not take a breath src.losebreath-- diff --git a/code/modules/mob/living/carbon/monkey/login.dm b/code/modules/mob/living/carbon/monkey/login.dm index 9f24a42fec6..1fb31db3874 100644 --- a/code/modules/mob/living/carbon/monkey/login.dm +++ b/code/modules/mob/living/carbon/monkey/login.dm @@ -1,7 +1,7 @@ /mob/living/carbon/monkey/Login() ..() - update_clothing() + rebuild_appearance() /* if (!( src.primary )) var/t1 = rand(1000, 1500) diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index ad85a5e6d0c..f97a0aa2ef1 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -69,7 +69,7 @@ if(!lying_icon) lying_icon = new /icon('monkey.dmi', "monkey0") icon = stand_icon - update_clothing() + rebuild_appearance() src << "\blue Your icons have been generated!" ..() @@ -930,7 +930,6 @@ UpdateDamageIcon() updatehealth() - update_clothing() return 1 /*/mob/living/carbon/monkey/UpdateDamageIcon() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index aa829b0930e..c1067a0523a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -40,14 +40,8 @@ return 0 var/mob/living/carbon/human/H = src //make this damage method divide the damage to be done among all the body parts, then burn each body part for that much damage. will have better effect then just randomly picking a body part var/divided_damage = (burn_amount)/(H.organs.len) - var/extradam = 0 //added to when organ is at max dam for(var/name in H.organs) - var/datum/organ/external/affecting = H.organs[name] - if(!affecting) continue - if(affecting.take_damage(0, divided_damage+extradam, 0, used_weapon)) - extradam = 0 - else - extradam += divided_damage + apply_damage(divided_damage, BURN, name, 0, 0, "Skin Burns") H.UpdateDamageIcon() H.updatehealth() return 1 diff --git a/code/modules/mob/living/parasite/meme.dm b/code/modules/mob/living/parasite/meme.dm index e3d7b53d467..c3c65b9911f 100644 --- a/code/modules/mob/living/parasite/meme.dm +++ b/code/modules/mob/living/parasite/meme.dm @@ -498,7 +498,6 @@ mob/living/parasite/meme/verb/Analgesic() mob/proc/clearHUD() - update_clothing() if(client) client.screen.Cut() // Take control of the mob diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index 959a83abcf3..7a294615bf3 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -1,6 +1,6 @@ /mob/living/silicon/ai/Login() ..() - update_clothing() + rebuild_appearance() for(var/S in src.client.screen) del(S) src.flash = new /obj/screen( null ) diff --git a/code/modules/mob/living/silicon/robot/hud.dm b/code/modules/mob/living/silicon/robot/hud.dm index d272c96f3a4..41100e1066c 100644 --- a/code/modules/mob/living/silicon/robot/hud.dm +++ b/code/modules/mob/living/silicon/robot/hud.dm @@ -202,7 +202,7 @@ mymob.pullin.icon = 'screen1_robot.dmi' mymob.pullin.icon_state = "pull0" mymob.pullin.name = "pull" - mymob.pullin.screen_loc = ui_pull_old + mymob.pullin.screen_loc = ui_pull_borg mymob.blind = new /obj/screen( null ) mymob.blind.icon = 'screen1_robot.dmi' diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index 1127d6bebb7..728112e394e 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -1,7 +1,7 @@ /mob/living/silicon/robot/Login() ..() - update_clothing() + rebuild_appearance() if (!isturf(src.loc)) src.client.eye = src.loc diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 18c54e635e5..6c752c4da8c 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -177,9 +177,6 @@ item.loc = null item.layer = initial(item.layer) u_equip(item) - //if (client) - // client.screen -= item - //update_clothing() return /mob/proc/get_active_hand() @@ -1161,5 +1158,27 @@ note dizziness decrements automatically in the mob's Life() proc. if(!isemptylist(args)) for(var/file in args) src << browse_rsc(file) - return 1 - return 0 + return 1 + return 0 + + +/** The stuff below here really should be in /mob/living, but due to tons of procs that should only + take /mob/living only taking /mob(or, indeed, some silly procs relying on usr.. yes, *procs*, not verbs..), + I'm putting this stuff into /mob for now +**/ +/mob/proc/rebuild_appearance() + // Rebuild the entire mob appearance completely, this should ONLY be called in rare cases, + // e.g. when the mob spawns. + return update_clothing() // most mobs still implement update_clothing() by simply rebuilding the entire appearance + +/mob/proc/update_body_appearance() + // Call this proc whenever something about the appearance of the body itself changes. + // For example, this must be called when you add a wound to a mob. + +/mob/proc/update_lying() + // Call this whenever the lying status of a mob changes. + +/mob/update_clothing() + // Call this proc whenever something about the clothing of a mob changes. Normally, you + // don't need to call this by hand, as the equip procs will do it for you. + ..() diff --git a/code/modules/mob/organ/organ.dm b/code/modules/mob/organ/organ.dm index 4070542e6c7..24d7e967c27 100644 --- a/code/modules/mob/organ/organ.dm +++ b/code/modules/mob/organ/organ.dm @@ -104,7 +104,7 @@ burn *= 0.66 //~2/3 damage for ROBOLIMBS if(owner && !robot) - owner.pain(display_name, (brute+burn)*3, 1) + owner.pain(display_name, (brute+burn)*3, 1, burn > brute) if(sharp) var/nux = brute * rand(10,15) if(brute_dam >= max_damage) @@ -235,7 +235,8 @@ var/datum/autopsy_data/W = autopsy_data[V] del W autopsy_data = list() - return update_icon() + var/result = update_icon() + return result proc/add_wound(var/used_weapon, var/damage) var/datum/autopsy_data/W = autopsy_data[used_weapon] @@ -248,6 +249,7 @@ W.damage += damage W.time_inflicted = world.time + owner.update_body_appearance() proc/get_damage() //returns total damage @@ -314,6 +316,7 @@ var/n_is = damage_state_text() if (n_is != damage_state) damage_state = n_is + owner.update_body_appearance() // I'm not sure about this, Sky probably knows better where to put it return 1 return 0 @@ -412,7 +415,7 @@ for(var/datum/organ/wound/W in wounds) W.update_health() del(W) - owner.update_body() + owner.update_body_appearance() owner.update_clothing() proc/createwound(var/size = 1, var/type = 0, var/damage) diff --git a/code/modules/mob/organ/pain.dm b/code/modules/mob/organ/pain.dm index 21825ce8eaa..3213b26ffae 100644 --- a/code/modules/mob/organ/pain.dm +++ b/code/modules/mob/organ/pain.dm @@ -7,7 +7,7 @@ mob/var/next_pain_time = 0 // partname is the name of a body part // amount is a num from 1 to 100 -mob/proc/pain(var/partname, var/amount, var/force) +mob/proc/pain(var/partname, var/amount, var/force, var/burning = 0) if(stat >= 2) return if(world.time < next_pain_time && !force) return @@ -17,15 +17,26 @@ mob/proc/pain(var/partname, var/amount, var/force) if(amount > 50 && prob(amount / 5)) src:drop_item() var/msg - switch(amount) - if(1 to 10) - msg = "Your [partname] hurts a bit." - if(11 to 90) - flash_weak_pain() - msg = "Ouch! Your [partname] hurts." - if(91 to 10000) - flash_pain() - msg = "OH GOD! Your [partname] is hurting terribly!" + if(burning) + switch(amount) + if(1 to 10) + msg = "\red Your [partname] burns." + if(11 to 90) + flash_weak_pain() + msg = "\red Your [partname] burns badly!" + if(91 to 10000) + flash_pain() + msg = "\red OH GOD! Your [partname] is on fire!" + else + switch(amount) + if(1 to 10) + msg = "Your [partname] hurts." + if(11 to 90) + flash_weak_pain() + msg = "Your [partname] hurts badly." + if(91 to 10000) + flash_pain() + msg = "OH GOD! Your [partname] is hurting terribly!" if(msg && (msg != last_pain_message || prob(10))) last_pain_message = msg src << msg diff --git a/code/modules/mob/simple_animal/corgi.dm b/code/modules/mob/simple_animal/corgi.dm index 2461e55f6e6..504c81d8faa 100644 --- a/code/modules/mob/simple_animal/corgi.dm +++ b/code/modules/mob/simple_animal/corgi.dm @@ -47,7 +47,7 @@ /mob/living/simple_animal/corgi/Life() ..() - update_clothing() + rebuild_appearance() /mob/living/simple_animal/corgi/show_inv(mob/user as mob) /* @@ -182,7 +182,6 @@ usr.drop_item() item_to_add.loc = src src.inventory_head = item_to_add - update_clothing() //Various hats and items (worn on his head) change Ian's behaviour. His attributes are reset when a HAT is removed. diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index d5575e28ae5..a1e940182ab 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -5,7 +5,7 @@ if (W==w_uniform) // will be teared continue drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 stunned = 1 @@ -69,7 +69,7 @@ return for(var/obj/item/W in src) drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 icon = null @@ -156,7 +156,7 @@ return for(var/obj/item/W in src) drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 icon = null @@ -222,7 +222,7 @@ return for(var/obj/item/W in src) drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 icon = null @@ -262,7 +262,7 @@ return for(var/obj/item/W in src) drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 icon = null @@ -313,7 +313,7 @@ return for(var/obj/item/W in src) drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 icon = null @@ -347,7 +347,7 @@ return for(var/obj/item/W in src) drop_from_slot(W) - update_clothing() + rebuild_appearance() monkeyizing = 1 canmove = 0 icon = null diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 9f5d6156ae3..99e93705c4b 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -388,7 +388,7 @@ mob/proc spawn(0) //Make it show the 2 states properly if(target_locked) target_locked.icon_state = "locking" - update_clothing() + update_clothing() // update_clothing clears overlays, so this might break stuff sleep(20) if(target_locked) target_locked.icon_state = "locked" diff --git a/code/setup.dm b/code/setup.dm index ae22ad53adc..0208423c852 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -14,6 +14,16 @@ #define MOLES_PLASMA_VISIBLE 0.5 //Moles in a standard cell after which plasma is visible +#define SPECIFIC_HEAT_TOXIN 200 +#define SPECIFIC_HEAT_AIR 20 +#define SPECIFIC_HEAT_CDO 30 +#define HEAT_CAPACITY_CALCULATION(oxygen,carbon_dioxide,nitrogen,toxins) \ + (carbon_dioxide*SPECIFIC_HEAT_CDO + (oxygen+nitrogen)*SPECIFIC_HEAT_AIR + toxins*SPECIFIC_HEAT_TOXIN) + +#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 + #define BREATH_VOLUME 0.5 //liters in a normal breath #define BREATH_PERCENTAGE BREATH_VOLUME/CELL_VOLUME //Amount of air to take a from a tile diff --git a/html/changelog.html b/html/changelog.html index 668176c0552..223bb93d994 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -56,6 +56,26 @@ Stuff which is in development and not yet visible to players or just code relate should be listed in the changelog upon commit though. Thanks. --> +
+

29 May 2012

+

Aryn updated:

+
    +
  • Airflow works on a room-by-room basis rather than on individual tiles. Rooms will depressurize much faster than they did, though not too fast for balance reasons.
  • +
  • Fire now works on a logarithmic scale based on oxygen and fuel content. This is a far more complex tweak under the hood than it is in game.
  • +
  • Plasma now has increased toxicity and can burn exposed skin and eyes. In addition, because of the new air, it fills the room instantaneously. Try not to spill any.
  • +
+
+ +
+

28 May 2012

+

Erthilo updated:

+
    +
  • Surgeons spawn with scrubs, Emergency Physicians spawn with first responder jackets.
  • +
  • Added water bottles to cola vending machines.
  • +
  • More HUD changes: https://dl.dropbox.com/u/4911517/ShareX/2012-05/2012-05-28_20-40-50.png
  • +
+
+

27 May 2012

Abi79 updated:

@@ -123,11 +143,11 @@ should be listed in the changelog upon commit though. Thanks. -->
-

22 May 2012

+

22 May 2012

cib updated:

  • Worked out a few problems with meme. They now can see, even if their mob is sleeping. Their actions are properly tracked in admin logs. Their points recharge slightly faster, and can now be seen in the Status tab.
  • -
+

Erthilo updated:

  • Some sleeping and resting fixes. Please report any more bugs with it!
  • @@ -169,17 +189,17 @@ should be listed in the changelog upon commit though. Thanks. -->

    Erthilo updated:

    • You can now right-click pick up items and open/close closets items. This means cyborgs can now open/close closets with a bit of fiddling!
    • -
    • Added confirmation pop-up for character slot deletion.
    • +
    • Added confirmation pop-up for character slot deletion.
-

19 May 2012

+

19 May 2012

cib updated:

  • Implemented Memetic Anomaly game mode. A meme is a kind of parasite that can live in a human host and jump from player to player. They have traitor-like objectives, but must achieve them by controlling hosts, rather than doing it themselves.
  • Credits for the idea goes to: SCP-1312-1 . Playtesters: Erthilo, Cael_Aislin, AfterShave and critica.
  • -
+

TG updated:

  • You can now swap hands by clicking with your middle mouse button (you have to click on a visible object though, that's the catch).
  • @@ -262,7 +282,7 @@ should be listed in the changelog upon commit though. Thanks. -->
  • Fixes mech weapons not firing.
  • Added cable restraints, made from 15 lengths of cable coil by right-click a cable coil in-hand and choosing 'Make Cable Restraints'. These works just like handcuffs, but only take 30 seconds to break!
  • The Head of Personnel can now change alert levels.
  • -
  • Fixes lockdown computers being blank.
  • +
  • Fixes lockdown computers being blank.
diff --git a/icons/effects/contamination.dmi b/icons/effects/contamination.dmi new file mode 100644 index 00000000000..e0f34e26469 Binary files /dev/null and b/icons/effects/contamination.dmi differ diff --git a/icons/effects/genetics.dmi b/icons/effects/genetics.dmi index ab2916710ac..d9a8e5f9ed5 100644 Binary files a/icons/effects/genetics.dmi and b/icons/effects/genetics.dmi differ diff --git a/icons/effects/plasma.dmi b/icons/effects/plasma.dmi index bf287fde3ec..5f9137267d4 100644 Binary files a/icons/effects/plasma.dmi and b/icons/effects/plasma.dmi differ diff --git a/icons/effects/tile_effects.dmi b/icons/effects/tile_effects.dmi index 4c78f8f2e06..5ed60b7adcd 100644 Binary files a/icons/effects/tile_effects.dmi and b/icons/effects/tile_effects.dmi differ diff --git a/icons/mob/livestock.dmi b/icons/mob/livestock.dmi index 43e485a297c..538e14b1ea1 100644 Binary files a/icons/mob/livestock.dmi and b/icons/mob/livestock.dmi differ diff --git a/icons/mob/screen1_Midnight.dmi b/icons/mob/screen1_Midnight.dmi index 42d15482c22..e37b1cce29a 100644 Binary files a/icons/mob/screen1_Midnight.dmi and b/icons/mob/screen1_Midnight.dmi differ diff --git a/icons/mob/screen1_Orange.dmi b/icons/mob/screen1_Orange.dmi index e3762ef4b2b..fb1136a5347 100644 Binary files a/icons/mob/screen1_Orange.dmi and b/icons/mob/screen1_Orange.dmi differ diff --git a/icons/mob/screen1_old.dmi b/icons/mob/screen1_old.dmi index 98ea94f656d..37d67820992 100644 Binary files a/icons/mob/screen1_old.dmi and b/icons/mob/screen1_old.dmi differ diff --git a/icons/mob/zone_sel.dmi b/icons/mob/zone_sel.dmi index 7f8bb1c4741..b7549296d2c 100644 Binary files a/icons/mob/zone_sel.dmi and b/icons/mob/zone_sel.dmi differ diff --git a/icons/obj/custom_items.dmi b/icons/obj/custom_items.dmi index cf18853c68d..700fc99bf16 100644 Binary files a/icons/obj/custom_items.dmi and b/icons/obj/custom_items.dmi differ diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi index 11a133c079f..e4652f8b620 100644 Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ