From a7bce8a60996a6bcfb12d49399f8e281150759c0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 15 Mar 2012 22:52:29 +0000 Subject: [PATCH] WHOOPS Signed-off-by: unknown --- code/ZAS/Airflow.dm | 587 ++++++++++++++++++++++++++++++++++ code/ZAS/Connection.dm | 61 ++++ code/ZAS/Creation.dm | 60 ++++ code/ZAS/Debug.dm | 56 ++++ code/ZAS/Definition.dm | 34 ++ code/ZAS/Fire.dm | 338 ++++++++++++++++++++ code/ZAS/Functions.dm | 159 +++++++++ code/ZAS/Plasma.dm | 252 +++++++++++++++ code/ZAS/Processing.dm | 103 ++++++ code/ZAS/Variable Settings.dm | 285 +++++++++++++++++ icons/misc/debug_connect.dmi | Bin 0 -> 223 bytes icons/misc/debug_group.dmi | Bin 0 -> 225 bytes icons/misc/debug_space.dmi | Bin 0 -> 222 bytes 13 files changed, 1935 insertions(+) create mode 100644 code/ZAS/Airflow.dm create mode 100644 code/ZAS/Connection.dm create mode 100644 code/ZAS/Creation.dm create mode 100644 code/ZAS/Debug.dm create mode 100644 code/ZAS/Definition.dm create mode 100644 code/ZAS/Fire.dm create mode 100644 code/ZAS/Functions.dm create mode 100644 code/ZAS/Plasma.dm create mode 100644 code/ZAS/Processing.dm create mode 100644 code/ZAS/Variable Settings.dm create mode 100644 icons/misc/debug_connect.dmi create mode 100644 icons/misc/debug_group.dmi create mode 100644 icons/misc/debug_space.dmi diff --git a/code/ZAS/Airflow.dm b/code/ZAS/Airflow.dm new file mode 100644 index 00000000000..486b0bdc6a9 --- /dev/null +++ b/code/ZAS/Airflow.dm @@ -0,0 +1,587 @@ +/* + +CONTAINS: +All AirflowX() procs, all Variable Setting Controls for airflow, save/load variable tweaks for airflow. + +VARIABLES: + +atom/movable/airflow_dest + The destination turf of a flying object. + +atom/movable/airflow_speed + The speed (1-15) at which a flying object is traveling to airflow_dest. Decays over time. + + +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. + RETURNS: Null + +atom/movable/check_airflow_movable(n) + Contains checks for moving any object due to airflow. + n is the percent of 1 Atmosphere that is flowing. + RETURNS: 1 if the object moves under the air conditions, 0 if it stays put. + +atom/movable/airflow_hit(atom/A) + Contains results of hitting a solid object (A) due to airflow. + A is the dense object hit. + Use airflow_speed to determine how fast the projectile was going. + + +AUTOMATIC PROCS: + +Airflow(zone/A, zone/B, n) + 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. + Called by zone updates. A is a zone connected to space. + +atom/movable/GotoAirflowDest(n) +atom/movable/RepelAirflowDest(n) + Called by main airflow procs to cause the object to fly to or away from destination at speed n. + Probably shouldn't call this directly unless you know what you're + doing and have set airflow_dest. airflow_hit() will be called if the object collides with an obstacle. + +*/ + +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 = 2 + zone_share_percent_NAME = "Zone Connection Transfer %" + zone_share_percent_DESC = "Percent of gas per connected tile that is shared between zones." + + //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. + +mob/proc/airflow_stun() + if(weakened <= 0) src << "\red The sudden rush of air knocks you over!" + weakened = max(weakened,5) + +mob/living/silicon/airflow_stun() + return + +mob/living/carbon/metroid/airflow_stun() + return + +mob/living/carbon/human/airflow_stun() + if(buckled) return 0 + if(wear_suit) + if(wear_suit.flags & SUITSPACE) return 0 + if(shoes) + if(shoes.flags & NOSLIP) return 0 + if(weakened <= 0) src << "\red The sudden rush of air knocks you over!" + weakened = max(weakened,2) + +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 + + return 1 + +mob/dead/observer/check_airflow_movable() + return 0 + +mob/living/silicon/check_airflow_movable() + return 0 + + +obj/item/check_airflow_movable(n) + . = ..() + switch(w_class) + if(2) + if(n < vsc.AF_SMALL_MOVEMENT_THRESHOLD) return 0 + if(3) + if(n < vsc.AF_NORMAL_MOVEMENT_THRESHOLD) return 0 + if(4,5) + if(n < vsc.AF_LARGE_MOVEMENT_THRESHOLD) return 0 + +proc/Airflow(zone/A,zone/B,n) + + //Now n is a percent of one atm. + n = round((n/vsc.AF_PERCENT_OF)*100,0.1) + + //Don't go any further if n is lower than the lowest value needed for airflow. + if(abs(n) < vsc.AF_TINY_MOVEMENT_THRESHOLD) 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() + + for(var/atom/movable/M in pplz) + + //Check for knocking people over + if(ismob(M) && n > vsc.AF_HUMAN_STUN_THRESHOLD) + if(M:nodamage) continue + M:airflow_stun() + + if(M.check_airflow_movable(n)) + + //Check for things that are in range of the midpoint turfs. + var/fail = 1 + for(var/turf/U in connected_turfs) + if(M in range(U)) fail = 0 + if(fail) 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. + + spawn M.GotoAirflowDest(abs(n) * (vsc.AF_SPEED_MULTIPLIER/10)) + //Send the object flying at a speed determined by n and AF_SPEED_MULTIPLIER. + + //Do it again for the stuff in the other zone, making it fly away. + for(var/atom/movable/M in otherpplz) + + if(ismob(M) && abs(n) > vsc.AF_HUMAN_STUN_THRESHOLD) + if(M:nodamage) continue + M:airflow_stun() + + if(M.check_airflow_movable(abs(n))) + + var/fail = 1 + for(var/turf/U in connected_turfs) + if(M in range(U)) fail = 0 + if(fail) continue + + if(M && !M.airflow_speed) + + M.airflow_dest = pick(connected_turfs) + + spawn M.RepelAirflowDest(abs(n) * (vsc.AF_SPEED_MULTIPLIER/10)) + +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. + + n = round((n/vsc.AF_PERCENT_OF)*100,0.1) + + if(n < vsc.AF_TINY_MOVEMENT_THRESHOLD) 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. + + for(var/atom/movable/M in pplz) + + if(ismob(M) && n > vsc.AF_HUMAN_STUN_THRESHOLD) + if(M:nodamage) continue + M:airflow_stun() + + if(M.check_airflow_movable(n)) + + var/fail = 1 + for(var/turf/U in connected_turfs) + if(M in range(U)) fail = 0 + if(fail) continue + + if(!M.airflow_speed) + M.airflow_dest = pick(connected_turfs) + spawn + if(M) M.GotoAirflowDest(n * (vsc.AF_SPEED_MULTIPLIER/10)) + //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 + + proc/GotoAirflowDest(n) + if(!airflow_dest) return + if(airflow_speed < 0) return + if(airflow_speed) + airflow_speed = n + return + if(airflow_dest == loc) + step_away(src,loc) + if(ismob(src)) + if(src:nodamage) return + if(istype(src, /mob/living/carbon/human)) + if(istype(src, /mob/living/carbon/human)) + if(src:buckled) return + if(src:wear_suit) + if(src:wear_suit.flags & SUITSPACE) return + if(src:shoes) + if(src:shoes.type == /obj/item/clothing/shoes/magboots) return + src << "\red You are sucked away by airflow!" + airflow_speed = min(round(n),9) + var + xo = airflow_dest.x - src.x + yo = airflow_dest.y - src.y + od = 0 + airflow_dest = null + if(!density) + density = 1 + od = 1 + while(airflow_speed > 0) + if(airflow_speed <= 0) return + airflow_speed = min(airflow_speed,15) + airflow_speed -= vsc.AF_SPEED_DECAY + if(airflow_speed > 7) + if(airflow_time++ >= airflow_speed - 7) + sleep(1 * tick_multiplier) + else + sleep(max(1,10-(airflow_speed+3)) * tick_multiplier) + if ((!( src.airflow_dest ) || src.loc == src.airflow_dest)) + src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z) + if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy)) + return + step_towards(src, src.airflow_dest) + if(ismob(src) && src:client) src:client:move_delay = world.time + 10 + airflow_dest = null + airflow_speed = -1 + spawn(150 * tick_multiplier) airflow_speed = 0 + if(od) + density = 0 + + proc/RepelAirflowDest(n) + if(!airflow_dest) return + if(airflow_speed < 0) return + if(airflow_speed) + airflow_speed = n + return + if(airflow_dest == loc) + step_away(src,loc) + if(ismob(src)) + if(src:nodamage) return + if(istype(src, /mob/living/carbon/human)) + if(istype(src, /mob/living/carbon/human)) + if(src:buckled) return + if(src:wear_suit) + if(src:wear_suit.flags & SUITSPACE) return + if(src:shoes) + if(src:shoes.type == /obj/item/clothing/shoes/magboots) return + src << "\red You are pushed away by airflow!" + airflow_speed = min(round(n),9) + var + xo = -(airflow_dest.x - src.x) + yo = -(airflow_dest.y - src.y) + od = 0 + airflow_dest = null + if(!density) + density = 1 + od = 1 + while(airflow_speed > 0) + if(airflow_speed <= 0) return + airflow_speed = min(airflow_speed,15) + airflow_speed -= vsc.AF_SPEED_DECAY + if(airflow_speed > 7) + if(airflow_time++ >= airflow_speed - 7) + sleep(1 * tick_multiplier) + else + sleep(max(1,10-(airflow_speed+3)) * tick_multiplier) + if ((!( src.airflow_dest ) || src.loc == src.airflow_dest)) + src.airflow_dest = locate(min(max(src.x + xo, 1), world.maxx), min(max(src.y + yo, 1), world.maxy), src.z) + if ((src.x == 1 || src.x == world.maxx || src.y == 1 || src.y == world.maxy)) + return + step_towards(src, src.airflow_dest) + if(ismob(src) && src:client) src:client:move_delay = world.time + 10 + airflow_dest = null + airflow_speed = -1 + spawn(150 * tick_multiplier) airflow_speed = 0 + if(od) + density = 0 + + Bump(atom/A) + if(airflow_speed > 0 && airflow_dest) + airflow_hit(A) + else + airflow_speed = 0 + . = ..() + +atom/movable/proc/airflow_hit(atom/A) + airflow_speed = -1 + spawn(50 * tick_multiplier) airflow_speed = 0 + airflow_dest = null + +mob/airflow_hit(atom/A) + for(var/mob/M in hearers(src)) + M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2) + playsound(src.loc, "smash.ogg", 25, 1, -1) + . = ..() + +obj/airflow_hit(atom/A) + for(var/mob/M in hearers(src)) + M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2) + playsound(src.loc, "smash.ogg", 25, 1, -1) + . = ..() + +obj/item/airflow_hit(atom/A) + airflow_speed = -1 + spawn(50 * tick_multiplier) airflow_speed = 0 + airflow_dest = null + +mob/living/carbon/human/airflow_hit(atom/A) + for(var/mob/M in hearers(src)) + M.show_message("\red [src] slams into [A]!",1,"\red You hear a loud slam!",2) + playsound(src.loc, "punch", 25, 1, -1) + loc:add_blood(src) + if (src.wear_suit) + src.wear_suit.add_blood(src) + if (src.w_uniform) + src.w_uniform.add_blood(src) + var/b_loss = airflow_speed * 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() + if(airflow_speed > 10) + paralysis += round(airflow_speed * vsc.AF_STUN_MULTIPLIER) + stunned = max(stunned,paralysis + 3) + else + stunned += round(airflow_speed * vsc.AF_STUN_MULTIPLIER/2) + . = ..() + +zone/proc/movables() + . = list() + for(var/turf/T in contents) + for(var/atom/A in T) + . += A + +proc/Get_Dir(atom/S,atom/T) //Shamelessly stolen from AJX.AdvancedGetDir + var/GDist=get_dist(S,T) + var/GDir=get_dir(S,T) + if(GDist<=3) + if(GDist==0) return 0 + if(GDist==1) + return GDir + + + var/X1=S.x*10 + var/X2=T.x*10 + var/Y1=S.y*10 + var/Y2=T.y*10 + var/Ref + if(GDir==NORTHEAST) + Ref=(X2/X1)*Y1 + if(Ref-1>Y2) .=EAST + else if(Ref+1Y2) .=WEST + else if(Ref+1Y2) .=SOUTH + else if(Ref+1Y2) .=SOUTH + else if(Ref+1> vsc + del F + world.log << "TWEAKS: Airflow, Plasma and Damage settings loaded." \ No newline at end of file diff --git a/code/ZAS/Connection.dm b/code/ZAS/Connection.dm new file mode 100644 index 00000000000..bf43f90d1c0 --- /dev/null +++ b/code/ZAS/Connection.dm @@ -0,0 +1,61 @@ +connection + var + turf //The turfs involved in the connection. + A + B + indirect = 0 //If the connection is purely indirect, the zones should not join. + last_updated //The tick at which this was last updated. + New(turf/T,turf/O) + A = T + B = O + if(A.zone) + if(!A.zone.connections) A.zone.connections = new() + A.zone.connections += src + if(B.zone) + if(!B.zone.connections) B.zone.connections = new() + B.zone.connections += src + if(A.zone && B.zone) + if(!A.zone.connected_zones) + A.zone.connected_zones = list() + if(!B.zone.connected_zones) + B.zone.connected_zones = list() + + if(!(B.zone in A.zone.connected_zones)) + A.zone.connected_zones += B.zone + A.zone.connected_zones[B.zone] = 1 + else + A.zone.connected_zones[B.zone]++ + + if(!(A.zone in B.zone.connected_zones)) + B.zone.connected_zones += A.zone + B.zone.connected_zones[A.zone] = 1 + else + B.zone.connected_zones[A.zone]++ + Del() + if(A.zone && A.zone.connections) + A.zone.connections -= src + if(B.zone && B.zone.connections) + B.zone.connections -= src + + if(A.zone && B.zone) + + if(B.zone in A.zone.connected_zones) + if(A.zone.connected_zones[B.zone] > 1) + A.zone.connected_zones[B.zone]-- + else + A.zone.connected_zones -= B.zone + + if(A.zone in B.zone.connected_zones) + if(B.zone.connected_zones[A.zone] > 1) + B.zone.connected_zones[A.zone]-- + else + B.zone.connected_zones -= A.zone + if(A.zone.connected_zones && !A.zone.connected_zones.len) + A.zone.connected_zones = null + if(B.zone.connected_zones && !B.zone.connected_zones.len) + B.zone.connected_zones = null + . = ..() + + proc/Cleanup() + if(A.zone == B.zone) del src + if(!A.zone || !B.zone) del src \ No newline at end of file diff --git a/code/ZAS/Creation.dm b/code/ZAS/Creation.dm new file mode 100644 index 00000000000..5f256767fd3 --- /dev/null +++ b/code/ZAS/Creation.dm @@ -0,0 +1,60 @@ +zone + New(turf/start) + if(istype(start,/list)) + contents = start + else + contents = FloodFill(start) + for(var/turf/T in contents) + T.zone = src + if(istype(T,/turf/space)) + AddSpace(T) + air = new + var/members = contents.len + for(var/turf/simulated/T in contents) + air.oxygen += T.oxygen / members + air.nitrogen += T.nitrogen / members + air.carbon_dioxide += T.carbon_dioxide / members + air.toxins += T.toxins / members + air.temperature += T.temperature / members + air.group_multiplier = contents.len + zones += src + Del() + zones -= src + . = ..() + +proc/FloodFill(turf/start) + var + list + open = list(start) + closed = list() + + while(open.len) + for(var/turf/T in open) + if(!T.HasDoor()) + for(var/d in cardinal) + var/turf/O = get_step(T,d) + if(O.ZCanPass(T) && !(O in open) && !(O in closed)) + open += O + open -= T + closed += T + + return closed + +turf/proc/ZCanPass(turf/T) + if(istype(T,/turf/space)) return 0 + else + if(T.blocks_air||blocks_air) + return 0 + + for(var/obj/obstacle in src) + if(istype(obstacle,/obj/machinery/door) && !istype(obstacle,/obj/machinery/door/window)) + 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 + if(!obstacle.CanPass(0, src, 0, 1)) + return 0 + + return 1 \ No newline at end of file diff --git a/code/ZAS/Debug.dm b/code/ZAS/Debug.dm new file mode 100644 index 00000000000..28fc3caef53 --- /dev/null +++ b/code/ZAS/Debug.dm @@ -0,0 +1,56 @@ + +client/proc/Zone_Info(turf/T as null|turf) + set category = "Debug" + if(T) + if(T.zone) + T.zone.DebugDisplay(mob) + else + mob << "No zone here." + else + for(T in world) + T.overlays -= 'debug_space.dmi' + T.overlays -= 'debug_group.dmi' + T.overlays -= 'debug_connect.dmi' + +zone/proc + DebugDisplay(mob/M) + if(!dbg_output) + dbg_output = 1 //Don't want to be spammed when someone investigates a zone... + for(var/turf/T in contents) + T.overlays += 'debug_group.dmi' + + for(var/turf/space/S in space_tiles) + S.overlays += 'debug_space.dmi' + + M << "Zone Air Contents" + M << "Oxygen: [air.oxygen]" + M << "Nitrogen: [air.nitrogen]" + M << "Plasma: [air.toxins]" + M << "Carbon Dioxide: [air.carbon_dioxide]" + M << "Temperature: [air.temperature]" + M << "Heat Energy: [air.thermal_energy()]" + M << "Pressure: [air.return_pressure()]" + M << "" + M << "Connections: [length(connections)]" + + for(var/connection/C in connections) + M << "[C.A] --> [C.B] [(C.indirect?"Indirect":"Direct")]" + C.A.overlays += 'debug_connect.dmi' + C.B.overlays += 'debug_connect.dmi' + spawn(50) + C.A.overlays -= 'debug_connect.dmi' + C.B.overlays -= 'debug_connect.dmi' + + else + dbg_output = 0 + + for(var/turf/T in contents) + T.overlays -= 'debug_group.dmi' + + for(var/turf/space/S in space_tiles) + S.overlays -= 'debug_space.dmi' + for(var/zone/Z in zones) + if(Z.air == air && Z != src) + var/turf/zloc = pick(Z.contents) + M << "\red Illegal air datum shared by: [zloc.loc.name]" + diff --git a/code/ZAS/Definition.dm b/code/ZAS/Definition.dm new file mode 100644 index 00000000000..729d19841fc --- /dev/null +++ b/code/ZAS/Definition.dm @@ -0,0 +1,34 @@ +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. + 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 diff --git a/code/ZAS/Fire.dm b/code/ZAS/Fire.dm new file mode 100644 index 00000000000..f265c45877a --- /dev/null +++ b/code/ZAS/Fire.dm @@ -0,0 +1,338 @@ +vs_control/var/IgnitionLevel = 10 //Moles of oxygen+plasma - co2 needed to burn. + +#define OXYGEN +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 + var/datum/gas_mixture/air_contents = return_air(1) + if(!air_contents) + return 0 + + /*if(active_hotspot) + if(soh) + if(air_contents.toxins > 0.5 && air_contents.oxygen > 0.5) + if(active_hotspot.temperature < exposed_temperature) + active_hotspot.temperature = exposed_temperature + if(active_hotspot.volume < exposed_volume) + active_hotspot.volume = exposed_volume + return 1*/ + var/igniting = 0 + if(locate(/obj/fire) in src) + 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) + 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 + F.volume = CELL_VOLUME + + //active_hotspot.just_spawned = (current_cycle < air_master.current_cycle) + //remove just_spawned protection if no longer processing this cell + + 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 + + anchored = 1 + mouse_opacity = 0 + + //luminosity = 3 + + icon = 'fire.dmi' + icon_state = "1" + + layer = TURF_LAYER + + var + volume = CELL_VOLUME + temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + firelevel = 10000 + 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(istype(S,/turf/simulated/floor)) + var + datum/gas_mixture/air_contents = S.return_air() + datum/gas/volatile_fuel/fuel = locate(/datum/gas/volatile_fuel/) in air_contents.trace_gases + fuel_level = 0 + 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.oxygen + air_contents.toxins + fuel_level*1.5 + liquid_level*1.5) - (air_contents.carbon_dioxide*0.25) + + 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. + + if(flow) + + if(flow.oxygen > 0.3 && (flow.toxins || fuel_level || liquid)) + + icon_state = "1" + if(firelevel > vsc.IgnitionLevel * 2) + icon_state = "2" + if(firelevel > vsc.IgnitionLevel * 3.5) + icon_state = "3" + flow.temperature = max(PLASMA_MINIMUM_BURN_TEMPERATURE+0.1,flow.temperature) + flow.zburn(liquid) + + else + del src + + + S.assume_air(flow) + 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)) + + + New(newLoc,fl) + ..() + dir = pick(cardinal) + sd_SetLuminosity(3) + firelevel = fl + for(var/mob/living/carbon/human/M in loc) + M.FireBurn(firelevel/(vsc.IgnitionLevel*10)) + + Del() + if (istype(loc, /turf/simulated)) + src.sd_SetLuminosity(0) + + loc = null + + ..() + +obj/liquid_fuel + icon = 'effects.dmi' + icon_state = "slube" + layer = TURF_LAYER+0.2 + anchored = 1 + var/amount = 1 + + New(newLoc) + for(var/obj/liquid_fuel/other in newLoc) + if(other != src) + other.amount += src.amount + del src + . = ..() + +vs_control/var/switch_fire = 1 + +turf/simulated/var/fire_protection = 0 + +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 + if(temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) + var + fuel_level = 0 + 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(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(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 + + else if(fuel_level) + oxygen -= fuel.moles * vsc.plc.OXY_TO_PLASMA + carbon_dioxide += fuel.moles + fuel.moles = 0 + + 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 diff --git a/code/ZAS/Functions.dm b/code/ZAS/Functions.dm new file mode 100644 index 00000000000..ab52842dd1f --- /dev/null +++ b/code/ZAS/Functions.dm @@ -0,0 +1,159 @@ +zone + proc + AddTurf(turf/T) + if(T in contents) return + contents += T + air.group_multiplier++ + T.zone = src + RemoveTurf(turf/T) + 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) + if(istype(S,/turf/space)) + if(!space_tiles) space_tiles = list() + space_tiles += S + RemoveSpace(turf/space/S) + if(space_tiles) + space_tiles -= S + if(!space_tiles.len) space_tiles = null + +turf/proc/HasDoor(turf/O) + for(var/obj/machinery/door/D in src) + if(isnum(O) && O) + if(!D.density) continue + if(istype(D,/obj/machinery/door/window)) + if(!O) continue + if(D.dir == get_dir(D,O)) return 1 + else + return 1 + +turf/proc/find_zone() + for(var/d in cardinal) + var/turf/T = get_step(src,d) + if(!T || !T.zone) continue + if(!zone) + zone = T.zone + zone.AddTurf(src) + else if(T.zone != zone) + ZConnect(src,T) + +proc + ZMerge(zone/A,zone/B) + //world << "Merge occured." + 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 + + A.air.group_multiplier = 1 + B.air.group_multiplier = 1 + + A.air.remove_ratio(a_size/c_size) + B.air.remove_ratio(b_size/c_size) + A.air.merge(B.air) + A.air.group_multiplier = c_size + + for(var/connection/C in B.connections) + if((C.A in new_contents) && (C.B in new_contents)) + del C + continue + A.connections += C + A.space_tiles += B.space_tiles + A.contents = new_contents + for(var/turf/T in B.contents) + T.zone = A + del B + + ZConnect(turf/A,turf/B) + 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." + return + if(!A.zone || !B.zone) return + if(A.zone == B.zone) return + if(!A.CanPass(0,B,0,0)) return + 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' + + + ZDisconnect(turf/A,turf/B) + if(A.zone && B.zone) + 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(A == B) return + if(A.CanPass(0,B,0,0)) return + if(A.HasDoor(B) || B.HasDoor(A)) return + var/zone/oldzone = A.zone + var/list/test = FloodFill(A) + if(B in test) return + else + var/zone/Z = new(test,oldzone.air) + 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 + var/datum/gas_mixture/Y_Air = new + Y_Air.copy_from(oldzone.air) + var/zone/Y = new(B,Y_Air) + 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 + 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) + else if(istype(B,/turf/space) && A.zone) + A.zone.RemoveSpace(B) \ No newline at end of file diff --git a/code/ZAS/Plasma.dm b/code/ZAS/Plasma.dm new file mode 100644 index 00000000000..df25a974578 --- /dev/null +++ b/code/ZAS/Plasma.dm @@ -0,0 +1,252 @@ +pl_control/var + PLASMA_DMG = 3 + PLASMA_DMG_NAME = "Plasma Damage Multiplier" + PLASMA_DMG_DESC = "Multiplier on how much damage inhaling plasma can do." + + OXY_TO_PLASMA = 1 + OXY_TO_PLASMA_NAME = "O2/Plasma Ratio" + OXY_TO_PLASMA_DESC = "Multiplier for the ratio of oxygen to plasma required in fires." + + CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. + CLOTH_CONTAMINATION_NAME = "Plasma - Cloth Contamination" + CLOTH_CONTAMINATION_RANDOM = 60 + CLOTH_CONTAMINATION_METHOD = "Toggle" + CLOTH_CONTAMINATION_DESC = "If set to nonzero, plasma will contaminate cloth items (uniforms, backpacks, etc.)\ + and cause a small amount of damage over time to anyone carrying or wearing them. Contamination can be detected\ + with a Health Analyzer, and washed off in the washer." + + ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or + //decontaminated. + ALL_ITEM_CONTAMINATION_NAME = "Plasma - Full Contamination" + ALL_ITEM_CONTAMINATION_RANDOM = 10 + ALL_ITEM_CONTAMINATION_METHOD = "Toggle" + ALL_ITEM_CONTAMINATION_DESC = "Like Cloth Contamination, but all item types are susceptible." + + PLASMAGUARD_ONLY = 0 + PLASMAGUARD_ONLY_NAME = "Plasma - Biosuits/Spacesuits Only" + PLASMAGUARD_ONLY_RANDOM = 20 + PLASMAGUARD_ONLY_METHOD = "Toggle" + PLASMAGUARD_ONLY_DESC = "If on, any suits that are not biosuits or space suits will not protect against contamination." + + //CANISTER_CORROSION = 0 //If this is on, plasma must be stored in orange tanks and canisters, + //CANISTER_CORROSION_RANDOM = 20 //or it will corrode the tank. + //CANISTER_CORROSION_METHOD = "Toggle" + + GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 10,000. + GENETIC_CORRUPTION_NAME = "Plasma - Genetic Corruption" + GENETIC_CORRUPTION_RANDOM = "PROB10/3d6" + GENETIC_CORRUPTION_METHOD = "Numeric" + GENETIC_CORRUPTION_DESC = "When set to a probability in 1000, any humans in plasma will have this chance to develop a random mutation." + + SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited. + SKIN_BURNS_NAME = "Plasma - Skin Burns" + SKIN_BURNS_RANDOM = 10 + SKIN_BURNS_METHOD = "Toggle" + SKIN_BURNS_DESC = "When toggled, humans with exposed skin will suffer burns (similar to mustard gas) in plasma." + + //PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time. + //PLASMA_INJECTS_TOXINS_RANDOM = 30 + //PLASMA_INJECTS_TOXINS_METHOD = "Toggle" + + EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection. + EYE_BURNS_NAME = "Plasma - Eye Burns" + EYE_BURNS_RANDOM = 30 + EYE_BURNS_METHOD = "Toggle" + EYE_BURNS_DESC = "When toggled, humans without masks that cover the eyes will suffer temporary blurriness and sight loss,\ + and may need glasses to see again if exposed for long durations." + + //N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high. + //N2O_REACTION_RANDOM = 5 + + //PLASMA_COLOR = "onturf" //Plasma can change colors yaaaay! + //PLASMA_COLOR_RANDOM = "PICKonturf,onturf" + + //PLASMA_DMG_OFFSET = 1 + //PLASMA_DMG_OFFSET_RANDOM = "1d5" + //PLASMA_DMG_QUOTIENT = 10 + //PLASMA_DMG_QUOTIENT_RANDOM = "1d10+4" + + CONTAMINATION_LOSS = 0.1 + _NAME = "Plasma - Contamination Damage" + CONTAMINATION_LOSS_DESC = "A number representing the damage done per life cycle by contaminated items." + + PLASMA_HALLUCINATION = 1 + PLASMA_HALLUCINATION_NAME = "Plasma - Hallucination" + PLASMA_HALLUCINATION_METHOD = "Toggle" + PLASMA_HALLUCINATION_DESC = "If toggled, uses the remnants of the hallucination code to induce visions in those\ + who breathe plasma." + N2O_HALLUCINATION = 1 + N2O_HALLUCINATION_NAME = "Nitrous Oxide - Hallucination" + N2O_HALLUCINATION_METHOD = "Toggle" + N2O_HALLUCINATION_DESC = "If toggled, uses the remnants of the hallucination code to induce visions in those\ + who breathe N2O." + //CONTAMINATION_LOSS_RANDOM = "5d5" +//Plasma has a chance to be a different color. + +obj/var/contaminated = 0 + +obj/item/proc + can_contaminate() + if(flags & PLASMAGUARD) return 0 + if((flags & SUITSPACE) && !vsc.plc.PLASMAGUARD_ONLY) return 1 + if(vsc.plc.ALL_ITEM_CONTAMINATION) return 1 + else if(istype(src,/obj/item/clothing)) return 1 + else if(istype(src,/obj/item/weapon/storage/backpack)) return 1 + +/mob/living/carbon/human/proc/contaminate() + if(!pl_suit_protected()) + suit_contamination() + else if(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(l_hand) + if(l_hand.can_contaminate()) l_hand.contaminated = 1 + if(r_hand) + if(r_hand.can_contaminate()) r_hand.contaminated = 1 + if(belt) + if(belt.can_contaminate()) belt.contaminated = 1 + if(wear_id && !pl_suit_protected()) + if(wear_id.can_contaminate()) wear_id.contaminated = 1 + if(l_ear && !pl_head_protected()) + if(l_ear.can_contaminate()) l_ear.contaminated = 1 + if(r_ear && !pl_head_protected()) + if(r_ear.can_contaminate()) r_ear.contaminated = 1 + +/mob/living/carbon/human/proc/pl_effects() + if(stat >= 2) + return + if(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 + 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) + 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(vsc.plc.GENETIC_CORRUPTION) + if(rand(1,1000) < 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/pl_head_protected() + if(head) + if(head.flags & PLASMAGUARD || head.flags & HEADSPACE) return 1 + return 0 + +/mob/living/carbon/human/proc/pl_suit_protected() + if(wear_suit) + if(wear_suit.flags & PLASMAGUARD || wear_suit.flags & SUITSPACE) return 1 + return 0 + +/mob/living/carbon/human/proc/suit_contamination() + if(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 + + +turf/Entered(obj/item/I) + . = ..() + if(istype(I)) + var/datum/gas_mixture/env = return_air(1) + if(env.toxins > 0.35) + if(I.can_contaminate()) + I.contaminated = 1 \ No newline at end of file diff --git a/code/ZAS/Processing.dm b/code/ZAS/Processing.dm new file mode 100644 index 00000000000..e1015a238f0 --- /dev/null +++ b/code/ZAS/Processing.dm @@ -0,0 +1,103 @@ +#define QUANTIZE(variable) (round(variable,0.0001)) +var/explosion_halt = 0 +zone + proc/process() + if(rebuild) + if(!contents) del src + var + turf/sample = pick(contents) + list/new_contents = FloodFill(sample) + problem = 0 + for(var/turf/T in contents) + if(!(T in new_contents)) + problem = 1 + + if(problem) + var/list/rebuild_turfs = list() + for(var/turf/T in contents - new_contents) + contents -= T + rebuild_turfs += T + T.zone = null + for(var/turf/T in rebuild_turfs) + if(!T.zone) + var/zone/Z = new/zone(T) + Z.air.copy_from(air) + rebuild = 0 + + var/total_space = 0 + var/turf/space/space + + 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] + + if(space_tiles) + for(var/T in space_tiles) + if(!istype(T,/turf/space)) space_tiles -= T + total_space += length(space_tiles) + if(length(space_tiles)) + space = space_tiles[1] + + if(total_space && space) + var/old_pressure = air.return_pressure() + air.temperature_mimic(space,OPEN_HEAT_TRANSFER_COEFFICIENT,total_space) + air.remove(MOLES_CELLSTANDARD * (air.group_multiplier/40) * total_space) + 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) + + air.react(null,0) + 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." + else 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) + + 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) + air.graphic_archived = air.graphic + + air.temperature = max(TCMB,air.temperature) + + if(length(connections)) + for(var/connection/C in connections) + C.Cleanup() + if(C && !C.indirect) + if(C.A.zone.air.compare(C.B.zone.air)) + ZMerge(C.A.zone,C.B.zone) + 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)) + + +zone/proc + connected_zones() + . = list() + for(var/connection/C in connections) + var/zone/Z + if(C.A.zone == src) + Z = C.B.zone + else + Z = C.A.zone + + if(Z in .) + .[Z]++ + else + . += Z + .[Z] = 1 \ No newline at end of file diff --git a/code/ZAS/Variable Settings.dm b/code/ZAS/Variable Settings.dm new file mode 100644 index 00000000000..188f885ae9b --- /dev/null +++ b/code/ZAS/Variable Settings.dm @@ -0,0 +1,285 @@ +var/global/vs_control/vsc = new +vs_control + var + list/settings = list() + list/bitflags = list("1","2","4","8","16","32","64","128","256","512","1024") + pl_control/plc = new() + /*RPREV_REQUIRE_HEADS_ALIVE = 0 + RPREV_REQUIRE_HEADS_ALIVE_DESC = "Require the heads to be captured alive in RP Rev, rather than either dead or captured." + RPREV_REQUIRE_REVS_ALIVE = 0 + RPREV_REQUIRE_REVS_ALIVE_DESC = "Require the rev leaders to be captured alive in RP Rev, rather than either dead or captured."*/ + New() + . = ..() + settings = vars.Copy() + + var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars. + for(var/V in D.vars) + settings -= V + + for(var/V in settings) + if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC") || findtextEx(V,"_METHOD")) + settings -= V + + settings -= "settings" + settings -= "bitflags" + settings -= "plc" + + proc/ChangeSettingsDialog(mob/user,list/L) + //var/which = input(user,"Choose a setting:") in L + var/dat = "" + for(var/ch in L) + if(findtextEx(ch,"_RANDOM") || findtextEx(ch,"_DESC") || findtextEx(ch,"_METHOD") || findtextEx(ch,"_NAME")) continue + var/vw + var/vw_desc = "No Description." + var/vw_name = ch + if(ch in plc.settings) + vw = plc.vars[ch] + if("[ch]_DESC" in plc.vars) vw_desc = plc.vars["[ch]_DESC"] + if("[ch]_NAME" in plc.vars) vw_name = plc.vars["[ch]_NAME"] + else + vw = vars[ch] + if("[ch]_DESC" in vars) vw_desc = vars["[ch]_DESC"] + if("[ch]_NAME" in plc.vars) vw_name = vars["[ch]_NAME"] + dat += "[vw_name] = [vw] \[Change\]
" + dat += "[vw_desc]

" + user << browse(dat,"window=settings") + Topic(href,href_list) + if("changevar" in href_list) + ChangeSetting(usr,href_list["changevar"]) + proc/ChangeSetting(mob/user,ch) + var/vw + var/how = "Text" + if(ch in plc.settings) + vw = plc.vars[ch] + if("[ch]_METHOD" in vars) + how = plc.vars["[ch]_METHOD"] + else + if(isnum(vw)) + how = "Numeric" + else + how = "Text" + else + vw = vars[ch] + if("[ch]_METHOD" in vars) + how = vars["[ch]_METHOD"] + else + if(isnum(vw)) + how = "Numeric" + else + how = "Text" + var/newvar = vw + switch(how) + if("Numeric") + newvar = input(user,"Enter a number:","Settings",newvar) as num + if("Bit Flag") + var/flag = input(user,"Toggle which bit?","Settings") in bitflags + flag = text2num(flag) + if(newvar & flag) + newvar &= ~flag + else + newvar |= flag + if("Toggle") + newvar = !newvar + if("Text") + newvar = input(user,"Enter a string:","Settings",newvar) as text + if("Long Text") + newvar = input(user,"Enter text:","Settings",newvar) as message + vw = newvar + if(ch in plc.settings) + plc.vars[ch] = vw + else + vars[ch] = vw + if(how == "Toggle") + newvar = (newvar?"ON":"OFF") + world << "\blue [key_name(user)] changed the setting [ch] to [newvar]." + //user << "[which] has been changed to [newvar]." + if(ch in plc.settings) + ChangeSettingsDialog(user,plc.settings) + else + ChangeSettingsDialog(user,settings) + proc/RandomizeWithProbability() + for(var/V in settings) + var/newvalue + if("[V]_RANDOM" in vars) + if(isnum(vars["[V]_RANDOM"])) + newvalue = prob(vars["[V]_RANDOM"]) + else if(istext(vars["[V]_RANDOM"])) + newvalue = roll(vars["[V]_RANDOM"]) + else + newvalue = vars[V] + V = newvalue + + proc/ChangePlasma() + for(var/V in plc.settings) + plc.Randomize(V) + ////world << "Plasma randomized." + + proc/SetDefault(def) + switch(def) + if("Original") + plc.CLOTH_CONTAMINATION = 0 //If this is on, plasma does damage by getting into cloth. + + plc.ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or + + plc.PLASMAGUARD_ONLY = 0 + + //plc.CANISTER_CORROSION = 0 //If this is on, plasma must be stored in orange tanks and canisters, + + plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000. + + plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited. + + //plc.PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time. + + plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection. + + //plc.N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high. + + //plc.PLASMA_COLOR = "onturf" //Plasma can change colors yaaaay! + + //plc.PLASMA_DMG_OFFSET = 1 + //plc.PLASMA_DMG_QUOTIENT = 10 + + plc.CONTAMINATION_LOSS = 0 + if("Hazard-Low") + plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. + + plc.ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or + + plc.PLASMAGUARD_ONLY = 0 + + // plc.CANISTER_CORROSION = 0 //If this is on, plasma must be stored in orange tanks and canisters, + + plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000. + + plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited. + + // plc.PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time. + + plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection. + + // plc.N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high. + + // plc.PLASMA_COLOR = "onturf" //RBPYB + + //if(prob(20)) + // plc.PLASMA_COLOR = pick("red","yellow","blue","purple") + + //plc.PLASMA_DMG_OFFSET = 1.5 + //plc.PLASMA_DMG_QUOTIENT = 8 + + plc.CONTAMINATION_LOSS = 0.01 + + var/s = pick(plc.settings) + plc.Randomize(s) + + if("Hazard-High") + plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. + + plc.ALL_ITEM_CONTAMINATION = 0 //If this is on, any item can be contaminated, so suits and tools must be discarded or + + plc.PLASMAGUARD_ONLY = 0 + + // plc.CANISTER_CORROSION = 1 //If this is on, plasma must be stored in orange tanks and canisters, + + plc.GENETIC_CORRUPTION = 0 //Chance of genetic corruption as well as toxic damage, X in 1000. + + plc.SKIN_BURNS = 0 //Plasma has an effect similar to mustard gas on the un-suited. + + // plc.PLASMA_INJECTS_TOXINS = 0 //Plasma damage injects the toxins chemical to do damage over time. + + plc.EYE_BURNS = 0 //Plasma burns the eyes of anyone not wearing eye protection. + + // plc.N2O_REACTION = 0 //Plasma can react with N2O, making sparks and starting a fire if levels are high. + + // plc.PLASMA_COLOR = "onturf"//pick("red","yellow","blue","purple") //RBPYB + + //plc.PLASMA_DMG_OFFSET = 3 + //plc.PLASMA_DMG_QUOTIENT = 5 + + plc.CONTAMINATION_LOSS = 0.05 + + for(var/i = rand(3,5),i>0,i--) + var/s = pick(plc.settings) + plc.Randomize(s) + + if("Everything") + plc.CLOTH_CONTAMINATION = 1 //If this is on, plasma does damage by getting into cloth. + + plc.ALL_ITEM_CONTAMINATION = 1 //If this is on, any item can be contaminated, so suits and tools must be discarded or ELSE + + plc.PLASMAGUARD_ONLY = 1 + + // plc.CANISTER_CORROSION = 1 //If this is on, plasma must be stored in orange tanks and canisters, + + plc.GENETIC_CORRUPTION = 5 //Chance of genetic corruption as well as toxic damage, X in 1000. + + plc.SKIN_BURNS = 1 //Plasma has an effect similar to mustard gas on the un-suited. + + // plc.PLASMA_INJECTS_TOXINS = 1 //Plasma damage injects the toxins chemical to do damage over time. + + plc.EYE_BURNS = 1 //Plasma burns the eyes of anyone not wearing eye protection. + + // plc.N2O_REACTION = 1 //Plasma can react with N2O, making sparks and starting a fire if levels are high. + + // plc.PLASMA_COLOR = "onturf" //RBPYB + + //plc.PLASMA_DMG_OFFSET = 3 + //plc.PLASMA_DMG_QUOTIENT = 5 + + plc.CONTAMINATION_LOSS = 0.02 + /////world << "Plasma color updated." + +pl_control + var/list/settings = list() + New() + . = ..() + settings = vars.Copy() + + var/datum/D = new() //Ensure only unique vars are put through by making a datum and removing all common vars. + for(var/V in D.vars) + settings -= V + + for(var/V in settings) + if(findtextEx(V,"_RANDOM") || findtextEx(V,"_DESC")) + settings -= V + + settings -= "settings" + proc/Randomize(V) + //world << "Randomizing [V]" + var/newvalue + if("[V]_RANDOM" in vars) + if(isnum(vars["[V]_RANDOM"])) + newvalue = prob(vars["[V]_RANDOM"]) + if(newvalue) + //world << "Probability [vars["[V]_RANDOM"]]%: Success." + else + //world << "Probability [vars["[V]_RANDOM"]]%: Failure." + else if(istext(vars["[V]_RANDOM"])) + var/txt = vars["[V]_RANDOM"] + if(findtextEx(txt,"PROB")) + //world << "Probability/Roll Combo \..." + txt = dd_text2list(txt,"/") + txt[1] = dd_replacetext(txt[1],"PROB","") + var/p = text2num(txt[1]) + var/r = txt[2] + //world << "Prob:[p]% Roll:[r]" + if(prob(p)) + newvalue = roll(r) + //world << "Success. New value: [newvalue]" + else + newvalue = vars[V] + //world << "Probability check failed." + else if(findtextEx(txt,"PICK")) + txt = dd_replacetext(txt,"PICK","") + //world << "Pick: [txt]" + txt = dd_text2list(txt,",") + newvalue = pick(txt) + //world << "Picked: [newvalue]" + else + newvalue = roll(txt) + //world << "Roll: [txt] - [newvalue]" + else + newvalue = vars[V] + vars[V] = newvalue + ////world << "Plasma color updated." \ No newline at end of file diff --git a/icons/misc/debug_connect.dmi b/icons/misc/debug_connect.dmi new file mode 100644 index 0000000000000000000000000000000000000000..0c96c86daa2afb103f2562bad12ce00fc9acd60a GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ%&L%x5|`BCkOrgvg-0Y)*SYD zb9$kwhmP?^kkOrgvg-0Y)*SYD zb9$kwhmP?^kkOrgvg-0Y)*SYD zb9$kwhmP?^k