Files
S.P.L.U.R.T-Station-13/code/modules/mob/living/carbon/human/species.dm
LetterJay c53fabaf5e fixes taurs
re-enables taurs
2016-08-15 00:05:24 -05:00

1538 lines
50 KiB
Plaintext

// This code handles different species in the game.
#define HUMAN_MAX_OXYLOSS 3
#define HUMAN_CRIT_MAX_OXYLOSS (SSmob.wait/30)
#define HEAT_DAMAGE_LEVEL_1 2
#define HEAT_DAMAGE_LEVEL_2 3
#define HEAT_DAMAGE_LEVEL_3 8
#define COLD_DAMAGE_LEVEL_1 0.5
#define COLD_DAMAGE_LEVEL_2 1.5
#define COLD_DAMAGE_LEVEL_3 3
#define HEAT_GAS_DAMAGE_LEVEL_1 2
#define HEAT_GAS_DAMAGE_LEVEL_2 4
#define HEAT_GAS_DAMAGE_LEVEL_3 8
#define COLD_GAS_DAMAGE_LEVEL_1 0.5
#define COLD_GAS_DAMAGE_LEVEL_2 1.5
#define COLD_GAS_DAMAGE_LEVEL_3 3
/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/limbs_id = null //this is used if you want to use a different species limb sprites. Mainly used for angels as they look like humans.
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
var/roundstart = 0 // can this mob be chosen at roundstart? (assuming the config option is checked?)
var/default_color = "#FFF" // if alien colors are disabled, this is the color that will be used by that race
var/eyes = "eyes" // which eyes the race uses. at the moment, the only types of eyes are "eyes" (regular eyes) and "jelleyes" (three eyes)
var/sexes = 1 // whether or not the race has sexual characteristics. at the moment this is only 0 for skeletons and shadows
var/hair_color = null // this allows races to have specific hair colors... if null, it uses the H's hair/facial hair colors. if "mutcolor", it uses the H's mutant_color
var/hair_alpha = 255 // the alpha used by the hair. 255 is completely solid, 0 is transparent.
var/use_skintones = 0 // does it use skintones or not? (spoiler alert this is only used by humans)
var/exotic_blood = "" // If your race wants to bleed something other than bog standard blood, change this to reagent id.
var/exotic_bloodtype = "" //If your race uses a non standard bloodtype (A+, O-, AB-, etc)
var/meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human //What the species drops on gibbing
var/skinned_type = /obj/item/stack/sheet/animalhide/generic
var/list/no_equip = list() // slots the race can't equip stuff to
var/nojumpsuit = 0 // this is sorta... weird. it basically lets you equip stuff that usually needs jumpsuits without one, like belts and pockets and ids
var/blacklisted = 0 //Flag to exclude from green slime core species.
var/dangerous_existence = null //A flag for transformation spells that tells them "hey if you turn a person into one of these without preperation, they'll probably die!"
var/say_mod = "says" // affects the speech message
var/list/default_features = list() // Default mutant bodyparts for this species. Don't forget to set one for every mutant bodypart you allow this species to have.
var/list/mutant_bodyparts = list() // Parts of the body that are diferent enough from the standard human model that they cause clipping with some equipment
var/list/mutant_organs = list(/obj/item/organ/tongue) //Internal organs that are unique to this race.
var/speedmod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster
var/armor = 0 // overall defense for the race... or less defense, if it's negative.
var/brutemod = 1 // multiplier for brute damage
var/burnmod = 1 // multiplier for burn damage
var/coldmod = 1 // multiplier for cold damage
var/heatmod = 1 // multiplier for heat damage
var/stunmod = 1 // multiplier for stun duration
var/punchdamagelow = 0 //lowest possible punch damage
var/punchdamagehigh = 9 //highest possible punch damage
var/punchstunthreshold = 9//damage at which punches from this race will stun //yes it should be to the attacked race but it's not useful that way even if it's logical
var/siemens_coeff = 1 //base electrocution coefficient
var/exotic_damage_overlay = ""
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
var/invis_sight = SEE_INVISIBLE_LIVING
var/darksight = 2
// species flags. these can be found in flags.dm
var/list/specflags = list(EYECOLOR,HAIR,FACEHAIR,LIPS)
var/attack_verb = "punch" // punch-specific attack verb
var/sound/attack_sound = 'sound/weapons/punch1.ogg'
var/sound/miss_sound = 'sound/weapons/punchmiss.ogg'
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.05
var/SA_para_min = 1 //Sleeping agent
var/SA_sleep_min = 5 //Sleeping agent
var/BZ_trip_balls_min = 1 //BZ gas.
//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
//Flight and floating
var/override_float = 0
///////////
// PROCS //
///////////
/datum/species/New()
if(!limbs_id) //if we havent set a limbs id to use, just use our own id
limbs_id = id
..()
/datum/species/proc/random_name(gender,unique,lastname)
if(unique)
return random_unique_name(gender)
var/randname
if(gender == MALE)
randname = pick(first_names_male)
else
randname = pick(first_names_female)
if(lastname)
randname += " [lastname]"
else
randname += " [pick(last_names)]"
return randname
//Please override this locally if you want to define when what species qualifies for what rank if human authority is enforced.
/datum/species/proc/qualifies_for_rank(rank, list/features)
if(rank in command_positions)
return 0
return 1
/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species)
// Drop the items the new species can't wear
for(var/slot_id in no_equip)
var/obj/item/thing = C.get_item_by_slot(slot_id)
if(thing)
C.unEquip(thing)
if(NODISMEMBER in specflags)
C.regenerate_limbs() //if we don't handle dismemberment, we grow our missing limbs back
var/obj/item/organ/heart/heart = C.getorganslot("heart")
var/obj/item/organ/lungs/lungs = C.getorganslot("lungs")
var/obj/item/organ/appendix/appendix = C.getorganslot("appendix")
if((NOBLOOD in specflags) && heart)
heart.Remove(C)
qdel(heart)
else if((!(NOBLOOD in specflags)) && (!heart))
heart = new()
heart.Insert(C)
if((NOBREATH in specflags) && lungs)
lungs.Remove(C)
qdel(lungs)
else if((!(NOBREATH in specflags)) && (!lungs))
lungs = new()
lungs.Insert(C)
if((NOHUNGER in specflags) && appendix)
appendix.Remove(C)
qdel(appendix)
else if((!(NOHUNGER in specflags)) && (!appendix))
appendix = new()
appendix.Insert(C)
for(var/path in mutant_organs)
var/obj/item/organ/I = new path()
I.Insert(C)
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
C.dna.blood_type = exotic_bloodtype
/datum/species/proc/on_species_loss(mob/living/carbon/C)
if(C.dna.species.exotic_bloodtype)
C.dna.blood_type = random_blood_type()
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
H.remove_overlay(HAIR_LAYER)
var/obj/item/bodypart/head/HD = H.get_bodypart("head")
if(!HD) //Decapitated
return
if(H.disabilities & HUSK)
return
var/datum/sprite_accessory/S
var/list/standing = list()
var/hair_hidden = 0
var/facialhair_hidden = 0
//we check if our hat or helmet hides our facial hair.
if(H.head)
var/obj/item/I = H.head
if(I.flags_inv & HIDEFACIALHAIR)
facialhair_hidden = 1
if(H.wear_mask)
var/obj/item/clothing/mask/M = H.wear_mask
if(M.flags_inv & HIDEFACIALHAIR)
facialhair_hidden = 1
if(H.facial_hair_style && (FACEHAIR in specflags) && !facialhair_hidden)
S = facial_hair_styles_list[H.facial_hair_style]
if(S)
var/image/img_facial_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER)
if(!forced_colour)
if(hair_color)
if(hair_color == "mutcolor")
img_facial_s.color = "#" + H.dna.features["mcolor"]
else
img_facial_s.color = "#" + hair_color
else
img_facial_s.color = "#" + H.facial_hair_color
else
img_facial_s.color = forced_colour
img_facial_s.alpha = hair_alpha
standing += img_facial_s
//we check if our hat or helmet hides our hair.
if(H.head)
var/obj/item/I = H.head
if(I.flags_inv & HIDEHAIR)
hair_hidden = 1
if(H.wear_mask)
var/obj/item/clothing/mask/M = H.wear_mask
if(M.flags_inv & HIDEHAIR)
hair_hidden = 1
if(!hair_hidden)
if(!H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
standing += image("icon"='icons/mob/human_face.dmi', "icon_state" = "debrained_s", "layer" = -HAIR_LAYER)
else if(H.hair_style && (HAIR in specflags))
S = hair_styles_list[H.hair_style]
if(S)
var/image/img_hair_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER)
img_hair_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER)
if(!forced_colour)
if(hair_color)
if(hair_color == "mutcolor")
img_hair_s.color = "#" + H.dna.features["mcolor"]
else
img_hair_s.color = "#" + hair_color
else
img_hair_s.color = "#" + H.hair_color
else
img_hair_s.color = forced_colour
img_hair_s.alpha = hair_alpha
standing += img_hair_s
if(standing.len)
H.overlays_standing[HAIR_LAYER] = standing
H.apply_overlay(HAIR_LAYER)
/datum/species/proc/handle_body(mob/living/carbon/human/H)
H.remove_overlay(BODY_LAYER)
var/list/standing = list()
handle_mutant_bodyparts(H)
var/obj/item/bodypart/head/HD = H.get_bodypart("head")
// lipstick
if(H.lip_style && (LIPS in specflags) && HD)
var/image/lips = image("icon"='icons/mob/human_face.dmi', "icon_state"="lips_[H.lip_style]_s", "layer" = -BODY_LAYER)
lips.color = H.lip_color
standing += lips
// eyes
if((EYECOLOR in specflags) && HD)
var/image/img_eyes_s = image("icon" = 'icons/mob/human_face.dmi', "icon_state" = "[eyes]_s", "layer" = -BODY_LAYER)
img_eyes_s.color = "#" + H.eye_color
standing += img_eyes_s
//Underwear, Undershirts & Socks
if(H.underwear)
var/datum/sprite_accessory/underwear/U = underwear_list[H.underwear]
if(U)
standing += image("icon"=U.icon, "icon_state"="[U.icon_state]_s", "layer"=-BODY_LAYER)
if(H.undershirt)
var/datum/sprite_accessory/undershirt/U2 = undershirt_list[H.undershirt]
if(U2)
if(H.dna.species.sexes && H.gender == FEMALE)
standing += wear_female_version("[U2.icon_state]_s", U2.icon, BODY_LAYER)
else
standing += image("icon"=U2.icon, "icon_state"="[U2.icon_state]_s", "layer"=-BODY_LAYER)
if(H.socks && H.get_num_legs() >= 2)
var/datum/sprite_accessory/socks/U3 = socks_list[H.socks]
if(U3)
standing += image("icon"=U3.icon, "icon_state"="[U3.icon_state]_s", "layer"=-BODY_LAYER)
//Custom Code
if(H.dna&&H.dna.taur&&!kpcode_cantaur(id))H.dna.taur=0//VERY BAD TEMP FIX
if(H.underwear&&H.underwear!="Nude"&&H.underwear_active&& (!H.dna||!H.dna.taur) )
var/datum/sprite_accessory/underwear/U = underwear_list[H.underwear]
if(U)
standing += image("icon"=U.icon, "icon_state"="[U.icon_state]_s", "layer"=-BODY_LAYER)
else if((!H.dna || !H.dna.taur) && (!H.wear_suit || !(H.wear_suit.flags_inv&HIDEJUMPSUIT)) && (!H.w_uniform||!(H.w_uniform.body_parts_covered&GROIN)) )
if(H.dna&&H.dna.cock)
//cock codes here
var/list/cock=H.dna.cock
var/cock_mod=0
var/cock_type=cock["type"]
if(cock["has"]==H.dna.COCK_NORMAL)cock_mod="n"
else if(cock["has"]==H.dna.COCK_HYPER)cock_mod="h"
else if(cock["has"]==H.dna.COCK_DOUBLE)cock_mod="d"
if(cock_mod)
var/icon/chk=new/icon('icons/mob/cock.dmi')
var/list/available_states=chk.IconStates()
if(available_states.Find("[cock_type]_c_[cock_mod]"))
var/image/cockimtmp = image("icon"='icons/mob/cock.dmi', "icon_state"="[cock_type]_c_[cock_mod]", "layer"=-BODY_LAYER)
var/new_color = "#" + cock["color"]
cockimtmp.color = new_color
standing += cockimtmp
if(available_states.Find("[cock_type]_s_[cock_mod]"))
var/image/cockimtmp = image("icon"='icons/mob/cock.dmi', "icon_state"="[cock_type]_s_[cock_mod]", "layer"=-BODY_LAYER)
if(H.dna.special_color[2])
var/new_color = "#" + H.dna.special_color[2]
cockimtmp.color = new_color
standing += cockimtmp
if(H.dna&&H.dna.taur)
var/taur_state="[kpcode_cantaur(H.dna.mutantrace())]_overlay"
if(H.vore_womb_datum.has_people()||H.vore_stomach_datum.has_people())
taur_state+="_f"
standing += generate_colour_icon('icons/mob/special/taur.dmi',"[taur_state]",H.dna.special_color,offset_x=-16,add_layer=-BODY_LAYER)
if(standing.len)
H.overlays_standing[BODY_LAYER] = standing
H.apply_overlay(BODY_LAYER)
/datum/species/proc/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour)
var/list/bodyparts_to_add = mutant_bodyparts.Copy()
var/list/relevent_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER)
var/list/standing = list()
H.remove_overlay(BODY_BEHIND_LAYER)
H.remove_overlay(BODY_ADJ_LAYER)
H.remove_overlay(BODY_FRONT_LAYER)
if(!mutant_bodyparts)
return
var/obj/item/bodypart/head/HD = H.get_bodypart("head")
if("tail_lizard" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "tail_lizard"
if("waggingtail_lizard" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "waggingtail_lizard"
else if ("tail_lizard" in mutant_bodyparts)
bodyparts_to_add -= "waggingtail_lizard"
if("tail_human" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "tail_human"
if("waggingtail_human" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "waggingtail_human"
else if ("tail_human" in mutant_bodyparts)
bodyparts_to_add -= "waggingtail_human"
if("spines" in mutant_bodyparts)
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "spines"
if("waggingspines" in mutant_bodyparts)
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "waggingspines"
else if ("tail" in mutant_bodyparts)
bodyparts_to_add -= "waggingspines"
if("snout" in mutant_bodyparts) //Take a closer look at that snout!
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE)) || !HD || HD.status == ORGAN_ROBOTIC)
bodyparts_to_add -= "snout"
if("frills" in mutant_bodyparts)
if(!H.dna.features["frills"] || H.dna.features["frills"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || !HD || HD.status == ORGAN_ROBOTIC)
bodyparts_to_add -= "frills"
if("horns" in mutant_bodyparts)
if(!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == ORGAN_ROBOTIC)
bodyparts_to_add -= "horns"
if("ears" in mutant_bodyparts)
if(!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == ORGAN_ROBOTIC)
bodyparts_to_add -= "ears"
if("wings" in mutant_bodyparts)
if(!H.dna.features["wings"] || H.dna.features["wings"] == "None" || (H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT)))
bodyparts_to_add -= "wings"
if("wings_open" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "wings_open"
else if ("wings" in mutant_bodyparts)
bodyparts_to_add -= "wings_open"
if(!bodyparts_to_add)
return
var/g = (H.gender == FEMALE) ? "f" : "m"
var/image/I
for(var/layer in relevent_layers)
for(var/bodypart in bodyparts_to_add)
var/datum/sprite_accessory/S
switch(bodypart)
if("tail_lizard")
S = tails_list_lizard[H.dna.features["tail_lizard"]]
if("waggingtail_lizard")
S.= animated_tails_list_lizard[H.dna.features["tail_lizard"]]
if("tail_human")
S = tails_list_human[H.dna.features["tail_human"]]
if("waggingtail_human")
S.= animated_tails_list_human[H.dna.features["tail_human"]]
if("spines")
S = spines_list[H.dna.features["spines"]]
if("waggingspines")
S.= animated_spines_list[H.dna.features["spines"]]
if("snout")
S = snouts_list[H.dna.features["snout"]]
if("frills")
S = frills_list[H.dna.features["frills"]]
if("horns")
S = horns_list[H.dna.features["horns"]]
if("ears")
S = ears_list[H.dna.features["ears"]]
if("body_markings")
S = body_markings_list[H.dna.features["body_markings"]]
if("wings")
S = wings_list[H.dna.features["wings"]]
if("wingsopen")
S = wings_open_list[H.dna.features["wings"]]
if(!S || S.icon_state == "none")
continue
//A little rename so we don't have to use tail_lizard or tail_human when naming the sprites.
if(bodypart == "tail_lizard" || bodypart == "tail_human")
bodypart = "tail"
else if(bodypart == "waggingtail_lizard" || bodypart == "waggingtail_human")
bodypart = "waggingtail"
var/icon_string
if(S.gender_specific)
icon_string = "[g]_[bodypart]_[S.icon_state]_[layer]"
else
icon_string = "m_[bodypart]_[S.icon_state]_[layer]"
I = image("icon" = S.icon, "icon_state" = icon_string, "layer" =- layer)
if(S.center)
I = center_image(I,S.dimension_x,S.dimension_y)
if(!(H.disabilities & HUSK))
if(!forced_colour)
switch(S.color_src)
if(MUTCOLORS)
if(fixed_mut_color)
I.color = "#[fixed_mut_color]"
else
I.color = "#[H.dna.features["mcolor"]]"
if(HAIR)
if(hair_color == "mutcolor")
I.color = "#[H.dna.features["mcolor"]]"
else
I.color = "#[H.hair_color]"
if(FACEHAIR)
I.color = "#[H.facial_hair_color]"
if(EYECOLOR)
I.color = "#[H.eye_color]"
else
I.color = forced_colour
standing += I
if(S.hasinner)
if(S.gender_specific)
icon_string = "[g]_[bodypart]inner_[S.icon_state]_[layer]"
else
icon_string = "m_[bodypart]inner_[S.icon_state]_[layer]"
I = image("icon" = S.icon, "icon_state" = icon_string, "layer" =- layer)
if(S.center)
I = center_image(I,S.dimension_x,S.dimension_y)
standing += I
H.overlays_standing[layer] = standing.Copy()
standing = list()
H.apply_overlay(BODY_BEHIND_LAYER)
H.apply_overlay(BODY_ADJ_LAYER)
H.apply_overlay(BODY_FRONT_LAYER)
/datum/species/proc/spec_life(mob/living/carbon/human/H)
if(NOBREATH in specflags)
H.setOxyLoss(0)
H.losebreath = 0
var/takes_crit_damage = (!(NOCRITDAMAGE in specflags))
if((H.health < config.health_threshold_crit) && takes_crit_damage)
H.adjustBruteLoss(1)
/datum/species/proc/spec_death(gibbed, mob/living/carbon/human/H)
return
/datum/species/proc/auto_equip(mob/living/carbon/human/H)
// handles the equipping of species-specific gear
return
/datum/species/proc/can_equip(obj/item/I, slot, disable_warning, mob/living/carbon/human/H)
if(slot in no_equip)
if(!I.species_exception || !is_type_in_list(src, I.species_exception))
return 0
var/R = H.has_right_hand()
var/L = H.has_left_hand()
var/num_arms = H.get_num_arms()
var/num_legs = H.get_num_legs()
switch(slot)
if(slot_l_hand)
if(H.l_hand)
return 0
if(!L)
return 0
return 1
if(slot_r_hand)
if(H.r_hand)
return 0
if(!R)
return 0
return 1
if(slot_wear_mask)
if(H.wear_mask)
return 0
if( !(I.slot_flags & SLOT_MASK) )
return 0
if(!H.get_bodypart("head"))
return 0
return 1
if(slot_back)
if(H.back)
return 0
if( !(I.slot_flags & SLOT_BACK) )
return 0
return 1
if(slot_wear_suit)
if(H.wear_suit)
return 0
if( !(I.slot_flags & SLOT_OCLOTHING) )
return 0
return 1
if(slot_gloves)
if(H.gloves)
return 0
if( !(I.slot_flags & SLOT_GLOVES) )
return 0
if(num_arms < 2)
return 0
return 1
if(slot_shoes)
if(H.shoes)
return 0
if( !(I.slot_flags & SLOT_FEET) )
return 0
if(num_legs < 2)
return 0
return 1
if(slot_belt)
if(H.belt)
return 0
if(!H.w_uniform && !nojumpsuit)
if(!disable_warning)
H << "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return 0
if( !(I.slot_flags & SLOT_BELT) )
return
return 1
if(slot_glasses)
if(H.glasses)
return 0
if( !(I.slot_flags & SLOT_EYES) )
return 0
if(!H.get_bodypart("head"))
return 0
return 1
if(slot_head)
if(H.head)
return 0
if( !(I.slot_flags & SLOT_HEAD) )
return 0
if(!H.get_bodypart("head"))
return 0
return 1
if(slot_ears)
if(H.ears)
return 0
if( !(I.slot_flags & SLOT_EARS) )
return 0
if(!H.get_bodypart("head"))
return 0
return 1
if(slot_w_uniform)
if(H.w_uniform)
return 0
if( !(I.slot_flags & SLOT_ICLOTHING) )
return 0
return 1
if(slot_wear_id)
if(H.wear_id)
return 0
if(!H.w_uniform && !nojumpsuit)
if(!disable_warning)
H << "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return 0
if( !(I.slot_flags & SLOT_ID) )
return 0
return 1
if(slot_l_store)
if(I.flags & NODROP) //Pockets aren't visible, so you can't move NODROP items into them.
return 0
if(H.l_store)
return 0
if(!H.w_uniform && !nojumpsuit)
if(!disable_warning)
H << "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return 0
if(I.slot_flags & SLOT_DENYPOCKET)
return
if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) )
return 1
if(slot_r_store)
if(I.flags & NODROP)
return 0
if(H.r_store)
return 0
if(!H.w_uniform && !nojumpsuit)
if(!disable_warning)
H << "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>"
return 0
if(I.slot_flags & SLOT_DENYPOCKET)
return 0
if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) )
return 1
return 0
if(slot_s_store)
if(I.flags & NODROP)
return 0
if(H.s_store)
return 0
if(!H.wear_suit)
if(!disable_warning)
H << "<span class='warning'>You need a suit before you can attach this [I.name]!</span>"
return 0
if(!H.wear_suit.allowed)
if(!disable_warning)
H << "You somehow have a suit with no defined allowed items for suit storage, stop that."
return 0
if(I.w_class > 4)
if(!disable_warning)
H << "The [I.name] is too big to attach." //should be src?
return 0
if( istype(I, /obj/item/device/pda) || istype(I, /obj/item/weapon/pen) || is_type_in_list(I, H.wear_suit.allowed) )
return 1
return 0
if(slot_handcuffed)
if(H.handcuffed)
return 0
if(!istype(I, /obj/item/weapon/restraints/handcuffs))
return 0
if(num_arms < 2)
return 0
return 1
if(slot_legcuffed)
if(H.legcuffed)
return 0
if(!istype(I, /obj/item/weapon/restraints/legcuffs))
return 0
if(num_legs < 2)
return 0
return 1
if(slot_in_backpack)
if (H.back && istype(H.back, /obj/item/weapon/storage/backpack))
var/obj/item/weapon/storage/backpack/B = H.back
if(B.contents.len < B.storage_slots && I.w_class <= B.max_w_class)
return 1
return 0
return 0 //Unsupported slot
/datum/species/proc/before_equip_job(datum/job/J, mob/living/carbon/human/H)
return
/datum/species/proc/after_equip_job(datum/job/J, mob/living/carbon/human/H)
return
/datum/species/proc/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(chem.id == exotic_blood)
H.blood_volume = min(H.blood_volume + round(chem.volume, 0.1), BLOOD_VOLUME_MAXIMUM)
H.reagents.remove_reagent(chem.id)
return 1
return 0
/datum/species/proc/handle_speech(message, mob/living/carbon/human/H)
return message
//return a list of spans or an empty list
/datum/species/proc/get_spans()
return list()
/datum/species/proc/check_weakness(obj/item/weapon, mob/living/attacker)
return 0
////////
//LIFE//
////////
/datum/species/proc/handle_chemicals_in_body(mob/living/carbon/human/H)
//The fucking FAT mutation is the dumbest shit ever. It makes the code so difficult to work with
if(H.disabilities & FAT)
if(H.overeatduration < 100)
H << "<span class='notice'>You feel fit again!</span>"
H.disabilities &= ~FAT
H.update_inv_w_uniform()
H.update_inv_wear_suit()
else
if(H.overeatduration > 500)
H << "<span class='danger'>You suddenly feel blubbery!</span>"
H.disabilities |= FAT
H.update_inv_w_uniform()
H.update_inv_wear_suit()
// nutrition decrease and satiety
if (H.nutrition > 0 && H.stat != DEAD && \
H.dna && H.dna.species && (!(NOHUNGER in H.dna.species.specflags)))
// THEY HUNGER
var/hunger_rate = HUNGER_FACTOR
if(H.satiety > 0)
H.satiety--
if(H.satiety < 0)
H.satiety++
if(prob(round(-H.satiety/40)))
H.Jitter(5)
hunger_rate = 3 * HUNGER_FACTOR
H.nutrition = max(0, H.nutrition - hunger_rate)
if (H.nutrition > NUTRITION_LEVEL_FULL)
if(H.overeatduration < 600) //capped so people don't take forever to unfat
H.overeatduration++
else
if(H.overeatduration > 1)
H.overeatduration -= 2 //doubled the unfat rate
//metabolism change
if(H.nutrition > NUTRITION_LEVEL_FAT)
H.metabolism_efficiency = 1
else if(H.nutrition > NUTRITION_LEVEL_FED && H.satiety > 80)
if(H.metabolism_efficiency != 1.25)
H << "<span class='notice'>You feel vigorous.</span>"
H.metabolism_efficiency = 1.25
else if(H.nutrition < NUTRITION_LEVEL_STARVING + 50)
if(H.metabolism_efficiency != 0.8)
H << "<span class='notice'>You feel sluggish.</span>"
H.metabolism_efficiency = 0.8
else
if(H.metabolism_efficiency == 1.25)
H << "<span class='notice'>You no longer feel vigorous.</span>"
H.metabolism_efficiency = 1
switch(H.nutrition)
if(NUTRITION_LEVEL_FULL to INFINITY)
H.throw_alert("nutrition", /obj/screen/alert/fat)
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FULL)
H.clear_alert("nutrition")
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
H.throw_alert("nutrition", /obj/screen/alert/hungry)
else
H.throw_alert("nutrition", /obj/screen/alert/starving)
/datum/species/proc/update_sight(mob/living/carbon/human/H)
H.sight = initial(H.sight)
H.see_in_dark = darksight
H.see_invisible = invis_sight
if(H.client.eye != H)
var/atom/A = H.client.eye
if(A.update_remote_sight(H)) //returns 1 if we override all other sight updates.
return
for(var/obj/item/organ/cyberimp/eyes/E in H.internal_organs)
H.sight |= E.sight_flags
if(E.dark_view)
H.see_in_dark = E.dark_view
if(E.see_invisible)
H.see_invisible = min(H.see_invisible, E.see_invisible)
if(H.glasses)
var/obj/item/clothing/glasses/G = H.glasses
H.sight |= G.vision_flags
H.see_in_dark = max(G.darkness_view, H.see_in_dark)
if(G.invis_override)
H.see_invisible = G.invis_override
else
H.see_invisible = min(G.invis_view, H.see_invisible)
for(var/X in H.dna.mutations)
var/datum/mutation/M = X
if(M.name == XRAY)
H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
H.see_in_dark = max(H.see_in_dark, 8)
if(H.see_override) //Override all
H.see_invisible = H.see_override
/datum/species/proc/update_health_hud(mob/living/carbon/human/H)
return 0
/datum/species/proc/handle_mutations_and_radiation(mob/living/carbon/human/H)
if(!(RADIMMUNE in specflags))
if(H.radiation)
if (H.radiation > 100)
H.Weaken(10)
H << "<span class='danger'>You feel weak.</span>"
H.emote("collapse")
switch(H.radiation)
if(50 to 75)
if(prob(5))
H.Weaken(3)
H << "<span class='danger'>You feel weak.</span>"
H.emote("collapse")
if(prob(15))
if(!( H.hair_style == "Shaved") || !(H.hair_style == "Bald") || (HAIR in specflags))
H << "<span class='danger'>Your hair starts to fall out in clumps...<span>"
spawn(50)
H.facial_hair_style = "Shaved"
H.hair_style = "Bald"
H.update_hair()
if(75 to 100)
if(prob(1))
H << "<span class='danger'>You mutate!</span>"
randmutb(H)
H.emote("gasp")
H.domutcheck()
return 0
return 1
////////////////
// MOVE SPEED //
////////////////
/datum/species/proc/movement_delay(mob/living/carbon/human/H)
. = 0
if(H.status_flags & GOTTAGOFAST)
. -= 1
if(H.status_flags & GOTTAGOREALLYFAST)
. -= 2
if(!(H.status_flags & IGNORESLOWDOWN))
if(!has_gravity(H))
if(FLYING in specflags)
. += speedmod
return
// If there's no gravity we have the sanic speed of jetpack.
var/obj/item/weapon/tank/jetpack/J = H.back
var/obj/item/clothing/suit/space/hardsuit/C = H.wear_suit
if(!istype(J) && istype(C))
J = C.jetpack
if(istype(J) && J.allow_thrust(0.01, H))
. -= 2
else
var/obj/item/organ/cyberimp/chest/thrusters/T = H.getorganslot("thrusters")
if(istype(T) && T.allow_thrust(0.01, H))
. -= 2
else
var/health_deficiency = (100 - H.health + H.staminaloss)
if(health_deficiency >= 40)
. += (health_deficiency / 25)
var/hungry = (500 - H.nutrition) / 5 // So overeat would be 100 and default level would be 80
if(hungry >= 70)
. += hungry / 50
if(H.wear_suit)
. += H.wear_suit.slowdown
if(H.shoes)
. += H.shoes.slowdown
if(H.back)
. += H.back.slowdown
if(H.l_hand && (H.l_hand.flags & HANDSLOW))
. += H.l_hand.slowdown
if(H.r_hand && (H.r_hand.flags & HANDSLOW))
. += H.r_hand.slowdown
if((H.disabilities & FAT))
. += 1.5
if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT)
. += (BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR
if(!(FLYING in specflags))
var/leg_amount = H.get_num_legs()
. += 6 - 3*leg_amount //the fewer the legs, the slower the mob
if(!leg_amount)
. += 6 - 3*H.get_num_arms() //crawling is harder with fewer arms
. += speedmod
//////////////////
// ATTACK PROCS //
//////////////////
/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H)
CHECK_DNA_AND_SPECIES(M)
CHECK_DNA_AND_SPECIES(H)
if(!istype(M)) //sanity check for drones.
return
if((M != H) && M.a_intent != "help" && H.check_shields(0, M.name, attack_type = UNARMED_ATTACK))
add_logs(M, H, "attempted to touch")
H.visible_message("<span class='warning'>[M] attempted to touch [H]!</span>")
return 0
var/datum/martial_art/attacker_style = M.martial_art
switch(M.a_intent)
if("help")
if(H.health >= 0 && !(H.status_flags & FAKEDEATH))
H.help_shake_act(M)
if(H != M)
add_logs(M, H, "shaked")
return 1
else
var/we_breathe = (!(NOBREATH in M.dna.species.specflags))
var/we_lung = M.getorganslot("lungs")
if(we_breathe && we_lung)
M.do_cpr(H)
else if(we_breathe && !we_lung)
M << "<span class='warning'>You have no lungs to breathe with, so you cannot peform CPR.</span>"
else
M << "<span class='notice'>You do not breathe, so you cannot perform CPR.</span>"
if("grab")
if(attacker_style && attacker_style.grab_act(M,H))
return 1
else
H.grabbedby(M)
return 1
if("harm")
if(attacker_style && attacker_style.harm_act(M,H))
return 1
else
M.do_attack_animation(H)
var/atk_verb = M.dna.species.attack_verb
if(H.lying)
atk_verb = "kick"
var/damage = rand(M.dna.species.punchdamagelow, M.dna.species.punchdamagehigh)
var/obj/item/bodypart/affecting = H.get_bodypart(ran_zone(M.zone_selected))
if(!damage || !affecting)
playsound(H.loc, M.dna.species.miss_sound, 25, 1, -1)
H.visible_message("<span class='warning'>[M] has attempted to [atk_verb] [H]!</span>")
return 0
var/armor_block = H.run_armor_check(affecting, "melee")
playsound(H.loc, M.dna.species.attack_sound, 25, 1, -1)
H.visible_message("<span class='danger'>[M] has [atk_verb]ed [H]!</span>", \
"<span class='userdanger'>[M] has [atk_verb]ed [H]!</span>")
H.apply_damage(damage, BRUTE, affecting, armor_block)
add_logs(M, H, "punched")
if((H.stat != DEAD) && damage >= M.dna.species.punchstunthreshold)
H.visible_message("<span class='danger'>[M] has weakened [H]!</span>", \
"<span class='userdanger'>[M] has weakened [H]!</span>")
H.apply_effect(4, WEAKEN, armor_block)
H.forcesay(hit_appends)
else if(H.lying)
H.forcesay(hit_appends)
if("disarm")
if(attacker_style && attacker_style.disarm_act(M,H))
return 1
else
M.do_attack_animation(H)
add_logs(M, H, "disarmed")
if(H.w_uniform)
H.w_uniform.add_fingerprint(M)
var/obj/item/bodypart/affecting = H.get_bodypart(ran_zone(M.zone_selected))
var/randn = rand(1, 100)
if(randn <= 25)
playsound(H, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
H.visible_message("<span class='danger'>[M] has pushed [H]!</span>",
"<span class='userdanger'>[M] has pushed [H]!</span>")
H.apply_effect(2, WEAKEN, H.run_armor_check(affecting, "melee", "Your armor prevents your fall!", "Your armor softens your fall!"))
H.forcesay(hit_appends)
return
var/talked = 0 // BubbleWrap
if(randn <= 60)
//BubbleWrap: Disarming breaks a pull
if(H.pulling)
H.visible_message("<span class='warning'>[M] has broken [H]'s grip on [H.pulling]!</span>")
talked = 1
H.stop_pulling()
//End BubbleWrap
if(!talked) //BubbleWrap
if(H.drop_item())
H.visible_message("<span class='danger'>[M] has disarmed [H]!</span>", \
"<span class='userdanger'>[M] has disarmed [H]!</span>")
playsound(H, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
return
playsound(H, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
H.visible_message("<span class='danger'>[M] attempted to disarm [H]!</span>", \
"<span class='userdanger'>[M] attemped to disarm [H]!</span>")
return
/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, target_area, mob/living/carbon/human/H)
// Allows you to put in item-specific reactions based on species
if(user != H)
user.do_attack_animation(H)
if(H.check_shields(I.force, "the [I.name]", I, MELEE_ATTACK, I.armour_penetration))
return 0
var/hit_area
if(!affecting) //Something went wrong. Maybe the limb is missing?
H.visible_message("<span class='danger'>[user] has attempted to attack [H] with [I]!</span>", \
"<span class='userdanger'>[user] has attempted to attack [H] with [I]!</span>")
playsound(H, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
return 0
hit_area = affecting.name
var/def_zone = affecting.body_zone
var/armor_block = H.run_armor_check(affecting, "melee", "<span class='notice'>Your armor has protected your [hit_area].</span>", "<span class='notice'>Your armor has softened a hit to your [hit_area].</span>",I.armour_penetration)
armor_block = min(90,armor_block) //cap damage reduction at 90%
var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords)
var/weakness = H.check_weakness(I, user)
apply_damage(I.force * weakness, I.damtype, def_zone, armor_block, H)
H.send_item_attack_message(I, user, hit_area)
if(!I.force)
return 0 //item force is zero
//dismemberment
var/probability = I.get_dismemberment_chance(affecting)
if(prob(probability) || ((EASYDISMEMBER in specflags) && prob(2*probability)))
if(affecting.dismember(I.damtype))
I.add_mob_blood(H)
playsound(get_turf(H), I.get_dismember_sound(), 80, 1)
var/bloody = 0
if(((I.damtype == BRUTE) && I.force && prob(25 + (I.force * 2))))
if(affecting.status == ORGAN_ORGANIC)
I.add_mob_blood(H) //Make the weapon bloody, not the person.
if(prob(I.force * 2)) //blood spatter!
bloody = 1
var/turf/location = H.loc
if(istype(location))
H.add_splatter_floor(location)
if(get_dist(user, H) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(H)
switch(hit_area)
if("head") //Harder to score a stun but if you do it lasts a bit longer
if(H.stat == CONSCIOUS && armor_block < 50)
if(prob(I.force))
H.visible_message("<span class='danger'>[H] has been knocked unconscious!</span>", \
"<span class='userdanger'>[H] has been knocked unconscious!</span>")
H.apply_effect(20, PARALYZE, armor_block)
if(prob(I.force + ((100 - H.health)/2)) && H != user && I.damtype == BRUTE)
ticker.mode.remove_revolutionary(H.mind)
if(bloody) //Apply blood
if(H.wear_mask)
H.wear_mask.add_mob_blood(H)
H.update_inv_wear_mask()
if(H.head)
H.head.add_mob_blood(H)
H.update_inv_head()
if(H.glasses && prob(33))
H.glasses.add_mob_blood(H)
H.update_inv_glasses()
if("chest") //Easier to score a stun but lasts less time
if(H.stat == CONSCIOUS && I.force && prob(I.force + 10))
H.visible_message("<span class='danger'>[H] has been knocked down!</span>", \
"<span class='userdanger'>[H] has been knocked down!</span>")
H.apply_effect(5, WEAKEN, armor_block)
if(bloody)
if(H.wear_suit)
H.wear_suit.add_mob_blood(H)
H.update_inv_wear_suit()
if(H.w_uniform)
H.w_uniform.add_mob_blood(H)
H.update_inv_w_uniform()
if(Iforce > 10 || Iforce >= 5 && prob(33))
H.forcesay(hit_appends) //forcesay checks stat already.
return TRUE
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H)
blocked = (100-(blocked+armor))/100
if(!damage || blocked <= 0)
return 0
var/obj/item/bodypart/organ = null
if(islimb(def_zone))
organ = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
organ = H.get_bodypart(check_zone(def_zone))
if(!organ)
return 0
damage = (damage * blocked)
switch(damagetype)
if(BRUTE)
H.damageoverlaytemp = 20
if(organ.take_damage(damage*brutemod, 0))
H.update_damage_overlays(0)
if(BURN)
H.damageoverlaytemp = 20
if(organ.take_damage(0, damage*burnmod))
H.update_damage_overlays(0)
if(TOX)
H.adjustToxLoss(damage)
if(OXY)
H.adjustOxyLoss(damage)
if(CLONE)
H.adjustCloneLoss(damage)
if(STAMINA)
H.adjustStaminaLoss(damage)
return 1
/datum/species/proc/on_hit(obj/item/projectile/proj_type, mob/living/carbon/human/H)
// called when hit by a projectile
switch(proj_type)
if(/obj/item/projectile/energy/floramut) // overwritten by plants/pods
H.show_message("<span class='notice'>The radiation beam dissipates harmlessly through your body.</span>")
if(/obj/item/projectile/energy/florayield)
H.show_message("<span class='notice'>The radiation beam dissipates harmlessly through your body.</span>")
return
/////////////
//BREATHING//
/////////////
/datum/species/proc/breathe(mob/living/carbon/human/H)
if(NOBREATH in specflags)
return TRUE
/datum/species/proc/check_breath(datum/gas_mixture/breath, var/mob/living/carbon/human/H)
if((H.status_flags & GODMODE))
return
var/lungs = H.getorganslot("lungs")
if(!breath || (breath.total_moles() == 0) || !lungs)
if(H.reagents.has_reagent("epinephrine") && lungs)
return
if(H.health >= config.health_threshold_crit)
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
if(!lungs)
H.adjustOxyLoss(1)
else if(!(NOCRITDAMAGE in specflags))
H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
H.failed_last_breath = 1
if(safe_oxygen_min)
H.throw_alert("oxy", /obj/screen/alert/oxy)
else if(safe_toxins_min)
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else if(safe_co2_min)
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
return 0
var/gas_breathed = 0
var/list/breath_gases = breath.gases
breath.assert_gases("o2", "plasma", "co2", "n2o", "bz")
//Partial pressures in our breath
var/O2_pp = breath.get_breath_partial_pressure(breath_gases["o2"][MOLES])
var/Toxins_pp = breath.get_breath_partial_pressure(breath_gases["plasma"][MOLES])
var/CO2_pp = breath.get_breath_partial_pressure(breath_gases["co2"][MOLES])
//-- OXY --//
//Too much oxygen! //Yes, some species may not like it.
if(safe_oxygen_max)
if(O2_pp > safe_oxygen_max && !(NOBREATH in specflags))
var/ratio = (breath_gases["o2"][MOLES]/safe_oxygen_max) * 10
H.adjustOxyLoss(Clamp(ratio,oxy_breath_dam_min,oxy_breath_dam_max))
H.throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
else
H.clear_alert("too_much_oxy")
//Too little oxygen!
if(safe_oxygen_min)
if(O2_pp < safe_oxygen_min)
gas_breathed = handle_too_little_breath(H,O2_pp,safe_oxygen_min,breath_gases["o2"][MOLES])
H.throw_alert("oxy", /obj/screen/alert/oxy)
else
H.failed_last_breath = 0
if(H.getOxyLoss())
H.adjustOxyLoss(-5)
gas_breathed = breath_gases["o2"][MOLES]
H.clear_alert("oxy")
//Exhale
breath_gases["o2"][MOLES] -= gas_breathed
breath_gases["co2"][MOLES] += gas_breathed
gas_breathed = 0
//-- 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(safe_co2_max)
if(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)
H.Paralyse(3)
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", /obj/screen/alert/too_much_co2)
if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
H.emote("cough")
else
H.co2overloadtime = 0
H.clear_alert("too_much_co2")
//Too little CO2!
if(safe_co2_min)
if(CO2_pp < safe_co2_min)
gas_breathed = handle_too_little_breath(H,CO2_pp, safe_co2_min,breath_gases["co2"][MOLES])
H.throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2)
else
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
gas_breathed = breath_gases["co2"][MOLES]
H.clear_alert("not_enough_co2")
//Exhale
breath_gases["co2"][MOLES] -= gas_breathed
breath_gases["o2"][MOLES] += gas_breathed
gas_breathed = 0
//-- TOX --//
//Too much toxins!
if(safe_toxins_max)
if(Toxins_pp > safe_toxins_max && !(NOBREATH in specflags))
var/ratio = (breath_gases["plasma"][MOLES]/safe_toxins_max) * 10
if(H.reagents)
H.reagents.add_reagent("plasma", Clamp(ratio, tox_breath_dam_min, tox_breath_dam_max))
H.throw_alert("tox_in_air", /obj/screen/alert/tox_in_air)
else
H.clear_alert("tox_in_air")
//Too little toxins!
if(safe_toxins_min)
if(Toxins_pp < safe_toxins_min && !(NOBREATH in specflags))
gas_breathed = handle_too_little_breath(H,Toxins_pp, safe_toxins_min, breath_gases["plasma"][MOLES])
H.throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox)
else
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
gas_breathed = breath_gases["plasma"][MOLES]
H.clear_alert("not_enough_tox")
//Exhale
breath_gases["plasma"][MOLES] -= gas_breathed
breath_gases["co2"][MOLES] += gas_breathed
gas_breathed = 0
//-- TRACES --//
if(breath && !(NOBREATH in specflags)) // If there's some other shit in the air lets deal with it here.
// N2O
var/SA_pp = breath.get_breath_partial_pressure(breath_gases["n2o"][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
H.Sleeping(max(H.sleeping+2, 10))
else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
if(prob(20))
H.emote(pick("giggle", "laugh"))
// BZ
var/bz_pp = breath.get_breath_partial_pressure(breath_gases["bz"][MOLES])
if(bz_pp > BZ_trip_balls_min)
H.hallucination += 20
if(prob(33))
H.adjustBrainLoss(3)
else if(bz_pp > 0.01)
H.hallucination += 5//Removed at 2 per tick so this will slowly build up
handle_breath_temperature(breath, H)
breath.garbage_collect()
return 1
//Returns the amount of true_pp we breathed
/datum/species/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.
return 0
if(!(NOBREATH in specflags) || (H.health <= config.health_threshold_crit))
if(prob(20))
H.emote("gasp")
if(breath_pp > 0)
var/ratio = safe_breath_min/breath_pp
H.adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!
H.failed_last_breath = 1
. = true_pp*ratio/6
else
H.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
H.failed_last_breath = 1
/datum/species/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/H) // called by human/life, handles temperatures
if(abs(310.15 - breath.temperature) > 50)
if(!(mutations_list[COLDRES] in H.dna.mutations)) // COLD DAMAGE
switch(breath.temperature)
if(-INFINITY to 120)
H.apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, "head")
if(120 to 200)
H.apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, "head")
if(200 to 260)
H.apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, "head")
if(!(RESISTTEMP in specflags)) // HEAT DAMAGE
switch(breath.temperature)
if(360 to 400)
H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head")
if(400 to 1000)
H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head")
if(1000 to INFINITY)
H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head")
/datum/species/proc/handle_environment(datum/gas_mixture/environment, mob/living/carbon/human/H)
if(!environment)
return
if(istype(H.loc, /obj/machinery/atmospherics/components/unary/cryo_cell))
return
var/loc_temp = H.get_temperature(environment)
//Body temperature is adjusted in two steps. First, your body tries to stabilize itself a bit.
if(H.stat != DEAD)
H.natural_bodytemperature_stabilization()
//Then, it reacts to the surrounding atmosphere based on your thermal protection
if(!H.on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases
if(loc_temp < H.bodytemperature)
//Place is colder than we are
var/thermal_protection = H.get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(thermal_protection < 1)
H.bodytemperature += min((1-thermal_protection) * ((loc_temp - H.bodytemperature) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX)
else
//Place is hotter than we are
var/thermal_protection = H.get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(thermal_protection < 1)
H.bodytemperature += min((1-thermal_protection) * ((loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR), BODYTEMP_HEATING_MAX)
// +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt.
if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !(RESISTTEMP in specflags))
//Body temperature is too hot.
switch(H.bodytemperature)
if(360 to 400)
H.throw_alert("temp", /obj/screen/alert/hot, 1)
H.apply_damage(HEAT_DAMAGE_LEVEL_1*heatmod, BURN)
if(400 to 460)
H.throw_alert("temp", /obj/screen/alert/hot, 2)
H.apply_damage(HEAT_DAMAGE_LEVEL_2*heatmod, BURN)
if(460 to INFINITY)
H.throw_alert("temp", /obj/screen/alert/hot, 3)
if(H.on_fire)
H.apply_damage(HEAT_DAMAGE_LEVEL_3*heatmod, BURN)
else
H.apply_damage(HEAT_DAMAGE_LEVEL_2*heatmod, BURN)
else if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !(mutations_list[COLDRES] in H.dna.mutations))
switch(H.bodytemperature)
if(200 to 260)
H.throw_alert("temp", /obj/screen/alert/cold, 1)
H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod, BURN)
if(120 to 200)
H.throw_alert("temp", /obj/screen/alert/cold, 2)
H.apply_damage(COLD_DAMAGE_LEVEL_2*coldmod, BURN)
if(-INFINITY to 120)
H.throw_alert("temp", /obj/screen/alert/cold, 3)
H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod, BURN)
else
H.clear_alert("temp")
else
H.clear_alert("temp")
// Account for massive pressure differences. Done by Polymorph
// Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense!
var/pressure = environment.return_pressure()
var/adjusted_pressure = H.calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob.
switch(adjusted_pressure)
if(HAZARD_HIGH_PRESSURE to INFINITY)
if(!(RESISTTEMP in specflags))
H.adjustBruteLoss( min( ( (adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) )
H.throw_alert("pressure", /obj/screen/alert/highpressure, 2)
else
H.clear_alert("pressure")
if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE)
H.throw_alert("pressure", /obj/screen/alert/highpressure, 1)
if(WARNING_LOW_PRESSURE to WARNING_HIGH_PRESSURE)
H.clear_alert("pressure")
if(HAZARD_LOW_PRESSURE to WARNING_LOW_PRESSURE)
H.throw_alert("pressure", /obj/screen/alert/lowpressure, 1)
else
if(H.dna.check_mutation(COLDRES) || (RESISTTEMP in specflags))
H.clear_alert("pressure")
else
H.adjustBruteLoss( LOW_PRESSURE_DAMAGE )
H.throw_alert("pressure", /obj/screen/alert/lowpressure, 2)
//////////
// FIRE //
//////////
/datum/species/proc/handle_fire(mob/living/carbon/human/H)
if((RESISTTEMP in specflags) || (NOFIRE in specflags))
return 1
/datum/species/proc/CanIgniteMob(mob/living/carbon/human/H)
if((RESISTTEMP in specflags) || (NOFIRE in specflags))
return FALSE
return TRUE
/datum/species/proc/ExtinguishMob(mob/living/carbon/human/H)
return
////////
//Stun//
////////
/datum/species/proc/spec_stun(mob/living/carbon/human/H,amount)
. = stunmod * amount
//////////////
//Space Move//
//////////////
/datum/species/proc/space_move()
return 0
/datum/species/proc/negates_gravity()
return 0
#undef HUMAN_MAX_OXYLOSS
#undef HUMAN_CRIT_MAX_OXYLOSS
#undef HEAT_DAMAGE_LEVEL_1
#undef HEAT_DAMAGE_LEVEL_2
#undef HEAT_DAMAGE_LEVEL_3
#undef COLD_DAMAGE_LEVEL_1
#undef COLD_DAMAGE_LEVEL_2
#undef COLD_DAMAGE_LEVEL_3
#undef HEAT_GAS_DAMAGE_LEVEL_1
#undef HEAT_GAS_DAMAGE_LEVEL_2
#undef HEAT_GAS_DAMAGE_LEVEL_3
#undef COLD_GAS_DAMAGE_LEVEL_1
#undef COLD_GAS_DAMAGE_LEVEL_2
#undef COLD_GAS_DAMAGE_LEVEL_3