diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index 22e69cd06d9f..9ce22b500b92 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -168,13 +168,13 @@
//TANKS
/// temperature in kelvins at which a tank will start to melt
-#define TANK_MELT_TEMPERATURE 1000000
+#define TANK_MELT_TEMPERATURE 1000 + T0C
/// Tank starts leaking
-#define TANK_LEAK_PRESSURE (30.*ONE_ATMOSPHERE)
+#define TANK_LEAK_PRESSURE (20 * ONE_ATMOSPHERE + 5)
/// Tank spills all contents into atmosphere
-#define TANK_RUPTURE_PRESSURE (35.*ONE_ATMOSPHERE)
+#define TANK_RUPTURE_PRESSURE (20 * ONE_ATMOSPHERE)
/// Boom 3x3 base explosion
-#define TANK_FRAGMENT_PRESSURE (40.*ONE_ATMOSPHERE)
+#define TANK_FRAGMENT_PRESSURE (25.*ONE_ATMOSPHERE)
/// +1 for each SCALE kPa aboe threshold
#define TANK_FRAGMENT_SCALE (6.*ONE_ATMOSPHERE)
#define TANK_MAX_RELEASE_PRESSURE (ONE_ATMOSPHERE*3)
@@ -337,6 +337,7 @@
#define GAS_HYDROGEN "h2"
#define GAS_CHLORINE "cl2"
#define GAS_HYDROGEN_CHLORIDE "hcl"
+#define GAS_CO "co"
#define GAS_FLAG_DANGEROUS (1<<0)
#define GAS_FLAG_BREATH_PROC (1<<1)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 36764c6bae9f..1b4e2b5ce208 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1542,3 +1542,46 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
if(dir & WEST)
return WEST
return 0
+
+/proc/filled_turfs(atom/center, radius = 3, type = "circle", include_edge = TRUE)
+ var/turf/center_turf = get_turf(center)
+ if(radius < 0 || !center)
+ return
+ if(radius == 0)
+ return list(center_turf)
+
+ var/list/directions
+ switch(type)
+ if("square")
+ directions = GLOB.alldirs
+ if("circle")
+ directions = GLOB.cardinals
+
+ var/list/results = list(center_turf)
+ var/list/turfs_to_check = list()
+ turfs_to_check += center_turf
+ for(var/i = radius; i > 0; i--)
+ for(var/X in turfs_to_check)
+ var/turf/T = X
+ for(var/direction in directions)
+ var/turf/AdjT = get_step(T, direction)
+ if(!AdjT)
+ continue
+ if (AdjT in results) // Ignore existing turfs
+ continue
+ if(AdjT.density)
+ if(include_edge)
+ results += AdjT
+ continue
+
+ turfs_to_check += AdjT
+ results += AdjT
+ return results
+
+/proc/flame_radius(turf/epicenter, radius = 1, power = 5, fire_color = "red")
+ if(!isturf(epicenter))
+ CRASH("flame_radius used without a valid turf parameter")
+ radius = clamp(radius, 1, 50) //Sanitize inputs
+
+ for(var/turf/turf_to_flame as anything in filled_turfs(epicenter, radius, "circle"))
+ turf_to_flame.IgniteTurf(power, fire_color)
diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm
index 3e044a441c0a..a20970c48304 100644
--- a/code/controllers/subsystem/explosions.dm
+++ b/code/controllers/subsystem/explosions.dm
@@ -358,6 +358,8 @@ SUBSYSTEM_DEF(explosions)
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
flameturf += T
+ if(flame_range)
+ flame_radius(epicenter, flame_range, flame_range*2)
//--- THROW ITEMS AROUND ---
var/throw_dir = get_dir(epicenter,T)
diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm
index 19b1e2094993..27e257e4fb08 100644
--- a/code/datums/components/pellet_cloud.dm
+++ b/code/datums/components/pellet_cloud.dm
@@ -46,8 +46,6 @@
var/mob/living/shooter
/datum/component/pellet_cloud/Initialize(projectile_type=/obj/item/shrapnel, magnitude=5)
- if(!isammocasing(parent) && !isgrenade(parent) && !islandmine(parent) && !issupplypod(parent))
- return COMPONENT_INCOMPATIBLE
if(magnitude < 1)
stack_trace("Invalid magnitude [magnitude] < 1 on pellet_cloud, parent: [parent]")
@@ -57,7 +55,7 @@
if(isammocasing(parent))
num_pellets = magnitude
- else if(isgrenade(parent) || islandmine(parent) || issupplypod(parent))
+ else
radius = magnitude
/datum/component/pellet_cloud/Destroy(force, silent)
@@ -76,7 +74,7 @@
RegisterSignal(parent, COMSIG_GRENADE_PRIME, PROC_REF(create_blast_pellets))
else if(islandmine(parent))
RegisterSignal(parent, COMSIG_MINE_TRIGGERED, PROC_REF(create_blast_pellets))
- else if(issupplypod(parent))
+ else
RegisterSignal(parent, COMSIG_SUPPLYPOD_LANDED, PROC_REF(create_blast_pellets))
/datum/component/pellet_cloud/UnregisterFromParent()
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 376d1ba16d11..0dc066b7ff90 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -832,6 +832,29 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(isturf(location))
location.hotspot_expose(flame_heat, 5)
+ if(!prob(1))
+ return
+
+ for(var/dir in GLOB.cardinals)
+ var/ruined_round = FALSE
+ var/turf/test_turf = get_step(location, dir)
+ for(var/obj/to_test as obj in test_turf.contents)
+ if(istype(to_test, /obj/structure/reagent_dispensers/fueltank))
+ location.visible_message("A single ember from [src] drops gently onto [to_test]. Uh oh.")
+ to_test.fire_act()
+ ruined_round = TRUE
+ break
+ if(istype(to_test, /obj/machinery/atmospherics/components/unary/tank))
+ location.visible_message("A flash fire forms around [src]!")
+ location.IgniteTurf(flame_heat/20)
+ new /obj/effect/hotspot(location)
+ ruined_round = TRUE
+ break
+ if(ruined_round)
+ break
+
+
+
/obj/item/proc/ignition_effect(atom/A, mob/user)
if(get_temperature())
. = "[user] lights [A] with [src]."
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index 106ee2a50525..3b116ac8a75b 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -188,7 +188,14 @@
var/turf/my_target = particles[W]
if(!W)
continue
- step_towards(W,my_target)
+ var/turf/tomove = get_step_towards(W,my_target)
+ if(!step_towards(W, tomove))
+ if(isopenturf(tomove))
+ W.forceMove(tomove)
+ particles -= W
+ else
+ W.Move(tomove)
+
if(!W.reagents)
continue
W.reagents.expose(get_turf(W))
diff --git a/code/game/objects/items/shrapnel.dm b/code/game/objects/items/shrapnel.dm
index 959649c8c59b..f60513cd8645 100644
--- a/code/game/objects/items/shrapnel.dm
+++ b/code/game/objects/items/shrapnel.dm
@@ -70,6 +70,8 @@
armour_penetration = -35
dismemberment = 10
shrapnel_type = /obj/item/shrapnel/hot
+ ricochets_max = 10
+ ricochet_incidence_leeway = 0
damage_type = BURN
/obj/projectile/bullet/shrapnel/hot/on_hit(atom/target, blocked = FALSE)
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index c45911f98f4a..af60f751710a 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -6,6 +6,7 @@
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BACK
hitsound = 'sound/weapons/smash.ogg'
+ resistance_flags = FIRE_PROOF // its metal, but the gas inside isnt nessarily fireproof...
pressure_resistance = ONE_ATMOSPHERE * 5
force = 5
throwforce = 10
@@ -16,7 +17,6 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 30)
var/datum/gas_mixture/air_contents = null
var/distribute_pressure = ONE_ATMOSPHERE
- var/integrity = 3
var/volume = 70
supports_variations = VOX_VARIATION
@@ -219,8 +219,37 @@
/obj/item/tank/process()
//Allow for reactions
air_contents.react()
+ var/turf/open/current_turf = get_turf(src)
+ if(current_turf)
+ temperature_expose(current_turf.air, current_turf.air.return_temperature(), current_turf.air.return_pressure())
check_status()
+/obj/item/tank/fire_act(exposed_temperature, exposed_volume)
+ . = ..()
+ var/tank_temperature = air_contents.return_temperature()
+ tank_temperature += exposed_temperature/20 //slowly equalize with the air, since this is over an active fire, we heat up faster
+ air_contents.set_temperature(tank_temperature)
+ if(exposed_temperature > TANK_MELT_TEMPERATURE)
+ take_damage(max((exposed_temperature - TANK_MELT_TEMPERATURE)/2, 0), BURN, 0)
+ if(exposed_volume > TANK_RUPTURE_PRESSURE) // implosion
+ take_damage(max((exposed_volume - TANK_RUPTURE_PRESSURE)/2, 0), BURN, 0)
+
+
+/obj/item/tank/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ if(!isturf(loc)) //so people dont freeze to death by cold air during eva
+ return ..()
+ var/tank_temperature = air_contents.return_temperature()
+ tank_temperature = ((tank_temperature * 6) + exposed_temperature)/7 //slowly equalize with the air
+ air_contents.set_temperature(tank_temperature)
+ if(exposed_temperature > TANK_MELT_TEMPERATURE)
+ take_damage(max((exposed_temperature - TANK_MELT_TEMPERATURE)/2, 0), BURN, 0)
+ if(exposed_volume > TANK_RUPTURE_PRESSURE) // implosion
+ take_damage(max((exposed_volume - TANK_RUPTURE_PRESSURE)/2, 0), BURN, 0)
+
+/obj/item/tank/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+ . = ..()
+ shake_animation(damage_amount, max(damage_amount/2, 2))
+
/obj/item/tank/proc/check_status()
//Handle exploding, leaking, and rupturing of the tank
@@ -232,40 +261,33 @@
if(pressure > TANK_FRAGMENT_PRESSURE)
if(!istype(src.loc, /obj/item/transfer_valve))
+ message_admins("[src] ruptured explosively at [ADMIN_VERBOSEJMP(src)], last touched by [get_mob_by_key(fingerprintslast)]!")
+ log_admin("[src] ruptured explosively at [ADMIN_VERBOSEJMP(src)], last touched by [get_mob_by_key(fingerprintslast)]!")
log_bomber(get_mob_by_key(fingerprintslast), "was last key to touch", src, "which ruptured explosively")
//Give the gas a chance to build up more pressure through reacting
air_contents.react(src)
pressure = air_contents.return_pressure()
- var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE
+ var/range = (pressure-TANK_RUPTURE_PRESSURE)/TANK_FRAGMENT_SCALE
var/turf/epicenter = get_turf(loc)
- explosion(epicenter, round(range*0.25), round(range*0.5), round(range), round(range*1.5))
+ explosion(epicenter, round(range*0.1), round(range*0.5), round(range), round(range*1.5))
+
+ AddComponent(/datum/component/pellet_cloud, /obj/projectile/bullet/shrapnel/hot, round(range))
if(istype(src.loc, /obj/item/transfer_valve))
qdel(src.loc)
else
- qdel(src)
+ return obj_destruction()
else if(pressure > TANK_RUPTURE_PRESSURE || temperature > TANK_MELT_TEMPERATURE)
- if(integrity <= 0)
- var/turf/T = get_turf(src)
- if(!T)
- return
- T.assume_air(air_contents)
- playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
- qdel(src)
- else
- integrity--
-
- else if(pressure > TANK_LEAK_PRESSURE)
- if(integrity <= 0)
- var/turf/T = get_turf(src)
- if(!T)
- return
- var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25)
- T.assume_air(leaked_gas)
- else
- integrity--
+ take_damage(max((pressure - TANK_RUPTURE_PRESSURE), 0), BRUTE, 0)
+ take_damage(max((temperature - TANK_MELT_TEMPERATURE), 0), BRUTE, 0)
+
+/obj/item/tank/obj_destruction(damage_flag)
+ var/turf/T = get_turf(src)
+ if(!T)
+ return ..()
+ T.assume_air(air_contents)
+ playsound(src.loc, 'sound/effects/spray.ogg', 100, TRUE, -3)
+ return ..()
- else if(integrity < 3)
- integrity++
diff --git a/code/modules/atmospherics/auxgm/breathing_classes.dm b/code/modules/atmospherics/auxgm/breathing_classes.dm
index cfc82adbffa1..462a635bfa89 100644
--- a/code/modules/atmospherics/auxgm/breathing_classes.dm
+++ b/code/modules/atmospherics/auxgm/breathing_classes.dm
@@ -24,7 +24,7 @@
gases = list(
GAS_O2 = 1,
GAS_PLUOXIUM = 8,
- GAS_CO2 = -0.7, // CO2 isn't actually toxic, just an asphyxiant
+ GAS_CO2 = -0.2, // CO2 isn't actually toxic, just an asphyxiant
)
products = list(
GAS_CO2 = 1,
diff --git a/code/modules/atmospherics/auxgm/gas_types.dm b/code/modules/atmospherics/auxgm/gas_types.dm
index 662b88046956..0729b9ce2821 100644
--- a/code/modules/atmospherics/auxgm/gas_types.dm
+++ b/code/modules/atmospherics/auxgm/gas_types.dm
@@ -29,6 +29,17 @@
)
)
+/datum/gas/carbon_monoxide
+ id = GAS_CO
+ specific_heat = 30
+ name = "Carbon Monoxide"
+ breath_results = GAS_CO
+
+ flags = GAS_FLAG_DANGEROUS
+
+ fusion_power = 0
+ enthalpy = -110500
+
/datum/gas/carbon_dioxide //what the fuck is this?
id = GAS_CO2
specific_heat = 30
@@ -161,7 +172,7 @@
specific_heat = 10
name = "Hydrogen"
flags = GAS_FLAG_DANGEROUS
- moles_visible = MOLES_GAS_VISIBLE
+ //moles_visible = MOLES_GAS_VISIBLE
color = "#ffe"
fusion_power = 0
fire_products = list(GAS_H2O = 1)
diff --git a/code/modules/atmospherics/gasmixtures/auxgm.dm b/code/modules/atmospherics/gasmixtures/auxgm.dm
index e774d1060ec3..8e4ec3eb30e2 100644
--- a/code/modules/atmospherics/gasmixtures/auxgm.dm
+++ b/code/modules/atmospherics/gasmixtures/auxgm.dm
@@ -38,6 +38,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
/datum/gas
var/id = ""
+ /// heat capacity? thats the only explanation on what this var is
var/specific_heat = 0
var/name = ""
var/gas_overlay = "generic" //icon_state in icons/effects/atmospherics.dmi
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 236c7b040d99..6d147db95c55 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -107,9 +107,9 @@
var/datum/radio_frequency/radio_connection
//anything outright hazardous (flammable, toxic, generally Weird)
- var/list/filter_basic = list(GAS_CO2, GAS_PLASMA, GAS_NITROUS, GAS_BZ, GAS_TRITIUM, GAS_NITRYL, GAS_FREON, GAS_HYDROGEN, GAS_CHLORINE, GAS_HYDROGEN_CHLORIDE)
+ var/list/filter_basic = list(GAS_CO2, GAS_PLASMA, GAS_NITROUS, GAS_BZ, GAS_TRITIUM, GAS_NITRYL, GAS_FREON, GAS_HYDROGEN, GAS_CHLORINE, GAS_HYDROGEN_CHLORIDE, GAS_CO)
//anything that isn't o2 or n2.
- var/list/filter_extra = list(GAS_CO2, GAS_PLASMA, GAS_NITROUS, GAS_BZ, GAS_TRITIUM, GAS_NITRYL, GAS_FREON, GAS_HYDROGEN, GAS_CHLORINE, GAS_HYDROGEN_CHLORIDE, GAS_H2O, GAS_HYPERNOB, GAS_STIMULUM, GAS_PLUOXIUM)
+ var/list/filter_extra = list(GAS_CO2, GAS_PLASMA, GAS_NITROUS, GAS_BZ, GAS_TRITIUM, GAS_NITRYL, GAS_FREON, GAS_HYDROGEN, GAS_CHLORINE, GAS_HYDROGEN_CHLORIDE, GAS_H2O, GAS_HYPERNOB, GAS_STIMULUM, GAS_PLUOXIUM, GAS_CO)
var/list/TLV = list( // Breathable air.
"pressure" = new/datum/tlv(HAZARD_LOW_PRESSURE, WARNING_LOW_PRESSURE, WARNING_HIGH_PRESSURE, HAZARD_HIGH_PRESSURE), // kPa. Values are min2, min1, max1, max2
@@ -129,7 +129,8 @@
GAS_FREON = new/datum/tlv/dangerous,
GAS_HYDROGEN = new/datum/tlv/dangerous,
GAS_CHLORINE = new/datum/tlv/dangerous,
- GAS_HYDROGEN_CHLORIDE = new/datum/tlv/dangerous
+ GAS_HYDROGEN_CHLORIDE = new/datum/tlv/dangerous,
+ GAS_CO = new/datum/tlv/dangerous,
)
/obj/machinery/airalarm/server // No checks here.
@@ -151,7 +152,8 @@
GAS_FREON = new/datum/tlv/no_checks,
GAS_HYDROGEN = new/datum/tlv/no_checks,
GAS_CHLORINE = new/datum/tlv/dangerous,
- GAS_HYDROGEN_CHLORIDE = new/datum/tlv/dangerous
+ GAS_HYDROGEN_CHLORIDE = new/datum/tlv/dangerous,
+ GAS_CO = new/datum/tlv/dangerous
)
heating_manage = FALSE
@@ -174,7 +176,8 @@
GAS_FREON = new/datum/tlv/dangerous,
GAS_HYDROGEN = new/datum/tlv/dangerous,
GAS_CHLORINE = new/datum/tlv/dangerous,
- GAS_HYDROGEN_CHLORIDE = new/datum/tlv/dangerous
+ GAS_HYDROGEN_CHLORIDE = new/datum/tlv/dangerous,
+ GAS_CO = new/datum/tlv/dangerous
)
heating_manage = FALSE
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
index 774fde011889..bdb9e8adc266 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
@@ -1,4 +1,4 @@
-#define AIR_CONTENTS ((25*ONE_ATMOSPHERE)*(air_contents.return_volume())/(R_IDEAL_GAS_EQUATION*air_contents.return_temperature()))
+#define AIR_CONTENTS ((50*ONE_ATMOSPHERE)*(air_contents.return_volume())/(R_IDEAL_GAS_EQUATION*air_contents.return_temperature()))
/obj/machinery/atmospherics/components/unary/tank
icon = 'icons/obj/atmospherics/pipes/pressure_tank.dmi'
icon_state = "generic"
@@ -6,6 +6,7 @@
name = "pressure tank"
desc = "A large vessel containing pressurized gas."
+ obj_integrity = 800
max_integrity = 800
density = TRUE
layer = ABOVE_WINDOW_LAYER
@@ -15,7 +16,7 @@
var/gas_type = 0
/obj/machinery/atmospherics/components/unary/tank/New()
- ..()
+ . = ..()
var/datum/gas_mixture/air_contents = airs[1]
air_contents.set_volume(volume)
air_contents.set_temperature(T20C)
@@ -24,13 +25,81 @@
name = "[name] ([GLOB.gas_data.names[gas_type]])"
setPipingLayer(piping_layer)
+/obj/machinery/atmospherics/components/unary/tank/Initialize(mapload)
+ . = ..()
+ SSair.start_processing_machine(src, mapload)
+
+/obj/machinery/atmospherics/components/unary/tank/process_atmos()
+ var/datum/gas_mixture/air_contents = airs[1]
+
+ //handle melting
+ var/current_temp = air_contents.return_temperature()
+ if(current_temp > TANK_MELT_TEMPERATURE)
+ take_damage(max((current_temp - TANK_MELT_TEMPERATURE), 0), BRUTE, 0)
+
+ //handle external melting
+ var/turf/open/current_turf = get_turf(src)
+ if(current_turf)
+ temperature_expose(current_turf.air, current_turf.air.return_temperature(), current_turf.air.return_pressure())
+
+ update_appearance()
+
+
+/obj/machinery/atmospherics/components/unary/tank/fire_act(exposed_temperature, exposed_volume)
+ . = ..()
+ var/datum/gas_mixture/air_contents = airs[1]
+ var/tank_temperature = air_contents.return_temperature()
+ tank_temperature += exposed_temperature/20 //equalize with the air - since this means theres an active fire on the canister's tile
+ air_contents.set_temperature(tank_temperature)
+ if(exposed_temperature > TANK_MELT_TEMPERATURE)
+ take_damage(max((exposed_temperature - TANK_MELT_TEMPERATURE)/2, 0), BURN, 0)
+ if(exposed_volume > TANK_RUPTURE_PRESSURE) // implosion
+ take_damage(max((exposed_volume - TANK_RUPTURE_PRESSURE)/2, 0), BURN, 0)
+
+
+/obj/machinery/atmospherics/components/unary/tank/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ if(exposed_temperature > TANK_MELT_TEMPERATURE) //dont equalize temperature as this would affect atmos balance, we only want fires to be more dangerous
+ take_damage(max((exposed_temperature - TANK_MELT_TEMPERATURE)/2, 0), BURN, 0)
+ if(exposed_volume > TANK_RUPTURE_PRESSURE) // implosion
+ take_damage(max((exposed_volume - TANK_RUPTURE_PRESSURE)/2, 0), BURN, 0)
+
+/obj/machinery/atmospherics/components/unary/tank/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+ . = ..()
+ shake_animation(damage_amount, max(damage_amount/2, 2))
+
+/obj/machinery/atmospherics/components/unary/tank/obj_destruction(damage_flag)
+ var/datum/gas_mixture/air_contents = airs[1]
+ //Give the gas a chance to build up more pressure through reacting
+ air_contents.react(src)
+ var/pressure = air_contents.return_pressure()
+ var/range = (pressure-TANK_LEAK_PRESSURE)/TANK_FRAGMENT_SCALE
+ var/turf/epicenter = get_turf(loc)
+ if(range > 2)
+ message_admins("[src] ruptured explosively at [ADMIN_VERBOSEJMP(src)], last touched by [get_mob_by_key(fingerprintslast)]!")
+ log_admin("[src] ruptured explosively at [ADMIN_VERBOSEJMP(src)], last touched by [get_mob_by_key(fingerprintslast)]!")
+ log_bomber(get_mob_by_key(fingerprintslast), "was last key to touch", src, "which ruptured explosively")
+ investigate_log("was destroyed.", INVESTIGATE_ATMOS)
+
+ explosion(epicenter, round(range*0.05), round(range*0.5), round(range), round(range*1.5))
+
+ AddComponent(/datum/component/pellet_cloud, /obj/projectile/bullet/shrapnel/hot, round(range))
+
+ var/turf/T = get_turf(src)
+ T.assume_air(air_contents)
+ air_update_turf()
+
+ density = FALSE
+ playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
+
+ return ..()
+
/obj/machinery/atmospherics/components/unary/tank/air
icon_state = "grey"
name = "pressure tank (Air)"
/obj/machinery/atmospherics/components/unary/tank/air/New()
- ..()
+ . = ..()
var/datum/gas_mixture/air_contents = airs[1]
air_contents.set_moles(GAS_O2, AIR_CONTENTS * 0.2)
air_contents.set_moles(GAS_N2, AIR_CONTENTS * 0.8)
@@ -46,7 +115,7 @@
icon_state = "orange"
/obj/machinery/atmospherics/components/unary/tank/fuel/New()
- ..()
+ . = ..()
var/datum/gas_mixture/air_contents = airs[1]
air_contents.set_moles(GAS_O2, AIR_CONTENTS * 0.3)
air_contents.set_moles(GAS_PLASMA, AIR_CONTENTS * 0.6)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
index dc8b278959f6..55e397652e48 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
@@ -20,7 +20,7 @@
var/id_tag = null
var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing
- var/filter_types = list(GAS_CO2, GAS_BZ)
+ var/filter_types = list(GAS_CO2, GAS_BZ, GAS_CO)
var/volume_rate = 200
var/widenet = 0 //is this scrubber acting on the 3x3 area around it.
var/list/turf/adjacent_turfs = list()
@@ -310,10 +310,10 @@
icon_state = "scrub_map_on-4"
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/lavaland
- filter_types = list(GAS_CO2, GAS_PLASMA, GAS_H2O, GAS_BZ)
+ filter_types = list(GAS_CO2, GAS_PLASMA, GAS_H2O, GAS_BZ, GAS_CO)
/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer3/lavaland
- filter_types = list(GAS_CO2, GAS_PLASMA, GAS_H2O, GAS_BZ)
+ filter_types = list(GAS_CO2, GAS_PLASMA, GAS_H2O, GAS_BZ, GAS_CO)
#undef SIPHONING
#undef SCRUBBING
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index cb6a1b9b0bb0..39c525515226 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -84,6 +84,12 @@
icon_state = "black"
gas_type = GAS_CO2
+/obj/machinery/portable_atmospherics/canister/carbon_monoxide
+ name = "co canister"
+ desc = "Carbon Monoxide. Highly dangerous and flammable"
+ icon_state = "black"
+ gas_type = GAS_CO
+
/obj/machinery/portable_atmospherics/canister/toxins
name = "plasma canister"
desc = "Plasma gas. The reason YOU are here. Highly toxic."
@@ -280,11 +286,26 @@
if(pressure > 100)
. += "can-o" + num2text(pressure_display)
+/obj/machinery/portable_atmospherics/canister/fire_act(exposed_temperature, exposed_volume)
+ . = ..()
+ var/can_temperature = air_contents.return_temperature()
+ can_temperature += exposed_temperature/20 //equalize with the air - since this means theres an active fire on the canister's tile
+ air_contents.set_temperature(can_temperature)
+ if(exposed_temperature > temperature_resistance)
+ take_damage(max((exposed_temperature - temperature_resistance)/2, 0), BURN, 0)
+ if(exposed_volume > TANK_RUPTURE_PRESSURE) // implosion
+ take_damage(max((exposed_volume - TANK_RUPTURE_PRESSURE)/2, 0), BURN, 0)
+
/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- if(exposed_temperature > temperature_resistance)
- take_damage(5, BURN, 0)
+ if(exposed_temperature > temperature_resistance) //dont equalize temperature as this would affect atmos balance, we only want fires to be more dangerous
+ take_damage(max((exposed_temperature - temperature_resistance)/2, 0), BURN, 0)
+ if(exposed_volume > TANK_RUPTURE_PRESSURE) // implosion
+ take_damage(max((exposed_volume - TANK_RUPTURE_PRESSURE)/2, 0), BURN, 0)
+/obj/machinery/portable_atmospherics/canister/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
+ . = ..()
+ shake_animation(damage_amount, max(damage_amount/2, 2))
/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
@@ -320,6 +341,22 @@
/obj/machinery/portable_atmospherics/canister/proc/canister_break()
disconnect()
+
+ //Give the gas a chance to build up more pressure through reacting
+ air_contents.react(src)
+ var/pressure = air_contents.return_pressure()
+ var/range = (pressure-TANK_RUPTURE_PRESSURE)/TANK_FRAGMENT_SCALE
+ var/turf/epicenter = get_turf(loc)
+ if(range > 2)
+ message_admins("[src] ruptured explosively at [ADMIN_VERBOSEJMP(src)], last touched by [get_mob_by_key(fingerprintslast)]!")
+ log_admin("[src] ruptured explosively at [ADMIN_VERBOSEJMP(src)], last touched by [get_mob_by_key(fingerprintslast)]!")
+ log_bomber(get_mob_by_key(fingerprintslast), "was last key to touch", src, "which ruptured explosively")
+ investigate_log("was destroyed.", INVESTIGATE_ATMOS)
+
+ explosion(epicenter, round(range*0.2), round(range*0.5), round(range), round(range*1.5))
+
+ AddComponent(/datum/component/pellet_cloud, /obj/projectile/bullet/shrapnel/hot, round(range))
+
var/turf/T = get_turf(src)
T.assume_air(air_contents)
air_update_turf()
@@ -327,7 +364,6 @@
obj_break()
density = FALSE
playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
- investigate_log("was destroyed.", INVESTIGATE_ATMOS)
if(holding)
holding.forceMove(T)
@@ -351,6 +387,16 @@
valve_open = !valve_open
timing = FALSE
+ //handle melting
+ var/current_temp = air_contents.return_temperature()
+ if(current_temp > temperature_resistance)
+ take_damage(max((current_temp - temperature_resistance), 0), BRUTE, 0)
+
+ //handle external melting
+ var/turf/open/current_turf = get_turf(src)
+ if(current_turf)
+ temperature_expose(current_turf.air, current_turf.air.return_temperature(), current_turf.air.return_pressure())
+
// Handle gas transfer.
if(valve_open)
var/turf/T = get_turf(src)
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index 7505d2b8789e..4c52f643f00f 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -8,7 +8,7 @@
var/volume_rate = 1000
var/overpressure_m = 80
var/use_overlays = TRUE
- var/list/scrubbing = list(GAS_PLASMA, GAS_CO2, GAS_NITROUS, GAS_BZ, GAS_NITRYL, GAS_TRITIUM, GAS_HYPERNOB, GAS_H2O, GAS_FREON, GAS_HYDROGEN)
+ var/list/scrubbing = list(GAS_PLASMA, GAS_CO2, GAS_NITROUS, GAS_BZ, GAS_NITRYL, GAS_TRITIUM, GAS_HYPERNOB, GAS_H2O, GAS_FREON, GAS_HYDROGEN, GAS_CO)
/obj/machinery/portable_atmospherics/scrubber/Destroy()
var/turf/T = get_turf(src)
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
index 94dd221945b9..f75710b86da7 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/goliath_broodmother.dm
@@ -212,7 +212,7 @@
/mob/living/simple_animal/hostile/asteroid/elite/broodmother_child/death()
. = ..()
visible_message("[src] explodes!")
- explosion(get_turf(loc),0,0,0,flame_range = 3, adminlog = FALSE)
+ explosion(get_turf(loc),0,0,0,flame_range = 2, adminlog = FALSE)
gib()
//Tentacles have less stun time compared to regular variant, to balance being able to use them much more often. Also, 10 more damage.
diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index e63cd1298616..a4021a3216cb 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -94,6 +94,8 @@
var/sheet_left = 0 // How much is left of the sheet
var/time_per_sheet = 260
var/current_heat = 0
+ var/pollution_multiplier = 0
+ var/pollution_gas = GAS_CO
/obj/machinery/power/port_gen/pacman/Initialize()
. = ..()
@@ -171,6 +173,9 @@
overheat()
qdel(src)
+ var/turf/current_turf = get_turf(src)
+ current_turf.atmos_spawn_air("[pollution_gas]=[power_output*pollution_multiplier];TEMP=[((current_heat-32)/1.8+273.15)]")
+
/obj/machinery/power/port_gen/pacman/handleInactive()
current_heat = max(current_heat - 2, 0)
if(current_heat == 0)
@@ -291,6 +296,7 @@
circuit = /obj/item/circuitboard/machine/pacman/super
sheet_path = /obj/item/stack/sheet/mineral/uranium
power_gen = 15000
+ pollution_multiplier = 0
/obj/machinery/power/port_gen/pacman/super/overheat()
. =..()
@@ -303,6 +309,7 @@
circuit = /obj/item/circuitboard/machine/pacman/mrs
sheet_path = /obj/item/stack/sheet/mineral/diamond
power_gen = 40000
+ pollution_multiplier = 0
/obj/machinery/power/port_gen/pacman/mrs/overheat()
. =..()
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index f97dc5a5ce86..b59a1530a209 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -984,7 +984,6 @@
human_holder = src
for(var/obj/item/gun/at_risk in get_all_contents())
var/chance_to_fire = GUN_NO_SAFETY_MALFUNCTION_CHANCE_MEDIUM
- var/did_fire = FALSE
if(human_holder)
// gun is less likely to go off in a holster
if(at_risk == human_holder.s_store)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 56d248e1d447..61e8a410a17e 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -523,6 +523,11 @@
color = "#808080" // rgb: 128, 128, 128
taste_mult = 0 // oderless and tasteless
+/datum/reagent/oxygen/on_mob_life(mob/living/carbon/M)
+ . = ..()
+ if(holder.has_reagent(/datum/reagent/carbon_monoxide)) //100% o2 will help with monoxide poisioning... since we cant do that, we just do this instead
+ holder.remove_reagent(/datum/reagent/carbon_monoxide, volume*4)
+
/datum/reagent/oxygen/expose_obj(obj/O, reac_volume)
if((!O) || (!reac_volume))
return 0
@@ -598,7 +603,7 @@
name = "Sulfur"
description = "A sickly yellow solid mostly known for its nasty smell. It's actually much more helpful than it looks in biochemisty."
reagent_state = SOLID
- color = "#BF8C00" // rgb: 191, 140, 0
+ color = "#f0e518"
taste_description = "rotten eggs"
/datum/reagent/carbon
@@ -1185,6 +1190,89 @@
M.confused = min(M.confused + 2, 5)
..()
+/datum/reagent/carbon_monoxide
+ name = "Carbon Monoxide"
+ description = "A highly dangerous gas for sapients."
+ reagent_state = GAS
+ metabolization_rate = 0.7 * REAGENTS_METABOLISM
+ color = "#96898c"
+ var/accumilation
+
+/datum/reagent/carbon_monoxide/on_mob_life(mob/living/carbon/victim)
+ if(holder.has_reagent(/datum/reagent/oxygen))
+ holder.remove_reagent(/datum/reagent/carbon_monoxide, 2*REM)
+ accumilation = accumilation/4
+
+ accumilation += volume
+ switch(accumilation)
+ if(10 to 50)
+ to_chat(src, "You feel dizzy.")
+ if(50 to 150)
+ to_chat(victim, "[pick("Your head hurts.", "Your head pounds.")]")
+ victim.Dizzy(5)
+ if(150 to 250)
+ to_chat(victim, "[pick("Your head hurts!", "You feel a burning knife inside your brain!", "A wave of pain fills your head!")]")
+ victim.Stun(10)
+ victim.Dizzy(5)
+ victim.confused = (accumilation/50)
+ victim.gain_trauma(/datum/brain_trauma/mild/expressive_aphasia)
+ victim.gain_trauma(/datum/brain_trauma/mild/muscle_weakness)
+ if(250 to 350)
+ to_chat(victim, "[pick("What were you doing...?", "Where are you...?", "What's going on...?")]")
+ victim.adjustStaminaLoss(3)
+ victim.Stun(35)
+
+ victim.Dizzy(5)
+ victim.confused = (accumilation/50)
+ victim.drowsyness = (accumilation/50)
+
+ victim.adjustToxLoss(accumilation/100*REM, 0)
+
+ victim.gain_trauma(/datum/brain_trauma/mild/expressive_aphasia)
+ victim.gain_trauma(/datum/brain_trauma/mild/muscle_weakness)
+ victim.gain_trauma(/datum/brain_trauma/mild/concussion)
+ victim.gain_trauma(/datum/brain_trauma/mild/speech_impediment)
+
+ if(350 to 3000)
+ victim.Unconscious(20 SECONDS)
+
+ victim.drowsyness += (accumilation/100)
+ victim.adjustToxLoss(accumilation/100*REM, 0)
+ if(3000 to INFINITY) //anti salt measure, if they reach this, just fucking kill them at this point
+ victim.death()
+ victim.cure_trauma_type(/datum/brain_trauma/mild/muscle_weakness)
+ victim.cure_trauma_type(/datum/brain_trauma/mild/concussion)
+ victim.cure_trauma_type(/datum/brain_trauma/mild/speech_impediment)
+ victim.cure_trauma_type(/datum/brain_trauma/mild/expressive_aphasia)
+
+ qdel(src)
+ return TRUE
+ accumilation -= (metabolization_rate * victim.metabolism_efficiency)
+ if(accumilation < 0)
+ holder.remove_reagent(/datum/reagent/carbon_monoxide, volume)
+ return TRUE //to avoid a runtime
+ return ..()
+
+/datum/reagent/carbon_monoxide/expose_obj(obj/O, reac_volume)
+ if((!O) || (!reac_volume))
+ return FALSE
+ var/temp = holder ? holder.chem_temp : T20C
+ O.atmos_spawn_air("[GAS_CO]=[reac_volume/2];TEMP=[temp]")
+
+/datum/reagent/carbon_monoxide/expose_turf(turf/open/T, reac_volume)
+ if(istype(T))
+ var/temp = holder ? holder.chem_temp : T20C
+ T.atmos_spawn_air("[GAS_CO]=[reac_volume/2];TEMP=[temp]")
+ return
+
+/datum/reagent/carbon_monoxide/on_mob_delete(mob/living/living_mob)
+ var/mob/living/carbon/living_carbon = living_mob
+ living_carbon.cure_trauma_type(/datum/brain_trauma/mild/muscle_weakness)
+ living_carbon.cure_trauma_type(/datum/brain_trauma/mild/concussion)
+ living_carbon.cure_trauma_type(/datum/brain_trauma/mild/speech_impediment)
+ living_carbon.cure_trauma_type(/datum/brain_trauma/mild/expressive_aphasia)
+
+
/datum/reagent/stimulum
name = "Stimulum"
description = "An unstable experimental gas that greatly increases the energy of those that inhale it." //WS Edit -- No longer references toxin damage.
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index f6af39b201c0..fada199eeb51 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -323,6 +323,36 @@
H.reagents.add_reagent(/datum/reagent/stimulum, max(0, 5 - existing))
breath.adjust_moles(GAS_STIMULUM, -gas_breathed)
+ // Carbon Monoxide
+ var/carbon_monoxide_pp = PP(breath,GAS_CO)
+ if (carbon_monoxide_pp > gas_stimulation_min)
+ H.reagents.add_reagent(/datum/reagent/carbon_monoxide, 1)
+ var/datum/reagent/carbon_monoxide/monoxide_reagent = H.reagents.has_reagent(/datum/reagent/carbon_monoxide)
+ if(!monoxide_reagent)
+ H.reagents.add_reagent(/datum/reagent/carbon_monoxide,2)
+ switch(carbon_monoxide_pp)
+ if (0 to 20)
+ monoxide_reagent.accumilation = min(monoxide_reagent.accumilation,50)
+ if (20 to 100)
+ monoxide_reagent.accumilation = min(monoxide_reagent.accumilation, 150)
+ H.reagents.add_reagent(/datum/reagent/carbon_monoxide,2)
+ if (100 to 200)
+ monoxide_reagent.accumilation = min(monoxide_reagent.accumilation, 250)
+ H.reagents.add_reagent(/datum/reagent/carbon_monoxide,4)
+ if (200 to 400)
+ monoxide_reagent.accumilation = min(monoxide_reagent.accumilation, 250)
+ H.reagents.add_reagent(/datum/reagent/carbon_monoxide,8)
+ if (400 to INFINITY)
+ monoxide_reagent.accumilation = max(monoxide_reagent.accumilation, 450)
+ H.reagents.add_reagent(/datum/reagent/carbon_monoxide,16)
+ else
+ var/datum/reagent/carbon_monoxide/monoxide_reagent = H.reagents.has_reagent(/datum/reagent/carbon_monoxide)
+ if(monoxide_reagent)
+ monoxide_reagent.accumilation = min(monoxide_reagent.accumilation, 150)
+
+
+ breath.adjust_moles(GAS_CO, -gas_breathed)
+
/obj/item/organ/lungs/proc/handle_too_little_breath(mob/living/carbon/human/H = null, breath_pp = 0, safe_breath_min = 0, true_pp = 0)
. = 0
if(!H || !safe_breath_min) //the other args are either: Ok being 0 or Specifically handled.