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.