mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
fix: having higher sanity is no longer punished by making you enter crit faster balance: you can have 100 mood instead of 99 before it starts slowly decreasing Remember higher sanity is BETTER You want crit_modifier (now crit_threshold) to be LOW so you can stay in fights more (you go into crit at 5hp vs 0hp). Why: Proc overhead, puts the load on mood, proper abstraction of mood component, values > defines, etc.
1677 lines
61 KiB
Plaintext
1677 lines
61 KiB
Plaintext
// This code handles different species in the game.
|
|
|
|
GLOBAL_LIST_EMPTY(roundstart_races)
|
|
|
|
/datum/species
|
|
var/id // 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 //this is used if you want to use a different species limb sprites. Mainly used for angels as they look like humans.
|
|
var/name // 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/default_color = "#FFF" // if alien colors are disabled, this is the color that will be used by that race
|
|
|
|
var/sexes = 1 // whether or not the race has sexual characteristics. at the moment this is only 0 for skeletons and shadows
|
|
|
|
var/list/offset_features = list(OFFSET_UNIFORM = list(0,0), OFFSET_ID = list(0,0), OFFSET_GLOVES = list(0,0), OFFSET_GLASSES = list(0,0), OFFSET_EARS = list(0,0), OFFSET_SHOES = list(0,0), OFFSET_S_STORE = list(0,0), OFFSET_FACEMASK = list(0,0), OFFSET_HEAD = list(0,0), OFFSET_FACE = list(0,0), OFFSET_BELT = list(0,0), OFFSET_BACK = list(0,0), OFFSET_SUIT = list(0,0), OFFSET_NECK = list(0,0))
|
|
|
|
var/hair_color // 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/reagent_containers/food/snacks/meat/slab/human //What the species drops on gibbing
|
|
var/skinned_type
|
|
var/liked_food = NONE
|
|
var/disliked_food = GROSS
|
|
var/toxic_food = TOXIC
|
|
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 //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() // Visible CURRENT bodyparts that are unique to a species. DO NOT USE THIS AS A LIST OF ALL POSSIBLE BODYPARTS AS IT WILL FUCK SHIT UP! Changes to this list for non-species specific bodyparts (ie cat ears and tails) should be assigned at organ level if possible. Layer hiding is handled by handle_mutant_bodyparts() below.
|
|
var/list/mutant_organs = list() //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/acidmod = 1 // multiplier for acid damage // yogs - Old Plant People
|
|
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/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded?
|
|
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
|
|
|
|
// species-only traits. Can be found in DNA.dm
|
|
var/list/species_traits = list()
|
|
// generic traits tied to having the species
|
|
var/list/inherent_traits = list()
|
|
var/list/inherent_biotypes = list(MOB_ORGANIC, MOB_HUMANOID)
|
|
|
|
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/obj/item/organ/lungs/mutantlungs = null
|
|
var/breathid = "o2"
|
|
|
|
var/obj/item/organ/brain/mutant_brain = /obj/item/organ/brain
|
|
var/obj/item/organ/heart/mutant_heart = /obj/item/organ/heart
|
|
var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes
|
|
var/obj/item/organ/ears/mutantears = /obj/item/organ/ears
|
|
var/obj/item/mutanthands
|
|
var/obj/item/organ/tongue/mutanttongue = /obj/item/organ/tongue
|
|
var/obj/item/organ/tail/mutanttail = null
|
|
|
|
var/obj/item/organ/liver/mutantliver
|
|
var/obj/item/organ/stomach/mutantstomach
|
|
var/override_float = FALSE
|
|
|
|
///////////
|
|
// PROCS //
|
|
///////////
|
|
|
|
|
|
/datum/species/New()
|
|
|
|
if(!limbs_id) //if we havent set a limbs id to use, just use our own id
|
|
limbs_id = id
|
|
..()
|
|
|
|
|
|
/proc/generate_selectable_species()
|
|
for(var/I in subtypesof(/datum/species))
|
|
var/datum/species/S = new I
|
|
if(S.check_roundstart_eligible())
|
|
GLOB.roundstart_races += S.id
|
|
qdel(S)
|
|
if(!GLOB.roundstart_races.len)
|
|
GLOB.roundstart_races += "human"
|
|
|
|
/datum/species/proc/check_roundstart_eligible()
|
|
if(id in (CONFIG_GET(keyed_flag_list/roundstart_races)))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/species/proc/random_name(gender,unique,lastname)
|
|
if(unique)
|
|
return random_unique_name(gender)
|
|
|
|
var/randname
|
|
if(gender == MALE)
|
|
randname = pick(GLOB.first_names_male)
|
|
else
|
|
randname = pick(GLOB.first_names_female)
|
|
|
|
if(lastname)
|
|
randname += " [lastname]"
|
|
else
|
|
randname += " [pick(GLOB.last_names)]"
|
|
|
|
return randname
|
|
|
|
//Called when cloning, copies some vars that should be kept
|
|
/datum/species/proc/copy_properties_from(datum/species/old_species)
|
|
return
|
|
|
|
//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 GLOB.command_positions)
|
|
return 0
|
|
return 1
|
|
|
|
//Will regenerate missing organs
|
|
/datum/species/proc/regenerate_organs(mob/living/carbon/C,datum/species/old_species,replace_current=TRUE)
|
|
var/obj/item/organ/brain/brain = C.getorganslot(ORGAN_SLOT_BRAIN)
|
|
var/obj/item/organ/heart/heart = C.getorganslot(ORGAN_SLOT_HEART)
|
|
var/obj/item/organ/lungs/lungs = C.getorganslot(ORGAN_SLOT_LUNGS)
|
|
var/obj/item/organ/appendix/appendix = C.getorganslot(ORGAN_SLOT_APPENDIX)
|
|
var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
|
|
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
|
|
var/obj/item/organ/tongue/tongue = C.getorganslot(ORGAN_SLOT_TONGUE)
|
|
var/obj/item/organ/liver/liver = C.getorganslot(ORGAN_SLOT_LIVER)
|
|
var/obj/item/organ/stomach/stomach = C.getorganslot(ORGAN_SLOT_STOMACH)
|
|
var/obj/item/organ/tail/tail = C.getorganslot(ORGAN_SLOT_TAIL)
|
|
|
|
var/should_have_brain = TRUE
|
|
var/should_have_heart = !(NOBLOOD in species_traits)
|
|
var/should_have_lungs = !(TRAIT_NOBREATH in inherent_traits)
|
|
var/should_have_appendix = !(TRAIT_NOHUNGER in inherent_traits)
|
|
var/should_have_eyes = TRUE
|
|
var/should_have_ears = TRUE
|
|
var/should_have_tongue = TRUE
|
|
var/should_have_liver = !(NOLIVER in species_traits)
|
|
var/should_have_stomach = !(NOSTOMACH in species_traits)
|
|
var/should_have_tail = mutanttail
|
|
|
|
if(brain && (replace_current || !should_have_brain))
|
|
if(!brain.decoy_override)//Just keep it if it's fake
|
|
brain.Remove(C,TRUE,TRUE)
|
|
QDEL_NULL(brain)
|
|
if(should_have_brain && !brain)
|
|
brain = new mutant_brain()
|
|
brain.Insert(C, TRUE, TRUE)
|
|
|
|
if(heart && (!should_have_heart || replace_current))
|
|
heart.Remove(C,1)
|
|
QDEL_NULL(heart)
|
|
if(should_have_heart && !heart)
|
|
heart = new mutant_heart()
|
|
heart.Insert(C)
|
|
|
|
if(lungs && (!should_have_lungs || replace_current))
|
|
lungs.Remove(C,1)
|
|
QDEL_NULL(lungs)
|
|
if(should_have_lungs && !lungs)
|
|
if(mutantlungs)
|
|
lungs = new mutantlungs()
|
|
else
|
|
lungs = new()
|
|
lungs.Insert(C)
|
|
|
|
if(liver && (!should_have_liver || replace_current))
|
|
liver.Remove(C,1)
|
|
QDEL_NULL(liver)
|
|
if(should_have_liver && !liver)
|
|
if(mutantliver)
|
|
liver = new mutantliver()
|
|
else
|
|
liver = new()
|
|
liver.Insert(C)
|
|
|
|
if(stomach && (!should_have_stomach || replace_current))
|
|
stomach.Remove(C,1)
|
|
QDEL_NULL(stomach)
|
|
if(should_have_stomach && !stomach)
|
|
if(mutantstomach)
|
|
stomach = new mutantstomach()
|
|
else
|
|
stomach = new()
|
|
stomach.Insert(C)
|
|
|
|
if(appendix && (!should_have_appendix || replace_current))
|
|
appendix.Remove(C,1)
|
|
QDEL_NULL(appendix)
|
|
if(should_have_appendix && !appendix)
|
|
appendix = new()
|
|
appendix.Insert(C)
|
|
|
|
if(tail && (!should_have_tail || replace_current))
|
|
tail.Remove(C,1)
|
|
QDEL_NULL(tail)
|
|
if(should_have_tail && !tail)
|
|
tail = new mutanttail()
|
|
tail.Insert(C)
|
|
|
|
if(C.get_bodypart(BODY_ZONE_HEAD))
|
|
if(eyes && (replace_current || !should_have_eyes))
|
|
eyes.Remove(C,1)
|
|
QDEL_NULL(eyes)
|
|
if(should_have_eyes && !eyes)
|
|
eyes = new mutanteyes
|
|
eyes.Insert(C)
|
|
|
|
if(ears && (replace_current || !should_have_ears))
|
|
ears.Remove(C,1)
|
|
QDEL_NULL(ears)
|
|
if(should_have_ears && !ears)
|
|
ears = new mutantears
|
|
ears.Insert(C)
|
|
|
|
if(tongue && (replace_current || !should_have_tongue))
|
|
tongue.Remove(C,1)
|
|
QDEL_NULL(tongue)
|
|
if(should_have_tongue && !tongue)
|
|
tongue = new mutanttongue
|
|
tongue.Insert(C)
|
|
|
|
if(old_species)
|
|
for(var/mutantorgan in old_species.mutant_organs)
|
|
var/obj/item/organ/I = C.getorgan(mutantorgan)
|
|
if(I)
|
|
I.Remove(C)
|
|
QDEL_NULL(I)
|
|
|
|
for(var/path in mutant_organs)
|
|
var/obj/item/organ/I = new path()
|
|
I.Insert(C)
|
|
|
|
/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 && (!thing.species_exception || !is_type_in_list(src,thing.species_exception)))
|
|
C.dropItemToGround(thing)
|
|
if(C.hud_used)
|
|
C.hud_used.update_locked_slots()
|
|
|
|
// this needs to be FIRST because qdel calls update_body which checks if we have DIGITIGRADE legs or not and if not then removes DIGITIGRADE from species_traits
|
|
if(("legs" in C.dna.species.mutant_bodyparts) && C.dna.features["legs"] == "Digitigrade Legs")
|
|
species_traits += DIGITIGRADE
|
|
if(DIGITIGRADE in species_traits)
|
|
C.Digitigrade_Leg_Swap(FALSE)
|
|
|
|
C.mob_biotypes = inherent_biotypes
|
|
|
|
regenerate_organs(C,old_species)
|
|
|
|
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
|
|
C.dna.blood_type = exotic_bloodtype
|
|
|
|
if(old_species.mutanthands)
|
|
for(var/obj/item/I in C.held_items)
|
|
if(istype(I, old_species.mutanthands))
|
|
qdel(I)
|
|
|
|
if(mutanthands)
|
|
// Drop items in hands
|
|
// If you're lucky enough to have a NODROP_1 item, then it stays.
|
|
for(var/V in C.held_items)
|
|
var/obj/item/I = V
|
|
if(istype(I))
|
|
C.dropItemToGround(I)
|
|
else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand
|
|
C.put_in_hands(new mutanthands())
|
|
|
|
for(var/X in inherent_traits)
|
|
C.add_trait(X, SPECIES_TRAIT)
|
|
|
|
if(TRAIT_VIRUSIMMUNE in inherent_traits)
|
|
for(var/datum/disease/A in C.diseases)
|
|
A.cure(FALSE)
|
|
|
|
|
|
/datum/species/proc/on_species_loss(mob/living/carbon/C)
|
|
if(C.dna.species.exotic_bloodtype)
|
|
C.dna.blood_type = random_blood_type()
|
|
if(DIGITIGRADE in species_traits)
|
|
C.Digitigrade_Leg_Swap(TRUE)
|
|
for(var/X in inherent_traits)
|
|
C.remove_trait(X, SPECIES_TRAIT)
|
|
|
|
/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(BODY_ZONE_HEAD)
|
|
if(!HD) //Decapitated
|
|
return
|
|
|
|
if(H.has_trait(TRAIT_HUSK))
|
|
return
|
|
var/datum/sprite_accessory/S
|
|
var/list/standing = list()
|
|
|
|
var/hair_hidden = FALSE //ignored if the matching dynamic_X_suffix is non-empty
|
|
var/facialhair_hidden = FALSE // ^
|
|
|
|
var/dynamic_hair_suffix = "" //if this is non-null, and hair+suffix matches an iconstate, then we render that hair instead
|
|
var/dynamic_fhair_suffix = ""
|
|
|
|
//for augmented heads
|
|
if(HD.status == BODYPART_ROBOTIC)
|
|
return
|
|
|
|
//we check if our hat or helmet hides our facial hair.
|
|
if(H.head)
|
|
var/obj/item/I = H.head
|
|
if(istype(I, /obj/item/clothing))
|
|
var/obj/item/clothing/C = I
|
|
dynamic_fhair_suffix = C.dynamic_fhair_suffix
|
|
if(I.flags_inv & HIDEFACIALHAIR)
|
|
facialhair_hidden = TRUE
|
|
|
|
if(H.wear_mask)
|
|
var/obj/item/clothing/mask/M = H.wear_mask
|
|
dynamic_fhair_suffix = M.dynamic_fhair_suffix //mask > head in terms of facial hair
|
|
if(M.flags_inv & HIDEFACIALHAIR)
|
|
facialhair_hidden = TRUE
|
|
|
|
if(H.facial_hair_style && (FACEHAIR in species_traits) && (!facialhair_hidden || dynamic_fhair_suffix))
|
|
S = GLOB.facial_hair_styles_list[H.facial_hair_style]
|
|
if(S)
|
|
|
|
//List of all valid dynamic_fhair_suffixes
|
|
var/static/list/fextensions
|
|
if(!fextensions)
|
|
var/icon/fhair_extensions = icon('icons/mob/facialhair_extensions.dmi')
|
|
fextensions = list()
|
|
for(var/s in fhair_extensions.IconStates(1))
|
|
fextensions[s] = TRUE
|
|
qdel(fhair_extensions)
|
|
|
|
//Is hair+dynamic_fhair_suffix a valid iconstate?
|
|
var/fhair_state = S.icon_state
|
|
var/fhair_file = S.icon
|
|
if(fextensions[fhair_state+dynamic_fhair_suffix])
|
|
fhair_state += dynamic_fhair_suffix
|
|
fhair_file = 'icons/mob/facialhair_extensions.dmi'
|
|
|
|
var/mutable_appearance/facial_overlay = mutable_appearance(fhair_file, fhair_state, -HAIR_LAYER)
|
|
|
|
if(!forced_colour)
|
|
if(hair_color)
|
|
if(hair_color == "mutcolor")
|
|
facial_overlay.color = "#" + H.dna.features["mcolor"]
|
|
else
|
|
facial_overlay.color = "#" + hair_color
|
|
else
|
|
facial_overlay.color = "#" + H.facial_hair_color
|
|
else
|
|
facial_overlay.color = forced_colour
|
|
|
|
facial_overlay.alpha = hair_alpha
|
|
|
|
standing += facial_overlay
|
|
|
|
if(H.head)
|
|
var/obj/item/I = H.head
|
|
if(istype(I, /obj/item/clothing))
|
|
var/obj/item/clothing/C = I
|
|
dynamic_hair_suffix = C.dynamic_hair_suffix
|
|
if(I.flags_inv & HIDEHAIR)
|
|
hair_hidden = TRUE
|
|
|
|
if(H.wear_mask)
|
|
var/obj/item/clothing/mask/M = H.wear_mask
|
|
if(!dynamic_hair_suffix) //head > mask in terms of head hair
|
|
dynamic_hair_suffix = M.dynamic_hair_suffix
|
|
if(M.flags_inv & HIDEHAIR)
|
|
hair_hidden = TRUE
|
|
|
|
if(!hair_hidden || dynamic_hair_suffix)
|
|
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
|
|
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
|
|
if(!(NOBLOOD in species_traits))
|
|
hair_overlay.icon = 'icons/mob/human_face.dmi'
|
|
hair_overlay.icon_state = "debrained"
|
|
|
|
else if(H.hair_style && (HAIR in species_traits))
|
|
S = GLOB.hair_styles_list[H.hair_style]
|
|
if(S)
|
|
|
|
//List of all valid dynamic_hair_suffixes
|
|
var/static/list/extensions
|
|
if(!extensions)
|
|
var/icon/hair_extensions = icon('icons/mob/hair_extensions.dmi') //hehe
|
|
extensions = list()
|
|
for(var/s in hair_extensions.IconStates(1))
|
|
extensions[s] = TRUE
|
|
qdel(hair_extensions)
|
|
|
|
//Is hair+dynamic_hair_suffix a valid iconstate?
|
|
var/hair_state = S.icon_state
|
|
var/hair_file = S.icon
|
|
if(extensions[hair_state+dynamic_hair_suffix])
|
|
hair_state += dynamic_hair_suffix
|
|
hair_file = 'icons/mob/hair_extensions.dmi'
|
|
|
|
hair_overlay.icon = hair_file
|
|
hair_overlay.icon_state = hair_state
|
|
|
|
if(!forced_colour)
|
|
if(hair_color)
|
|
if(hair_color == "mutcolor")
|
|
hair_overlay.color = "#" + H.dna.features["mcolor"]
|
|
else
|
|
hair_overlay.color = "#" + hair_color
|
|
else
|
|
hair_overlay.color = "#" + H.hair_color
|
|
else
|
|
hair_overlay.color = forced_colour
|
|
hair_overlay.alpha = hair_alpha
|
|
if(OFFSET_FACE in H.dna.species.offset_features)
|
|
hair_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FACE][1]
|
|
hair_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
|
|
if(hair_overlay.icon)
|
|
standing += hair_overlay
|
|
|
|
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()
|
|
|
|
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
|
|
|
|
if(HD && !(H.has_trait(TRAIT_HUSK)))
|
|
// lipstick
|
|
if(H.lip_style && (LIPS in species_traits))
|
|
var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/human_face.dmi', "lips_[H.lip_style]", -BODY_LAYER)
|
|
lip_overlay.color = H.lip_color
|
|
if(OFFSET_FACE in H.dna.species.offset_features)
|
|
lip_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FACE][1]
|
|
lip_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
|
|
standing += lip_overlay
|
|
|
|
// eyes
|
|
if(!(NOEYES in species_traits))
|
|
var/has_eyes = H.getorganslot(ORGAN_SLOT_EYES)
|
|
var/mutable_appearance/eye_overlay
|
|
if(!has_eyes)
|
|
eye_overlay = mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER)
|
|
else
|
|
eye_overlay = mutable_appearance('icons/mob/human_face.dmi', "eyes", -BODY_LAYER)
|
|
if((EYECOLOR in species_traits) && has_eyes)
|
|
eye_overlay.color = "#" + H.eye_color
|
|
if(OFFSET_FACE in H.dna.species.offset_features)
|
|
eye_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FACE][1]
|
|
eye_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
|
|
standing += eye_overlay
|
|
|
|
//Underwear, Undershirts & Socks
|
|
if(!(NO_UNDERWEAR in species_traits))
|
|
if(H.underwear)
|
|
var/datum/sprite_accessory/underwear/underwear = GLOB.underwear_list[H.underwear]
|
|
if(underwear)
|
|
standing += mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
|
|
|
|
if(H.undershirt)
|
|
var/datum/sprite_accessory/undershirt/undershirt = GLOB.undershirt_list[H.undershirt]
|
|
if(undershirt)
|
|
if(H.dna.species.sexes && H.gender == FEMALE)
|
|
standing += wear_female_version(undershirt.icon_state, undershirt.icon, BODY_LAYER)
|
|
else
|
|
standing += mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
|
|
|
|
if(H.socks && H.get_num_legs(FALSE) >= 2 && !(DIGITIGRADE in species_traits))
|
|
var/datum/sprite_accessory/socks/socks = GLOB.socks_list[H.socks]
|
|
if(socks)
|
|
standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
|
|
|
|
if(standing.len)
|
|
H.overlays_standing[BODY_LAYER] = standing
|
|
|
|
H.apply_overlay(BODY_LAYER)
|
|
handle_mutant_bodyparts(H)
|
|
|
|
/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(BODY_ZONE_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 == BODYPART_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 == BODYPART_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 == BODYPART_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 == BODYPART_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) && (!H.wear_suit.species_exception || !is_type_in_list(src, H.wear_suit.species_exception))))
|
|
bodyparts_to_add -= "wings"
|
|
|
|
if("wings_open" in mutant_bodyparts)
|
|
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT) && (!H.wear_suit.species_exception || !is_type_in_list(src, H.wear_suit.species_exception)))
|
|
bodyparts_to_add -= "wings_open"
|
|
else if ("wings" in mutant_bodyparts)
|
|
bodyparts_to_add -= "wings_open"
|
|
|
|
//Digitigrade legs are stuck in the phantom zone between true limbs and mutant bodyparts. Mainly it just needs more agressive updating than most limbs.
|
|
var/update_needed = FALSE
|
|
var/not_digitigrade = TRUE
|
|
for(var/X in H.bodyparts)
|
|
var/obj/item/bodypart/O = X
|
|
if(!O.use_digitigrade)
|
|
continue
|
|
not_digitigrade = FALSE
|
|
if(!(DIGITIGRADE in species_traits)) //Someone cut off a digitigrade leg and tacked it on
|
|
species_traits += DIGITIGRADE
|
|
var/should_be_squished = FALSE
|
|
if(H.wear_suit && ((H.wear_suit.flags_inv & HIDEJUMPSUIT) || (H.wear_suit.body_parts_covered & LEGS)) || (H.w_uniform && (H.w_uniform.body_parts_covered & LEGS)))
|
|
should_be_squished = TRUE
|
|
if(O.use_digitigrade == FULL_DIGITIGRADE && should_be_squished)
|
|
O.use_digitigrade = SQUISHED_DIGITIGRADE
|
|
update_needed = TRUE
|
|
else if(O.use_digitigrade == SQUISHED_DIGITIGRADE && !should_be_squished)
|
|
O.use_digitigrade = FULL_DIGITIGRADE
|
|
update_needed = TRUE
|
|
if(update_needed)
|
|
H.update_body_parts()
|
|
if(not_digitigrade && (DIGITIGRADE in species_traits)) //Curse is lifted
|
|
species_traits -= DIGITIGRADE
|
|
|
|
if(!bodyparts_to_add)
|
|
return
|
|
|
|
var/g = (H.gender == FEMALE) ? "f" : "m"
|
|
|
|
for(var/layer in relevent_layers)
|
|
var/layertext = mutant_bodyparts_layertext(layer)
|
|
|
|
for(var/bodypart in bodyparts_to_add)
|
|
var/datum/sprite_accessory/S
|
|
switch(bodypart)
|
|
if("tail_lizard")
|
|
S = GLOB.tails_list_lizard[H.dna.features["tail_lizard"]]
|
|
if("waggingtail_lizard")
|
|
S.= GLOB.animated_tails_list_lizard[H.dna.features["tail_lizard"]]
|
|
if("tail_human")
|
|
S = GLOB.tails_list_human[H.dna.features["tail_human"]]
|
|
if("waggingtail_human")
|
|
S.= GLOB.animated_tails_list_human[H.dna.features["tail_human"]]
|
|
if("spines")
|
|
S = GLOB.spines_list[H.dna.features["spines"]]
|
|
if("waggingspines")
|
|
S.= GLOB.animated_spines_list[H.dna.features["spines"]]
|
|
if("snout")
|
|
S = GLOB.snouts_list[H.dna.features["snout"]]
|
|
if("frills")
|
|
S = GLOB.frills_list[H.dna.features["frills"]]
|
|
if("horns")
|
|
S = GLOB.horns_list[H.dna.features["horns"]]
|
|
if("ears")
|
|
S = GLOB.ears_list[H.dna.features["ears"]]
|
|
if("body_markings")
|
|
S = GLOB.body_markings_list[H.dna.features["body_markings"]]
|
|
if("wings")
|
|
S = GLOB.wings_list[H.dna.features["wings"]]
|
|
if("wingsopen")
|
|
S = GLOB.wings_open_list[H.dna.features["wings"]]
|
|
if("legs")
|
|
S = GLOB.legs_list[H.dna.features["legs"]]
|
|
if("moth_wings")
|
|
S = GLOB.moth_wings_list[H.dna.features["moth_wings"]]
|
|
if("caps")
|
|
S = GLOB.caps_list[H.dna.features["caps"]]
|
|
if(!S || S.icon_state == "none")
|
|
continue
|
|
|
|
var/mutable_appearance/accessory_overlay = mutable_appearance(S.icon, layer = -layer)
|
|
|
|
//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"
|
|
|
|
if(S.gender_specific)
|
|
accessory_overlay.icon_state = "[g]_[bodypart]_[S.icon_state]_[layertext]"
|
|
else
|
|
accessory_overlay.icon_state = "m_[bodypart]_[S.icon_state]_[layertext]"
|
|
|
|
if(S.center)
|
|
accessory_overlay = center_image(accessory_overlay, S.dimension_x, S.dimension_y)
|
|
|
|
if(!(H.has_trait(TRAIT_HUSK)))
|
|
if(!forced_colour)
|
|
switch(S.color_src)
|
|
if(MUTCOLORS)
|
|
if(fixed_mut_color)
|
|
accessory_overlay.color = "#[fixed_mut_color]"
|
|
else
|
|
accessory_overlay.color = "#[H.dna.features["mcolor"]]"
|
|
if(HAIR)
|
|
if(hair_color == "mutcolor")
|
|
accessory_overlay.color = "#[H.dna.features["mcolor"]]"
|
|
else
|
|
accessory_overlay.color = "#[H.hair_color]"
|
|
if(FACEHAIR)
|
|
accessory_overlay.color = "#[H.facial_hair_color]"
|
|
if(EYECOLOR)
|
|
accessory_overlay.color = "#[H.eye_color]"
|
|
else
|
|
accessory_overlay.color = forced_colour
|
|
standing += accessory_overlay
|
|
|
|
if(S.hasinner)
|
|
var/mutable_appearance/inner_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
|
|
if(S.gender_specific)
|
|
inner_accessory_overlay.icon_state = "[g]_[bodypart]inner_[S.icon_state]_[layertext]"
|
|
else
|
|
inner_accessory_overlay.icon_state = "m_[bodypart]inner_[S.icon_state]_[layertext]"
|
|
|
|
if(S.center)
|
|
inner_accessory_overlay = center_image(inner_accessory_overlay, S.dimension_x, S.dimension_y)
|
|
|
|
standing += inner_accessory_overlay
|
|
|
|
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)
|
|
|
|
|
|
//This exists so sprite accessories can still be per-layer without having to include that layer's
|
|
//number in their sprite name, which causes issues when those numbers change.
|
|
/datum/species/proc/mutant_bodyparts_layertext(layer)
|
|
switch(layer)
|
|
if(BODY_BEHIND_LAYER)
|
|
return "BEHIND"
|
|
if(BODY_ADJ_LAYER)
|
|
return "ADJ"
|
|
if(BODY_FRONT_LAYER)
|
|
return "FRONT"
|
|
|
|
|
|
/datum/species/proc/spec_life(mob/living/carbon/human/H)
|
|
if(H.has_trait(TRAIT_NOBREATH))
|
|
H.setOxyLoss(0)
|
|
H.losebreath = 0
|
|
|
|
var/takes_crit_damage = (!H.has_trait(TRAIT_NOCRITDAMAGE))
|
|
if((H.health < H.crit_threshold) && 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, bypass_equip_delay_self = FALSE)
|
|
if(slot in no_equip)
|
|
if(!I.species_exception || !is_type_in_list(src, I.species_exception))
|
|
return FALSE
|
|
|
|
var/num_arms = H.get_num_arms(FALSE)
|
|
var/num_legs = H.get_num_legs(FALSE)
|
|
|
|
switch(slot)
|
|
if(SLOT_HANDS)
|
|
if(H.get_empty_held_indexes())
|
|
return TRUE
|
|
return FALSE
|
|
if(SLOT_WEAR_MASK)
|
|
if(H.wear_mask)
|
|
return FALSE
|
|
if(!(I.slot_flags & ITEM_SLOT_MASK))
|
|
return FALSE
|
|
if(!H.get_bodypart(BODY_ZONE_HEAD))
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_NECK)
|
|
if(H.wear_neck)
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_NECK) )
|
|
return FALSE
|
|
return TRUE
|
|
if(SLOT_BACK)
|
|
if(H.back)
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_BACK) )
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_WEAR_SUIT)
|
|
if(H.wear_suit)
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_OCLOTHING) )
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_GLOVES)
|
|
if(H.gloves)
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_GLOVES) )
|
|
return FALSE
|
|
if(num_arms < 2)
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_SHOES)
|
|
if(H.shoes)
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_FEET) )
|
|
return FALSE
|
|
if(num_legs < 2)
|
|
return FALSE
|
|
if(DIGITIGRADE in species_traits)
|
|
if(!disable_warning)
|
|
to_chat(H, "<span class='warning'>The footwear around here isn't compatible with your feet!</span>")
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_BELT)
|
|
if(H.belt)
|
|
return FALSE
|
|
|
|
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
|
|
|
|
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
|
|
if(!disable_warning)
|
|
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
|
|
return FALSE
|
|
if(!(I.slot_flags & ITEM_SLOT_BELT))
|
|
return
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_GLASSES)
|
|
if(H.glasses)
|
|
return FALSE
|
|
if(!(I.slot_flags & ITEM_SLOT_EYES))
|
|
return FALSE
|
|
if(!H.get_bodypart(BODY_ZONE_HEAD))
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_HEAD)
|
|
if(H.head)
|
|
return FALSE
|
|
if(!(I.slot_flags & ITEM_SLOT_HEAD))
|
|
return FALSE
|
|
if(!H.get_bodypart(BODY_ZONE_HEAD))
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_EARS)
|
|
if(H.ears)
|
|
return FALSE
|
|
if(!(I.slot_flags & ITEM_SLOT_EARS))
|
|
return FALSE
|
|
if(!H.get_bodypart(BODY_ZONE_HEAD))
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_W_UNIFORM)
|
|
if(H.w_uniform)
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_ICLOTHING) )
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_WEAR_ID)
|
|
if(H.wear_id)
|
|
return FALSE
|
|
|
|
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
|
|
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
|
|
if(!disable_warning)
|
|
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
|
|
return FALSE
|
|
if( !(I.slot_flags & ITEM_SLOT_ID) )
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_L_STORE)
|
|
if(I.item_flags & NODROP) //Pockets aren't visible, so you can't move NODROP_1 items into them.
|
|
return FALSE
|
|
if(H.l_store)
|
|
return FALSE
|
|
|
|
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_L_LEG)
|
|
|
|
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
|
|
if(!disable_warning)
|
|
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
|
|
return FALSE
|
|
if(I.slot_flags & ITEM_SLOT_DENYPOCKET)
|
|
return FALSE
|
|
if( I.w_class <= WEIGHT_CLASS_SMALL || (I.slot_flags & ITEM_SLOT_POCKET) )
|
|
return TRUE
|
|
if(SLOT_R_STORE)
|
|
if(I.item_flags & NODROP)
|
|
return FALSE
|
|
if(H.r_store)
|
|
return FALSE
|
|
|
|
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_R_LEG)
|
|
|
|
if(!H.w_uniform && !nojumpsuit && (!O || O.status != BODYPART_ROBOTIC))
|
|
if(!disable_warning)
|
|
to_chat(H, "<span class='warning'>You need a jumpsuit before you can attach this [I.name]!</span>")
|
|
return FALSE
|
|
if(I.slot_flags & ITEM_SLOT_DENYPOCKET)
|
|
return FALSE
|
|
if( I.w_class <= WEIGHT_CLASS_SMALL || (I.slot_flags & ITEM_SLOT_POCKET) )
|
|
return TRUE
|
|
return FALSE
|
|
if(SLOT_S_STORE)
|
|
if(I.item_flags & NODROP)
|
|
return FALSE
|
|
if(H.s_store)
|
|
return FALSE
|
|
if(!H.wear_suit)
|
|
if(!disable_warning)
|
|
to_chat(H, "<span class='warning'>You need a suit before you can attach this [I.name]!</span>")
|
|
return FALSE
|
|
if(!H.wear_suit.allowed)
|
|
if(!disable_warning)
|
|
to_chat(H, "You somehow have a suit with no defined allowed items for suit storage, stop that.")
|
|
return FALSE
|
|
if(I.w_class > WEIGHT_CLASS_BULKY)
|
|
if(!disable_warning)
|
|
to_chat(H, "The [I.name] is too big to attach.") //should be src?
|
|
return FALSE
|
|
if( istype(I, /obj/item/pda) || istype(I, /obj/item/pen) || is_type_in_list(I, H.wear_suit.allowed) )
|
|
return TRUE
|
|
return FALSE
|
|
if(SLOT_HANDCUFFED)
|
|
if(H.handcuffed)
|
|
return FALSE
|
|
if(!istype(I, /obj/item/restraints/handcuffs))
|
|
return FALSE
|
|
if(num_arms < 2)
|
|
return FALSE
|
|
return TRUE
|
|
if(SLOT_LEGCUFFED)
|
|
if(H.legcuffed)
|
|
return FALSE
|
|
if(!istype(I, /obj/item/restraints/legcuffs))
|
|
return FALSE
|
|
if(num_legs < 2)
|
|
return FALSE
|
|
return TRUE
|
|
if(SLOT_IN_BACKPACK)
|
|
if(H.back)
|
|
if(SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_CAN_INSERT, I, H, TRUE))
|
|
return TRUE
|
|
return FALSE
|
|
return FALSE //Unsupported slot
|
|
|
|
/datum/species/proc/equip_delay_self_check(obj/item/I, mob/living/carbon/human/H, bypass_equip_delay_self)
|
|
if(!I.equip_delay_self || bypass_equip_delay_self)
|
|
return TRUE
|
|
H.visible_message("<span class='notice'>[H] start putting on [I]...</span>", "<span class='notice'>You start putting on [I]...</span>")
|
|
return do_after(H, I.equip_delay_self, target = H)
|
|
|
|
/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)
|
|
H.update_mutant_bodyparts()
|
|
|
|
/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.del_reagent(chem.id)
|
|
return 1
|
|
return FALSE
|
|
|
|
/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, mob/living/attacker)
|
|
return FALSE
|
|
|
|
////////
|
|
//LIFE//
|
|
////////
|
|
|
|
/datum/species/proc/handle_digestion(mob/living/carbon/human/H)
|
|
|
|
//The fucking TRAIT_FAT mutation is the dumbest shit ever. It makes the code so difficult to work with
|
|
if(H.has_trait(TRAIT_FAT))//I share your pain, past coder.
|
|
if(H.overeatduration < 100)
|
|
to_chat(H, "<span class='notice'>You feel fit again!</span>")
|
|
H.remove_trait(TRAIT_FAT, OBESITY)
|
|
H.update_inv_w_uniform()
|
|
H.update_inv_wear_suit()
|
|
else
|
|
if(H.overeatduration >= 100)
|
|
to_chat(H, "<span class='danger'>You suddenly feel blubbery!</span>")
|
|
H.add_trait(TRAIT_FAT, OBESITY)
|
|
H.update_inv_w_uniform()
|
|
H.update_inv_wear_suit()
|
|
|
|
// nutrition decrease and satiety
|
|
if (H.nutrition > 0 && H.stat != DEAD && !H.has_trait(TRAIT_NOHUNGER))
|
|
// THEY HUNGER
|
|
var/hunger_rate = HUNGER_FACTOR
|
|
GET_COMPONENT_FROM(mood, /datum/component/mood, H)
|
|
if(mood && mood.sanity > SANITY_DISTURBED)
|
|
hunger_rate *= max(0.5, 1 - 0.002 * mood.sanity) //0.85 to 0.75
|
|
|
|
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
|
|
hunger_rate *= H.physiology.hunger_mod
|
|
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.has_trait(TRAIT_NOHUNGER))
|
|
to_chat(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)
|
|
to_chat(H, "<span class='notice'>You feel sluggish.</span>")
|
|
H.metabolism_efficiency = 0.8
|
|
else
|
|
if(H.metabolism_efficiency == 1.25)
|
|
to_chat(H, "<span class='notice'>You no longer feel vigorous.</span>")
|
|
H.metabolism_efficiency = 1
|
|
|
|
switch(H.nutrition)
|
|
if(NUTRITION_LEVEL_FULL to INFINITY)
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/fat)
|
|
H.throw_alert("nutrition", /obj/screen/alert/fat)
|
|
if(NUTRITION_LEVEL_WELL_FED to NUTRITION_LEVEL_FULL)
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/wellfed)
|
|
H.clear_alert("nutrition")
|
|
if( NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED)
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/fed)
|
|
H.clear_alert("nutrition")
|
|
if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED)
|
|
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "nutrition")
|
|
H.clear_alert("nutrition")
|
|
if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY)
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/hungry)
|
|
H.throw_alert("nutrition", /obj/screen/alert/hungry)
|
|
if(0 to NUTRITION_LEVEL_STARVING)
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "nutrition", /datum/mood_event/nutrition/starving)
|
|
H.throw_alert("nutrition", /obj/screen/alert/starving)
|
|
|
|
/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)
|
|
. = FALSE
|
|
var/radiation = H.radiation
|
|
|
|
if(H.has_trait(TRAIT_RADIMMUNE))
|
|
radiation = 0
|
|
return TRUE
|
|
|
|
if(radiation > RAD_MOB_KNOCKDOWN && prob(RAD_MOB_KNOCKDOWN_PROB))
|
|
if(!H.IsKnockdown())
|
|
H.emote("collapse")
|
|
H.Knockdown(RAD_MOB_KNOCKDOWN_AMOUNT)
|
|
to_chat(H, "<span class='danger'>You feel weak.</span>")
|
|
|
|
if(radiation > RAD_MOB_VOMIT && prob(RAD_MOB_VOMIT_PROB))
|
|
H.vomit(10, TRUE)
|
|
|
|
if(radiation > RAD_MOB_MUTATE)
|
|
if(prob(1))
|
|
to_chat(H, "<span class='danger'>You mutate!</span>")
|
|
H.randmutb()
|
|
H.emote("gasp")
|
|
H.domutcheck()
|
|
|
|
if(radiation > RAD_MOB_HAIRLOSS)
|
|
if(prob(15) && !(H.hair_style == "Bald") && (HAIR in species_traits))
|
|
to_chat(H, "<span class='danger'>Your hair starts to fall out in clumps...</span>")
|
|
addtimer(CALLBACK(src, .proc/go_bald, H), 50)
|
|
|
|
/datum/species/proc/go_bald(mob/living/carbon/human/H)
|
|
if(QDELETED(H)) //may be called from a timer
|
|
return
|
|
H.facial_hair_style = "Shaved"
|
|
H.hair_style = "Bald"
|
|
H.update_hair()
|
|
|
|
////////////////
|
|
// MOVE SPEED //
|
|
////////////////
|
|
|
|
/datum/species/proc/movement_delay(mob/living/carbon/human/H)
|
|
. = 0 //We start at 0.
|
|
var/flight = 0 //Check for flight and flying items
|
|
var/flightpack = 0
|
|
var/ignoreslow = 0
|
|
var/gravity = 0
|
|
var/obj/item/flightpack/F = H.get_flightpack()
|
|
if(istype(F) && F.flight)
|
|
flightpack = 1
|
|
if(H.movement_type & FLYING)
|
|
flight = 1
|
|
|
|
gravity = H.has_gravity()
|
|
|
|
if(!flightpack && gravity) //Check for chemicals and innate speedups and slowdowns if we're moving using our body and not a flying suit
|
|
if(H.has_trait(TRAIT_GOTTAGOFAST))
|
|
. -= 1
|
|
if(H.has_trait(TRAIT_GOTTAGOREALLYFAST))
|
|
. -= 2
|
|
. += speedmod
|
|
. += H.physiology.speed_mod
|
|
|
|
if(H.has_trait(TRAIT_IGNORESLOWDOWN))
|
|
ignoreslow = 1
|
|
|
|
if(!gravity)
|
|
var/obj/item/tank/jetpack/J = H.back
|
|
var/obj/item/clothing/suit/space/hardsuit/C = H.wear_suit
|
|
var/obj/item/organ/cyberimp/chest/thrusters/T = H.getorganslot(ORGAN_SLOT_THRUSTERS)
|
|
if(!istype(J) && istype(C))
|
|
J = C.jetpack
|
|
if(istype(J) && J.full_speed && J.allow_thrust(0.01, H)) //Prevents stacking
|
|
. -= 2
|
|
else if(istype(T) && T.allow_thrust(0.01, H))
|
|
. -= 2
|
|
|
|
if(flightpack && F.boost)
|
|
. -= F.boost_speed
|
|
else if(flightpack && F.brake)
|
|
. += 1
|
|
|
|
if(!ignoreslow && !flightpack && gravity)
|
|
if(H.wear_suit)
|
|
. += H.wear_suit.slowdown
|
|
if(H.shoes)
|
|
. += H.shoes.slowdown
|
|
if(H.back)
|
|
. += H.back.slowdown
|
|
for(var/obj/item/I in H.held_items)
|
|
if(I.item_flags & SLOWS_WHILE_IN_HAND)
|
|
. += I.slowdown
|
|
var/health_deficiency = (100 - H.health + H.staminaloss)
|
|
if(health_deficiency >= 40)
|
|
if(flight)
|
|
. += (health_deficiency / 75)
|
|
else
|
|
. += (health_deficiency / 25)
|
|
if(CONFIG_GET(flag/disable_human_mood))
|
|
var/hungry = (500 - H.nutrition) / 5 //So overeat would be 100 and default level would be 80
|
|
if((hungry >= 70) && !flight) //Being hungry will still allow you to use a flightsuit/wings.
|
|
. += hungry / 50
|
|
|
|
//Moving in high gravity is very slow (Flying too)
|
|
if(gravity > STANDARD_GRAVITY)
|
|
var/grav_force = min(gravity - STANDARD_GRAVITY,3)
|
|
. += 1 + grav_force
|
|
|
|
GET_COMPONENT_FROM(mood, /datum/component/mood, H)
|
|
if(mood && !flight) //How can depression slow you down if you can just fly away from your problems?
|
|
switch(mood.sanity)
|
|
if(SANITY_INSANE to SANITY_CRAZY)
|
|
. += 1.5
|
|
if(SANITY_CRAZY to SANITY_UNSTABLE)
|
|
. += 1
|
|
if(SANITY_UNSTABLE to SANITY_DISTURBED)
|
|
. += 0.5
|
|
|
|
if(H.has_trait(TRAIT_FAT))
|
|
. += (1.5 - flight)
|
|
if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !H.has_trait(TRAIT_RESISTCOLD))
|
|
. += (BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR
|
|
return .
|
|
|
|
//////////////////
|
|
// ATTACK PROCS //
|
|
//////////////////
|
|
|
|
//////////////////
|
|
// ATTACK PROCS //
|
|
//////////////////
|
|
|
|
/datum/species/proc/help(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
if(target.health >= 0 && !(target.has_trait(TRAIT_FAKEDEATH)))
|
|
target.help_shake_act(user)
|
|
if(target != user)
|
|
add_logs(user, target, "shaked")
|
|
return 1
|
|
else
|
|
var/we_breathe = !user.has_trait(TRAIT_NOBREATH)
|
|
var/we_lung = user.getorganslot(ORGAN_SLOT_LUNGS)
|
|
|
|
if(we_breathe && we_lung)
|
|
user.do_cpr(target)
|
|
else if(we_breathe && !we_lung)
|
|
to_chat(user, "<span class='warning'>You have no lungs to breathe with, so you cannot peform CPR.</span>")
|
|
else
|
|
to_chat(user, "<span class='notice'>You do not breathe, so you cannot perform CPR.</span>")
|
|
|
|
/datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
if(target.check_block())
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s grab attempt!</span>")
|
|
return 0
|
|
if(attacker_style && attacker_style.grab_act(user,target))
|
|
return 1
|
|
else
|
|
target.grabbedby(user)
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
/datum/species/proc/harm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
if(user.has_trait(TRAIT_PACIFISM))
|
|
to_chat(user, "<span class='warning'>You don't want to harm [target]!</span>")
|
|
return FALSE
|
|
if(target.check_block())
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s attack!</span>")
|
|
return FALSE
|
|
if(attacker_style && attacker_style.harm_act(user,target))
|
|
return TRUE
|
|
else
|
|
|
|
var/atk_verb = user.dna.species.attack_verb
|
|
if(target.lying)
|
|
atk_verb = "kick"
|
|
|
|
switch(atk_verb)
|
|
if("kick")
|
|
user.do_attack_animation(target, ATTACK_EFFECT_KICK)
|
|
if("slash")
|
|
user.do_attack_animation(target, ATTACK_EFFECT_CLAW)
|
|
if("smash")
|
|
user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
|
|
else
|
|
user.do_attack_animation(target, ATTACK_EFFECT_PUNCH)
|
|
|
|
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
|
|
|
|
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
|
|
|
|
if(!damage || !affecting)
|
|
playsound(target.loc, user.dna.species.miss_sound, 25, 1, -1)
|
|
target.visible_message("<span class='danger'>[user] has attempted to [atk_verb] [target]!</span>",\
|
|
"<span class='userdanger'>[user] has attempted to [atk_verb] [target]!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
return FALSE
|
|
|
|
|
|
var/armor_block = target.run_armor_check(affecting, "melee")
|
|
|
|
playsound(target.loc, user.dna.species.attack_sound, 25, 1, -1)
|
|
|
|
target.visible_message("<span class='danger'>[user] has [atk_verb]ed [target]!</span>", \
|
|
"<span class='userdanger'>[user] has [atk_verb]ed [target]!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
|
|
if(user.limb_destroyer)
|
|
target.dismembering_strike(user, affecting.body_zone)
|
|
target.apply_damage(damage, BRUTE, affecting, armor_block)
|
|
add_logs(user, target, "punched")
|
|
if((target.stat != DEAD) && damage >= user.dna.species.punchstunthreshold)
|
|
target.visible_message("<span class='danger'>[user] has knocked [target] down!</span>", \
|
|
"<span class='userdanger'>[user] has knocked [target] down!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
target.apply_effect(80, EFFECT_KNOCKDOWN, armor_block)
|
|
target.forcesay(GLOB.hit_appends)
|
|
else if(target.lying)
|
|
target.forcesay(GLOB.hit_appends)
|
|
|
|
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
if(target.check_block())
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s disarm attempt!</span>")
|
|
return 0
|
|
if(attacker_style && attacker_style.disarm_act(user,target))
|
|
return 1
|
|
else
|
|
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
|
|
|
|
if(target.w_uniform)
|
|
target.w_uniform.add_fingerprint(user)
|
|
var/randomized_zone = ran_zone(user.zone_selected)
|
|
SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected)
|
|
var/obj/item/bodypart/affecting = target.get_bodypart(randomized_zone)
|
|
var/randn = rand(1, 100)
|
|
if(randn <= 25)
|
|
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
|
target.visible_message("<span class='danger'>[user] has pushed [target]!</span>",
|
|
"<span class='userdanger'>[user] has pushed [target]!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
target.apply_effect(40, EFFECT_KNOCKDOWN, target.run_armor_check(affecting, "melee", "Your armor prevents your fall!", "Your armor softens your fall!"))
|
|
target.forcesay(GLOB.hit_appends)
|
|
add_logs(user, target, "pushed over")
|
|
return
|
|
|
|
if(randn <= 60)
|
|
var/obj/item/I = null
|
|
if(target.pulling)
|
|
target.visible_message("<span class='warning'>[user] has broken [target]'s grip on [target.pulling]!</span>")
|
|
target.stop_pulling()
|
|
else
|
|
I = target.get_active_held_item()
|
|
if(target.dropItemToGround(I))
|
|
target.visible_message("<span class='danger'>[user] has disarmed [target]!</span>", \
|
|
"<span class='userdanger'>[user] has disarmed [target]!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
else
|
|
I = null
|
|
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
|
add_logs(user, target, "disarmed", "[I ? " removing \the [I]" : ""]")
|
|
return
|
|
|
|
|
|
playsound(target, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
|
target.visible_message("<span class='danger'>[user] attempted to disarm [target]!</span>", \
|
|
"<span class='userdanger'>[user] attemped to disarm [target]!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
add_logs(user, target, "attempted to disarm")
|
|
|
|
|
|
/datum/species/proc/spec_hitby(atom/movable/AM, mob/living/carbon/human/H)
|
|
return
|
|
|
|
/datum/species/proc/spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style)
|
|
if(!istype(M))
|
|
return
|
|
CHECK_DNA_AND_SPECIES(M)
|
|
CHECK_DNA_AND_SPECIES(H)
|
|
|
|
if(!istype(M)) //sanity check for drones.
|
|
return
|
|
if(M.mind)
|
|
attacker_style = M.mind.martial_art
|
|
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 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
|
|
switch(M.a_intent)
|
|
if("help")
|
|
help(M, H, attacker_style)
|
|
|
|
if("grab")
|
|
grab(M, H, attacker_style)
|
|
|
|
if("harm")
|
|
harm(M, H, attacker_style)
|
|
|
|
if("disarm")
|
|
disarm(M, H, attacker_style)
|
|
|
|
/datum/species/proc/spec_attacked_by(obj/item/I, mob/living/user, obj/item/bodypart/affecting, intent, mob/living/carbon/human/H)
|
|
// Allows you to put in item-specific reactions based on species
|
|
if(user != H)
|
|
if(H.check_shields(I, I.force, "the [I.name]", MELEE_ATTACK, I.armour_penetration))
|
|
return 0
|
|
if(H.check_block())
|
|
H.visible_message("<span class='warning'>[H] blocks [I]!</span>")
|
|
return 0
|
|
|
|
var/hit_area
|
|
if(!affecting) //Something went wrong. Maybe the limb is missing?
|
|
affecting = H.bodyparts[1]
|
|
|
|
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) || (H.has_trait(TRAIT_EASYDISMEMBER) && prob(probability))) //try twice
|
|
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 == BODYPART_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(BODY_ZONE_HEAD)
|
|
if(H.stat == CONSCIOUS && armor_block < 50)
|
|
if(prob(I.force))
|
|
H.visible_message("<span class='danger'>[H] has been knocked senseless!</span>", \
|
|
"<span class='userdanger'>[H] has been knocked senseless!</span>")
|
|
H.confused = max(H.confused, 20)
|
|
H.adjustBrainLoss(20)
|
|
H.adjust_blurriness(10)
|
|
if(prob(10))
|
|
H.gain_trauma(/datum/brain_trauma/mild/concussion)
|
|
else
|
|
if(!I.is_sharp())
|
|
H.adjustBrainLoss(I.force * 0.2)
|
|
|
|
if(!I.is_sharp() && prob(I.force + ((100 - H.health) * 0.5)) && H != user) // rev deconversion through blunt trauma.
|
|
var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev)
|
|
if(rev)
|
|
rev.remove_revolutionary(FALSE, user)
|
|
|
|
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(BODY_ZONE_CHEST)
|
|
if(H.stat == CONSCIOUS && armor_block < 50)
|
|
if(prob(I.force))
|
|
H.visible_message("<span class='danger'>[H] has been knocked down!</span>", \
|
|
"<span class='userdanger'>[H] has been knocked down!</span>")
|
|
H.apply_effect(60, EFFECT_KNOCKDOWN, 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(GLOB.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)
|
|
var/hit_percent = (100-(blocked+armor))/100
|
|
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
|
|
if(!damage || hit_percent <= 0)
|
|
return 0
|
|
|
|
var/obj/item/bodypart/BP = null
|
|
if(isbodypart(def_zone))
|
|
BP = def_zone
|
|
else
|
|
if(!def_zone)
|
|
def_zone = ran_zone(def_zone)
|
|
BP = H.get_bodypart(check_zone(def_zone))
|
|
if(!BP)
|
|
BP = H.bodyparts[1]
|
|
|
|
switch(damagetype)
|
|
if(BRUTE)
|
|
H.damageoverlaytemp = 20
|
|
if(BP)
|
|
if(BP.receive_damage(damage * hit_percent * brutemod * H.physiology.brute_mod, 0))
|
|
H.update_damage_overlays()
|
|
else//no bodypart, we deal damage with a more general method.
|
|
H.adjustBruteLoss(damage * hit_percent * brutemod * H.physiology.brute_mod)
|
|
if(BURN)
|
|
H.damageoverlaytemp = 20
|
|
if(BP)
|
|
if(BP.receive_damage(0, damage * hit_percent * burnmod * H.physiology.burn_mod))
|
|
H.update_damage_overlays()
|
|
else
|
|
H.adjustFireLoss(damage * hit_percent * burnmod * H.physiology.burn_mod)
|
|
if(TOX)
|
|
H.adjustToxLoss(damage * hit_percent * H.physiology.tox_mod)
|
|
if(OXY)
|
|
H.adjustOxyLoss(damage * hit_percent * H.physiology.oxy_mod)
|
|
if(CLONE)
|
|
H.adjustCloneLoss(damage * hit_percent * H.physiology.clone_mod)
|
|
if(STAMINA)
|
|
if(BP)
|
|
if(BP.receive_damage(0, 0, damage * hit_percent * H.physiology.stamina_mod))
|
|
H.update_stamina()
|
|
else
|
|
H.adjustStaminaLoss(damage * hit_percent * H.physiology.stamina_mod)
|
|
if(BRAIN)
|
|
H.adjustBrainLoss(damage * hit_percent * H.physiology.brain_mod)
|
|
return 1
|
|
|
|
/datum/species/proc/on_hit(obj/item/projectile/P, mob/living/carbon/human/H)
|
|
// called when hit by a projectile
|
|
switch(P.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>")
|
|
|
|
/datum/species/proc/bullet_act(obj/item/projectile/P, mob/living/carbon/human/H)
|
|
// called before a projectile hit
|
|
return 0
|
|
|
|
/////////////
|
|
//BREATHING//
|
|
/////////////
|
|
|
|
/datum/species/proc/breathe(mob/living/carbon/human/H)
|
|
if(H.has_trait(TRAIT_NOBREATH))
|
|
return TRUE
|
|
|
|
|
|
/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 parts: first there your body tries to naturally preserve homeostasis (shivering/sweating), then it reacts to the surrounding environment
|
|
//Thermal protection (insulation) has mixed benefits in two situations (hot in hot places, cold in hot places)
|
|
if(!H.on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases
|
|
var/natural = 0
|
|
if(H.stat != DEAD)
|
|
natural = H.natural_bodytemperature_stabilization()
|
|
var/thermal_protection = 1
|
|
if(loc_temp < H.bodytemperature) //Place is colder than we are
|
|
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(H.bodytemperature < BODYTEMP_NORMAL) //we're cold, insulation helps us retain body heat and will reduce the heat we lose to the environment
|
|
H.adjust_bodytemperature((thermal_protection+1)*natural + max(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
|
|
else //we're sweating, insulation hinders our ability to reduce heat - and it will reduce the amount of cooling you get from the environment
|
|
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + max((thermal_protection * (loc_temp - H.bodytemperature) + BODYTEMP_NORMAL - H.bodytemperature) / BODYTEMP_COLD_DIVISOR , BODYTEMP_COOLING_MAX)) //Extra calculation for hardsuits to bleed off heat
|
|
else //Place is hotter than we are
|
|
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(H.bodytemperature < BODYTEMP_NORMAL) //and we're cold, insulation enhances our ability to retain body heat but reduces the heat we get from the environment
|
|
H.adjust_bodytemperature((thermal_protection+1)*natural + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
|
|
else //we're sweating, insulation hinders out ability to reduce heat - but will reduce the amount of heat we get from the environment
|
|
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + min(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR, BODYTEMP_HEATING_MAX))
|
|
|
|
// +/- 50 degrees from 310K is the 'safe' zone, where no damage is dealt.
|
|
if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !H.has_trait(TRAIT_RESISTHEAT))
|
|
//Body temperature is too hot.
|
|
|
|
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "hot", /datum/mood_event/hot)
|
|
|
|
var/burn_damage
|
|
switch(H.bodytemperature)
|
|
if(BODYTEMP_HEAT_DAMAGE_LIMIT to 400)
|
|
H.throw_alert("temp", /obj/screen/alert/hot, 1)
|
|
burn_damage = HEAT_DAMAGE_LEVEL_1
|
|
if(400 to 460)
|
|
H.throw_alert("temp", /obj/screen/alert/hot, 2)
|
|
burn_damage = HEAT_DAMAGE_LEVEL_2
|
|
else
|
|
H.throw_alert("temp", /obj/screen/alert/hot, 3)
|
|
if(H.on_fire)
|
|
burn_damage = HEAT_DAMAGE_LEVEL_3
|
|
else
|
|
burn_damage = HEAT_DAMAGE_LEVEL_2
|
|
burn_damage = burn_damage * heatmod * H.physiology.heat_mod
|
|
if (H.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4) //40% for level 3 damage on humans
|
|
H.emote("scream")
|
|
H.apply_damage(burn_damage, BURN)
|
|
|
|
else if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !H.has_trait(TRAIT_RESISTCOLD))
|
|
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "cold", /datum/mood_event/cold)
|
|
switch(H.bodytemperature)
|
|
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
|
|
H.throw_alert("temp", /obj/screen/alert/cold, 1)
|
|
H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod*H.physiology.cold_mod, BURN)
|
|
if(120 to 200)
|
|
H.throw_alert("temp", /obj/screen/alert/cold, 2)
|
|
H.apply_damage(COLD_DAMAGE_LEVEL_2*coldmod*H.physiology.cold_mod, BURN)
|
|
else
|
|
H.throw_alert("temp", /obj/screen/alert/cold, 3)
|
|
H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod*H.physiology.cold_mod, BURN)
|
|
|
|
else
|
|
H.clear_alert("temp")
|
|
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
|
|
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
|
|
|
|
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(!H.has_trait(TRAIT_RESISTHIGHPRESSURE))
|
|
H.adjustBruteLoss(min(((adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 ) * PRESSURE_DAMAGE_COEFFICIENT, MAX_HIGH_PRESSURE_DAMAGE) * H.physiology.pressure_mod)
|
|
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.has_trait(TRAIT_RESISTLOWPRESSURE))
|
|
H.clear_alert("pressure")
|
|
else
|
|
H.adjustBruteLoss(LOW_PRESSURE_DAMAGE * H.physiology.pressure_mod)
|
|
H.throw_alert("pressure", /obj/screen/alert/lowpressure, 2)
|
|
|
|
//////////
|
|
// FIRE //
|
|
//////////
|
|
|
|
/datum/species/proc/handle_fire(mob/living/carbon/human/H, no_protection = FALSE)
|
|
if(H.has_trait(TRAIT_NOFIRE))
|
|
return
|
|
if(H.on_fire)
|
|
//the fire tries to damage the exposed clothes and items
|
|
var/list/burning_items = list()
|
|
//HEAD//
|
|
var/obj/item/clothing/head_clothes = null
|
|
if(H.glasses)
|
|
head_clothes = H.glasses
|
|
if(H.wear_mask)
|
|
head_clothes = H.wear_mask
|
|
if(H.wear_neck)
|
|
head_clothes = H.wear_neck
|
|
if(H.head)
|
|
head_clothes = H.head
|
|
if(head_clothes)
|
|
burning_items += head_clothes
|
|
else if(H.ears)
|
|
burning_items += H.ears
|
|
|
|
//CHEST//
|
|
var/obj/item/clothing/chest_clothes = null
|
|
if(H.w_uniform)
|
|
chest_clothes = H.w_uniform
|
|
if(H.wear_suit)
|
|
chest_clothes = H.wear_suit
|
|
|
|
if(chest_clothes)
|
|
burning_items += chest_clothes
|
|
|
|
//ARMS & HANDS//
|
|
var/obj/item/clothing/arm_clothes = null
|
|
if(H.gloves)
|
|
arm_clothes = H.gloves
|
|
if(H.w_uniform && ((H.w_uniform.body_parts_covered & HANDS) || (H.w_uniform.body_parts_covered & ARMS)))
|
|
arm_clothes = H.w_uniform
|
|
if(H.wear_suit && ((H.wear_suit.body_parts_covered & HANDS) || (H.wear_suit.body_parts_covered & ARMS)))
|
|
arm_clothes = H.wear_suit
|
|
if(arm_clothes)
|
|
burning_items |= arm_clothes
|
|
|
|
//LEGS & FEET//
|
|
var/obj/item/clothing/leg_clothes = null
|
|
if(H.shoes)
|
|
leg_clothes = H.shoes
|
|
if(H.w_uniform && ((H.w_uniform.body_parts_covered & FEET) || (H.w_uniform.body_parts_covered & LEGS)))
|
|
leg_clothes = H.w_uniform
|
|
if(H.wear_suit && ((H.wear_suit.body_parts_covered & FEET) || (H.wear_suit.body_parts_covered & LEGS)))
|
|
leg_clothes = H.wear_suit
|
|
if(leg_clothes)
|
|
burning_items |= leg_clothes
|
|
|
|
for(var/X in burning_items)
|
|
var/obj/item/I = X
|
|
if(!(I.resistance_flags & FIRE_PROOF))
|
|
I.take_damage(H.fire_stacks, BURN, "fire", 0)
|
|
|
|
var/thermal_protection = H.get_thermal_protection()
|
|
|
|
if(thermal_protection >= FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT && !no_protection)
|
|
return
|
|
if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT && !no_protection)
|
|
H.adjust_bodytemperature(11)
|
|
else
|
|
H.adjust_bodytemperature(BODYTEMP_HEATING_MAX + (H.fire_stacks * 12))
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "on_fire", /datum/mood_event/on_fire)
|
|
|
|
/datum/species/proc/CanIgniteMob(mob/living/carbon/human/H)
|
|
if(H.has_trait(TRAIT_NOFIRE))
|
|
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 * H.physiology.stun_mod * amount
|
|
|
|
//////////////
|
|
//Space Move//
|
|
//////////////
|
|
|
|
/datum/species/proc/space_move(mob/living/carbon/human/H)
|
|
return 0
|
|
|
|
/datum/species/proc/negates_gravity(mob/living/carbon/human/H)
|
|
return 0
|