diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index fbde37f1d94..0b6d9ac9da6 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -87,15 +87,35 @@ name = "Alert" desc = "Something seems to have gone wrong with this alert, so report this bug please" + +//Gas alerts /obj/screen/alert/oxy - name = "Choking" + name = "Choking (No O2)" desc = "You're not getting enough oxygen. Find some good air before you pass out! \ The box in your backpack has an oxygen tank and gas mask in it." +/obj/screen/alert/too_much_oxy + name = "Choking (O2)" + desc = "There's too much oxygen in the air, and you're breathing it in! Find some good air before you pass out!" + +/obj/screen/alert/not_enough_co2 + name = "Choking (No CO2)" + desc = "You're not getting enough carbon dioxide. Find some good air before you pass out!" + +/obj/screen/alert/too_much_co2 + name = "Chocking (CO2)" + desc = "There's too much carbon dioxide in the air, and you're breathing it in! Find some good air before you pass out!" + +/obj/screen/alert/not_enough_tox + name = "Choking (No Plasma)" + desc = "You're not getting enough plama. Find some good air before you pass out!" + /obj/screen/alert/tox_in_air - name = "Toxic Gas" + name = "Choking (Plasma)" desc = "There's highly flammable, toxic plasma in the air and you're breathing it in. Find some fresh air. \ The box in your backpack has an oxygen tank and gas mask in it." +//End gas alerts + /obj/screen/alert/fat name = "Fat" diff --git a/code/datums/gas_mixture.dm b/code/datums/gas_mixture.dm index 54302f051a3..e958755dc88 100644 --- a/code/datums/gas_mixture.dm +++ b/code/datums/gas_mixture.dm @@ -948,3 +948,22 @@ What are the archived variables for? else return 0 return 1 + + + + +//Takes the amount of the gas you want to PP as an argument +//So I don't have to do some hacky switches/defines/magic strings + +//eg: +//Tox_PP = get_partial_pressure(gas_mixture.toxins) +//O2_PP = get_partial_pressure(gas_mixture.oxygen) + +//Does handle trace gases! + +/datum/gas_mixture/proc/get_breath_partial_pressure(var/gas_pressure) + var/breath_pressure = (total_moles()*R_IDEAL_GAS_EQUATION*temperature)/BREATH_VOLUME + return (gas_pressure/total_moles())*breath_pressure + + + diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index d8934a1372f..b4010cc839d 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -104,6 +104,41 @@ src.air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) return + +/* + * Plasmaman Plasma Tank + */ + +/obj/item/weapon/tank/internals/plasmaman + icon_state = "plasmaman_tank" + item_state = "plasmaman_tank" + +/obj/item/weapon/tank/internals/plasmaman/New() + ..() + + src.air_contents.toxins = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + return + +/obj/item/weapon/tank/internals/plasmaman/full/New() + ..() + + src.air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + return + + +/obj/item/weapon/tank/internals/plasmaman/belt + icon_state = "plasmaman_tank_belt" + item_state = "plasmaman_tank_belt" + slot_flags = SLOT_BELT + +/obj/item/weapon/tank/internals/plasmaman/belt/full/New() + ..() + + src.air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + return + + + /* * Emergency Oxygen */ diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index af5e35c3ff7..94823768564 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -639,6 +639,7 @@ var/global/list/g_fancy_list_of_types = null "blue wizard", "red wizard", "marisa wizard", + "plasmaman" ) var/dresscode = input("Select dress for [M]", "Robust quick dress shop") as null|anything in dresspacks if (isnull(dresscode)) @@ -1014,6 +1015,12 @@ var/global/list/g_fancy_list_of_types = null W.update_label() M.equip_to_slot_or_del(W, slot_wear_id) + if("plasmaman") + M.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/hardsuit/plasmaman(M), slot_head) + M.equip_to_slot_or_del(new /obj/item/clothing/suit/space/eva/plasmaman(M),slot_wear_suit) + M.equip_to_slot_or_del(new /obj/item/weapon/tank/internals/plasmaman/full(M),slot_back) + M.equip_to_slot_or_del(new /obj/item/clothing/mask/gas(M),slot_wear_mask) + M.regenerate_icons() diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index f178515a89b..e8746d637c6 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -1,10 +1,13 @@ //Baseline hardsuits + + /obj/item/clothing/head/helmet/space/hardsuit name = "hardsuit helmet" desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." icon_state = "hardsuit0-engineering" item_state = "eng_helm" armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75) + var/basestate = "hardsuit" var/brightness_on = 4 //luminosity when on var/on = 0 item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) @@ -17,7 +20,7 @@ user << "You cannot turn the light on while in this [user.loc]!" //To prevent some lighting anomalities. return on = !on - icon_state = "hardsuit[on]-[item_color]" + icon_state = "[basestate][on]-[item_color]" user.update_inv_head() //so our mob-overlays update if(on) user.AddLuminosity(brightness_on) diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm new file mode 100644 index 00000000000..b3e05df12ce --- /dev/null +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -0,0 +1,47 @@ + //Suits for the pink and grey skeletons! + +/obj/item/clothing/suit/space/eva/plasmaman + name = "plasmaman suit" + desc = "A special containment suit designed to protect a plasmaman's volatile body from outside exposure and quickly extinguish it in emergencies." + allowed = list(/obj/item/weapon/gun,/obj/item/ammo_casing,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/restraints/handcuffs,/obj/item/weapon/tank) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 0) + icon_state = "plasmaman_suit" + item_state = "plasmaman_suit" + var/next_extinguish = 0 + var/extinguish_cooldown = 100 + var/extinguishes_left = 10 + + +/obj/item/clothing/suit/space/eva/plasmaman/examine(mob/user) + ..() + user << "There are [extinguishes_left] extinguisher canisters left in this suit." + + +/obj/item/clothing/suit/space/eva/plasmaman/proc/Extinguish(var/mob/living/carbon/human/H) + if(!istype(H)) + return + + if(H.fire_stacks) + if(extinguishes_left) + if(next_extinguish > world.time) + return + next_extinguish = world.time + extinguish_cooldown + extinguishes_left-- + H.visible_message("[H]'s suit automatically extinguishes them!","Your suit automatically extinguishes you.") + H.ExtinguishMob() + PoolOrNew(/obj/effect/effect/water, get_turf(H)) + + +//I just want the light feature of the hardsuit helmet +/obj/item/clothing/head/helmet/space/hardsuit/plasmaman + name = "plasmaman helmet" + desc = "A special containment helmet designed to protect a plasmaman's volatile body from outside exposure and quickly extinguish it in emergencies." + icon_state = "plasmaman_helmet0-plasma" + item_color = "plasma" //needed for the helmet lighting + item_state = "plasmaman_helmet0" + flags = HEADCOVERSEYES | BLOCKHAIR | HEADCOVERSMOUTH | STOPSPRESSUREDMAGE | THICKMATERIAL + //Removed the NODROP from /helmet/space/hardsuit. + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + //Removed the HIDEFACE from /helmet/space/hardsuit + basestate = "plasmaman_helmet" + diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 090bc46f055..22e364c7d9d 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -22,6 +22,9 @@ #define COLD_GAS_DAMAGE_LEVEL_2 1.5 #define COLD_GAS_DAMAGE_LEVEL_3 3 +#define BREATHES_OXY 1 +#define BREATHES_PLASMA 2 + /datum/species var/id = null // if the game needs to manually check your race to do something not included in a proc here, it will use this var/name = null // this is the fluff name. these will be left generic (such as 'Lizardperson' for the lizard race) so servers can change them to whatever @@ -62,6 +65,26 @@ var/mob/living/list/ignored_by = list() // list of mobs that will ignore this species + //Breathing! + var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa + var/safe_oxygen_max = 0 + var/safe_co2_min = 0 + var/safe_co2_max = 10 // Yes it's an arbitrary value who cares? + var/safe_toxins_min = 0 + var/safe_toxins_max = 0.005 + var/SA_para_min = 1 //Sleeping agent + var/SA_sleep_min = 5 //Sleeping agent + + //Breath damage + var/oxy_breath_dam_min = 1 + var/oxy_breath_dam_max = 10 + var/co2_breath_dam_min = 1 + var/co2_breath_dam_max = 10 + var/tox_breath_dam_min = MIN_PLASMA_DAMAGE + var/tox_breath_dam_max = MAX_PLASMA_DAMAGE + + var/custom_fire = 0 //Whether we handle fire icons ourself + /////////// // PROCS // /////////// @@ -1064,24 +1087,26 @@ return 0 - var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa - //var/safe_oxygen_max = 140 // Maximum safe partial pressure of O2, in kPa (Not used for now) - var/safe_co2_max = 10 // Yes it's an arbitrary value who cares? - var/safe_toxins_max = 0.005 - var/SA_para_min = 1 - var/SA_sleep_min = 5 var/oxygen_used = 0 - var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME - //Partial pressure of the O2 in our breath - var/O2_pp = (breath.oxygen/breath.total_moles())*breath_pressure - // Same, but for the toxins - var/Toxins_pp = (breath.toxins/breath.total_moles())*breath_pressure - // And CO2, lets say a PP of more than 10 will be bad (It's a little less really, but eh, being passed out all round aint no fun) - var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*breath_pressure // Tweaking to fit the hacky bullshit I've done with atmo -- TLE - //var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*0.5 // The default pressure value + //Partial pressures in our breath + var/O2_pp = breath.get_breath_partial_pressure(breath.oxygen) + var/Toxins_pp = breath.get_breath_partial_pressure(breath.toxins) + var/CO2_pp = breath.get_breath_partial_pressure(breath.carbon_dioxide) - if(O2_pp < safe_oxygen_min) // Too little oxygen + + //-- OXY --// + + //Too much oxygen! //Yes, some species may not like it. + if(safe_oxygen_max && O2_pp > safe_oxygen_max && !(NOBREATH in specflags)) + var/ratio = (breath.oxygen/safe_oxygen_max) * 10 + H.adjustOxyLoss(Clamp(ratio,oxy_breath_dam_min,oxy_breath_dam_max)) + H.throw_alert("too_much_oxy") + else + H.clear_alert("too_much_oxy") + + //Too little oxygen! + if(safe_oxygen_min && O2_pp < safe_oxygen_min) if(!(NOBREATH in specflags) || (H.health <= config.health_threshold_crit)) if(prob(20)) spawn(0) H.emote("gasp") @@ -1103,8 +1128,11 @@ breath.oxygen -= oxygen_used breath.carbon_dioxide += oxygen_used + + //-- CO2 --// + //CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick. - if(CO2_pp > safe_co2_max && !(NOBREATH in specflags)) + if(safe_co2_max && CO2_pp > safe_co2_max && !(NOBREATH in specflags)) if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so. H.co2overloadtime = world.time else if(world.time - H.co2overloadtime > 120) @@ -1112,24 +1140,53 @@ H.adjustOxyLoss(3) // Lets hurt em a little, let them know we mean business if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good! H.adjustOxyLoss(8) + H.throw_alert("too_much_co2") if(prob(20)) // Lets give them some chance to know somethings not right though I guess. spawn(0) H.emote("cough") else H.co2overloadtime = 0 + H.clear_alert("too_much_co2") - if(Toxins_pp > safe_toxins_max && !(NOBREATH in specflags)) // Too much toxins + //Too little CO2! + if(safe_co2_min && CO2_pp < safe_co2_min && !(NOBREATH in specflags)) + var/ratio = (breath.oxygen/safe_co2_min) * 10 + H.adjustOxyLoss(Clamp(ratio,co2_breath_dam_min,co2_breath_dam_max)) + if(prob(20)) + spawn(0) H.emote("gasp") + H.throw_alert("not_enough_co2") + else + H.clear_alert("not_enough_co2") + + + //-- TOX --// + + //Too much toxins! + if(safe_toxins_max && Toxins_pp > safe_toxins_max && !(NOBREATH in specflags)) var/ratio = (breath.toxins/safe_toxins_max) * 10 - //adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) //Limit amount of damage toxin exposure can do per second if(H.reagents) - H.reagents.add_reagent("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) + H.reagents.add_reagent("plasma", Clamp(ratio, tox_breath_dam_min, tox_breath_dam_max)) H.throw_alert("tox_in_air") else H.clear_alert("tox_in_air") + + //Too little toxins! + if(safe_toxins_min && Toxins_pp < safe_toxins_min && !(NOBREATH in specflags)) + var/ratio = (breath.toxins/safe_toxins_min) * 10 + H.adjustOxyLoss(Clamp(ratio, tox_breath_dam_min, tox_breath_dam_max)) + if(prob(20)) + spawn(0) H.emote("gasp") + H.throw_alert("not_enough_tox") + else + H.clear_alert("not_enough_tox") + + + //-- TRACES --// + if(breath.trace_gases.len && !(NOBREATH in specflags)) // If there's some other shit in the air lets deal with it here. for(var/datum/gas/sleeping_agent/SA in breath.trace_gases) - var/SA_pp = (SA.moles/breath.total_moles())*breath_pressure + var/SA_pp = breath.get_breath_partial_pressure(SA.moles) if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit H.Paralyse(3) // 3 gives them one second to wake up and run away a bit! if(SA_pp > SA_sleep_min) // Enough to make us sleep as well diff --git a/code/modules/mob/living/carbon/human/species_types.dm b/code/modules/mob/living/carbon/human/species_types.dm index f87374ee822..1cf33cbef26 100644 --- a/code/modules/mob/living/carbon/human/species_types.dm +++ b/code/modules/mob/living/carbon/human/species_types.dm @@ -344,3 +344,52 @@ for(var/mob/M in dead_mob_list) M << "[user.name]: [message]" return "" + + +var/global/image/plasmaman_on_fire = image("icon"='icons/mob/OnFire.dmi', "icon_state"="plasmaman") + +/datum/species/plasmaman + name = "Plasbone" + id = "plasmaman" + say_mod = "rattles" + sexes = 0 + meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton + specflags = list(NOBLOOD,RADIMMUNE) + safe_oxygen_min = 0 //We don't breath this + safe_toxins_min = 16 //We breath THIS! + safe_toxins_max = 0 + custom_fire = 1 + var/skin = 0 + +/datum/species/plasmaman/skin + name = "Skinbone" + skin = 1 + +/datum/species/plasmaman/update_base_icon_state(var/mob/living/carbon/human/H) + var/base = ..() + if(base == id) + base = "[base][skin]" + return base + +/datum/species/plasmaman/spec_life(var/mob/living/carbon/human/H) + var/datum/gas_mixture/environment = H.loc.return_air() + + if(!istype(H.wear_suit, /obj/item/clothing/suit/space/eva/plasmaman) || !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/plasmaman)) + if(environment) + if((environment.oxygen /environment.total_moles()) >= 0.01) + if(!H.on_fire) + H.visible_message("[H]'s body reacts with the atmosphere and bursts into flames!","Your body reacts with the atmosphere and bursts into flame!") + H.adjust_fire_stacks(0.5) + H.IgniteMob() + else + if(H.fire_stacks) + var/obj/item/clothing/suit/space/eva/plasmaman/P = H.wear_suit + if(istype(P)) + P.Extinguish(H) + + if(H.on_fire) + H.underlays |= plasmaman_on_fire + else + //Thank god humans don't use underlays + H.underlays.Cut() + diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index c33e23bbf8e..307763e4044 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -177,6 +177,9 @@ Please contact me on #coderbus IRC. ~Carnie x remove_overlay(FIRE_LAYER) if(on_fire) + if(dna) + if(dna.species.custom_fire) + return overlays_standing[FIRE_LAYER] = image("icon"='icons/mob/OnFire.dmi', "icon_state"="Standing", "layer"=-FIRE_LAYER) apply_overlay(FIRE_LAYER) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 997387dd784..4cd512731e0 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -513,3 +513,4 @@ if(360.15 to INFINITY) //360.15 is 310.15 + 50, the temperature where you start to feel effects. //We totally need a sweat system cause it totally makes sense...~ bodytemperature += min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers + diff --git a/icons/mob/OnFire.dmi b/icons/mob/OnFire.dmi index 8bf6e2c7749..abd550d9bdd 100644 Binary files a/icons/mob/OnFire.dmi and b/icons/mob/OnFire.dmi differ diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index b35a33aa5c4..c59228819bb 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/belt.dmi b/icons/mob/belt.dmi index 140bca9a4b2..c8d92941b47 100644 Binary files a/icons/mob/belt.dmi and b/icons/mob/belt.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 04b6d65e00c..e99704b9d04 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/human.dmi b/icons/mob/human.dmi index 1f29999f1a7..362e68cd8c2 100644 Binary files a/icons/mob/human.dmi and b/icons/mob/human.dmi differ diff --git a/icons/mob/inhands/clothing_lefthand.dmi b/icons/mob/inhands/clothing_lefthand.dmi index 269ec744d08..d36bbaeda97 100644 Binary files a/icons/mob/inhands/clothing_lefthand.dmi and b/icons/mob/inhands/clothing_lefthand.dmi differ diff --git a/icons/mob/inhands/clothing_righthand.dmi b/icons/mob/inhands/clothing_righthand.dmi index 00f5e23eb0e..65366e873a4 100644 Binary files a/icons/mob/inhands/clothing_righthand.dmi and b/icons/mob/inhands/clothing_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 9a75e1666b5..92504c50588 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 17fdcfdd538..568d7f77036 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index cf8d94b9594..47c860ee148 100644 Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 1dce6fa6429..664f622584c 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index e6157b2bbb4..f4d22e41ee2 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index ccda4d6b1cd..8d358692f26 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/tank.dmi b/icons/obj/tank.dmi index 01e5983da03..a8e3f3a23f1 100644 Binary files a/icons/obj/tank.dmi and b/icons/obj/tank.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 33764a17413..79ccdfadc86 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -899,6 +899,7 @@ #include "code\modules\clothing\spacesuits\chronosuit.dm" #include "code\modules\clothing\spacesuits\hardsuit.dm" #include "code\modules\clothing\spacesuits\miscellaneous.dm" +#include "code\modules\clothing\spacesuits\plasmamen.dm" #include "code\modules\clothing\spacesuits\syndi.dm" #include "code\modules\clothing\suits\armor.dm" #include "code\modules\clothing\suits\bio.dm"