2235 lines
89 KiB
Plaintext
2235 lines
89 KiB
Plaintext
// This code handles different species in the game.
|
|
|
|
GLOBAL_LIST_EMPTY(roundstart_races)
|
|
GLOBAL_LIST_EMPTY(roundstart_race_names)
|
|
|
|
/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/has_field_of_vision = TRUE
|
|
|
|
//Species Icon Drawing Offsets - Pixel X, Pixel Y, Aka X = Horizontal and Y = Vertical, from bottom left corner
|
|
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_EYES = list(0,0),
|
|
OFFSET_LIPS = list(0,0),
|
|
OFFSET_BELT = list(0,0),
|
|
OFFSET_BACK = list(0,0),
|
|
OFFSET_HAIR = list(0,0),
|
|
OFFSET_FHAIR = list(0,0),
|
|
OFFSET_SUIT = list(0,0),
|
|
OFFSET_NECK = list(0,0),
|
|
OFFSET_MUTPARTS = 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 = NO_SKINTONES // 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/list/gib_types = list(/obj/effect/gibspawner/human, /obj/effect/gibspawner/human/bodypartless)
|
|
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/species_language_holder = /datum/language_holder
|
|
var/list/mutant_bodyparts = list() // Visible CURRENT bodyparts that are unique to a species. 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/stunmod = 1 // multiplier for stun duration
|
|
var/punchdamagelow = 1 //lowest possible punch damage. if this is set to 0, punches will always miss
|
|
var/punchdamagehigh = 10 //highest possible punch damage
|
|
var/punchstunthreshold = 10//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"]
|
|
var/inert_mutation = DWARFISM
|
|
var/list/special_step_sounds //Sounds to override barefeet walkng
|
|
var/grab_sound //Special sound for grabbing
|
|
var/datum/outfit/outfit_important_for_life // A path to an outfit that is important for species life e.g. plasmaman outfit
|
|
|
|
// 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/inherent_biotypes = 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/list/mob/living/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
|
|
|
|
//Citadel snowflake
|
|
var/fixed_mut_color2 = ""
|
|
var/fixed_mut_color3 = ""
|
|
var/whitelisted = 0 //Is this species restricted to certain players?
|
|
var/whitelist = list() //List the ckeys that can use this species, if it's whitelisted.: list("John Doe", "poopface666", "SeeALiggerPullTheTrigger") Spaces & capitalization can be included or ignored entirely for each key as it checks for both.
|
|
var/icon_limbs //Overrides the icon used for the limbs of this species. Mainly for downstream, and also because hardcoded icons disgust me. Implemented and maintained as a favor in return for a downstream's implementation of synths.
|
|
|
|
/// Our default override for typing indicator state
|
|
var/typing_indicator_state
|
|
|
|
///////////
|
|
// 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(clear = FALSE)
|
|
if(clear)
|
|
GLOB.roundstart_races = list()
|
|
GLOB.roundstart_race_names = list()
|
|
for(var/I in subtypesof(/datum/species))
|
|
var/datum/species/S = new I
|
|
if(S.check_roundstart_eligible())
|
|
GLOB.roundstart_races |= S.id
|
|
GLOB.roundstart_race_names["[S.name]"] = 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_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) //SPECIES JOB RESTRICTIONS
|
|
//if(rank in GLOB.command_positions) Left as an example: The format qualifies for rank takes.
|
|
// return 0 //It returns false when it runs the proc so they don't get jobs from the global list.
|
|
return 1 //It returns 1 to say they are a-okay to continue.
|
|
|
|
//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(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(TRUE)
|
|
QDEL_NULL(heart)
|
|
if(should_have_heart && !heart)
|
|
heart = new mutant_heart()
|
|
heart.Insert(C)
|
|
|
|
if(lungs && (!should_have_lungs || replace_current))
|
|
lungs.Remove(TRUE)
|
|
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(TRUE)
|
|
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(TRUE)
|
|
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(TRUE)
|
|
QDEL_NULL(appendix)
|
|
if(should_have_appendix && !appendix)
|
|
appendix = new()
|
|
appendix.Insert(C)
|
|
|
|
if(tail && (!should_have_tail || replace_current))
|
|
tail.Remove(TRUE)
|
|
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(TRUE)
|
|
QDEL_NULL(eyes)
|
|
if(should_have_eyes && !eyes)
|
|
eyes = new mutanteyes
|
|
eyes.Insert(C, TRUE)
|
|
|
|
if(ears && (replace_current || !should_have_ears))
|
|
ears.Remove(TRUE)
|
|
QDEL_NULL(ears)
|
|
if(should_have_ears && !ears)
|
|
ears = new mutantears
|
|
ears.Insert(C)
|
|
|
|
if(tongue && (replace_current || !should_have_tongue))
|
|
tongue.Remove(TRUE)
|
|
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()
|
|
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, pref_load)
|
|
// 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(C.dna.species.mutant_bodyparts["legs"] && (C.dna.features["legs"] == "Digitigrade" || C.dna.features["legs"] == "Avian"))
|
|
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 TRAIT_NODROP 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)
|
|
ADD_TRAIT(C, X, SPECIES_TRAIT)
|
|
|
|
if(TRAIT_VIRUSIMMUNE in inherent_traits)
|
|
for(var/datum/disease/A in C.diseases)
|
|
A.cure(FALSE)
|
|
|
|
if(ishuman(C))
|
|
var/mob/living/carbon/human/H = C
|
|
if(NOGENITALS in H.dna.species.species_traits)
|
|
H.give_genitals(TRUE) //call the clean up proc to delete anything on the mob then return.
|
|
if(mutant_bodyparts["meat_type"]) //I can't believe it's come to the meat
|
|
H.type_of_meat = GLOB.meat_types[H.dna.features["meat_type"]]
|
|
|
|
if(H.physiology)
|
|
if(mutant_bodyparts["taur"])
|
|
var/datum/sprite_accessory/taur/T = GLOB.taur_list[H.dna.features["taur"]]
|
|
switch(T?.taur_mode)
|
|
if(STYLE_HOOF_TAURIC)
|
|
H.physiology.footstep_type = FOOTSTEP_MOB_SHOE
|
|
if(STYLE_PAW_TAURIC)
|
|
H.physiology.footstep_type = FOOTSTEP_MOB_CLAW
|
|
if(STYLE_SNEK_TAURIC)
|
|
H.physiology.footstep_type = FOOTSTEP_MOB_CRAWL
|
|
else
|
|
H.physiology.footstep_type = null
|
|
else
|
|
H.physiology.footstep_type = null
|
|
|
|
if(H.client && has_field_of_vision && CONFIG_GET(flag/use_field_of_vision))
|
|
H.LoadComponent(/datum/component/field_of_vision, H.field_of_vision_type)
|
|
|
|
C.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/species, TRUE, multiplicative_slowdown = speedmod)
|
|
|
|
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
|
|
|
|
|
|
// EDIT ENDS
|
|
|
|
/datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
|
|
if(C.dna.species.exotic_bloodtype)
|
|
if(!new_species.exotic_bloodtype)
|
|
C.dna.blood_type = random_blood_type()
|
|
else
|
|
C.dna.blood_type = new_species.exotic_bloodtype
|
|
if(DIGITIGRADE in species_traits)
|
|
C.Digitigrade_Leg_Swap(TRUE)
|
|
for(var/X in inherent_traits)
|
|
REMOVE_TRAIT(C, X, SPECIES_TRAIT)
|
|
|
|
C.remove_movespeed_modifier(/datum/movespeed_modifier/species)
|
|
|
|
if(mutant_bodyparts["meat_type"])
|
|
C.type_of_meat = GLOB.meat_types[C.dna.features["meat_type"]]
|
|
else
|
|
C.type_of_meat = initial(meat)
|
|
|
|
//If their inert mutation is not the same, swap it out
|
|
if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
|
|
C.dna.remove_mutation(inert_mutation)
|
|
//keep it at the right spot, so we can't have people taking shortcuts
|
|
var/location = C.dna.mutation_index.Find(inert_mutation)
|
|
C.dna.mutation_index[location] = new_species.inert_mutation
|
|
C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
|
|
|
|
if(!new_species.has_field_of_vision && has_field_of_vision && ishuman(C) && CONFIG_GET(flag/use_field_of_vision))
|
|
var/datum/component/field_of_vision/F = C.GetComponent(/datum/component/field_of_vision)
|
|
if(F)
|
|
qdel(F)
|
|
|
|
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
|
|
|
|
/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(HAS_TRAIT(H, 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 && istype(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
|
|
|
|
if(OFFSET_FHAIR in H.dna.species.offset_features)
|
|
facial_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FHAIR][1]
|
|
facial_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FHAIR][2]
|
|
|
|
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 && istype(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_HAIR in H.dna.species.offset_features)
|
|
hair_overlay.pixel_x += H.dna.species.offset_features[OFFSET_HAIR][1]
|
|
hair_overlay.pixel_y += H.dna.species.offset_features[OFFSET_HAIR][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 && !(HAS_TRAIT(H, 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_LIPS in H.dna.species.offset_features)
|
|
lip_overlay.pixel_x += H.dna.species.offset_features[OFFSET_LIPS][1]
|
|
lip_overlay.pixel_y += H.dna.species.offset_features[OFFSET_LIPS][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_EYES in H.dna.species.offset_features)
|
|
eye_overlay.pixel_x += H.dna.species.offset_features[OFFSET_EYES][1]
|
|
eye_overlay.pixel_y += H.dna.species.offset_features[OFFSET_EYES][2]
|
|
|
|
standing += eye_overlay
|
|
|
|
//Underwear, Undershirts & Socks
|
|
if(!(NO_UNDERWEAR in species_traits))
|
|
var/datum/sprite_accessory/taur/TA
|
|
if(mutant_bodyparts["taur"] && H.dna.features["taur"])
|
|
TA = GLOB.taur_list[H.dna.features["taur"]]
|
|
if(!(TA?.hide_legs) && H.socks && !H.hidden_socks && H.get_num_legs(FALSE) >= 2)
|
|
if(H.saved_socks)
|
|
H.socks = H.saved_socks
|
|
H.saved_socks = ""
|
|
var/datum/sprite_accessory/underwear/socks/S = GLOB.socks_list[H.socks]
|
|
if(S)
|
|
var/digilegs = ((DIGITIGRADE in species_traits) && S.has_digitigrade) ? "_d" : ""
|
|
var/mutable_appearance/MA = mutable_appearance(S.icon, "[S.icon_state][digilegs]", -BODY_LAYER)
|
|
if(S.has_color)
|
|
MA.color = "#[H.socks_color]"
|
|
standing += MA
|
|
|
|
if(H.underwear && !H.hidden_underwear)
|
|
if(H.saved_underwear)
|
|
H.underwear = H.saved_underwear
|
|
H.saved_underwear = ""
|
|
var/datum/sprite_accessory/underwear/bottom/B = GLOB.underwear_list[H.underwear]
|
|
if(B)
|
|
var/digilegs = ((DIGITIGRADE in species_traits) && B.has_digitigrade) ? "_d" : ""
|
|
var/mutable_appearance/MA = mutable_appearance(B.icon, "[B.icon_state][digilegs]", -BODY_LAYER)
|
|
if(B.has_color)
|
|
MA.color = "#[H.undie_color]"
|
|
standing += MA
|
|
|
|
if(H.undershirt && !H.hidden_undershirt)
|
|
if(H.saved_undershirt)
|
|
H.undershirt = H.saved_undershirt
|
|
H.saved_undershirt = ""
|
|
var/datum/sprite_accessory/underwear/top/T = GLOB.undershirt_list[H.undershirt]
|
|
if(T)
|
|
var/state = "[T.icon_state][((DIGITIGRADE in species_traits) && T.has_digitigrade) ? "_d" : ""]"
|
|
var/mutable_appearance/MA
|
|
if(H.dna.species.sexes && H.dna.features["body_model"] == FEMALE)
|
|
MA = wear_alpha_masked_version(state, T.icon, BODY_LAYER, FEMALE_UNIFORM_TOP)
|
|
else
|
|
MA = mutable_appearance(T.icon, state, -BODY_LAYER)
|
|
if(T.has_color)
|
|
MA.color = "#[H.shirt_color]"
|
|
standing += MA
|
|
|
|
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()
|
|
|
|
H.remove_overlay(BODY_BEHIND_LAYER)
|
|
H.remove_overlay(BODY_ADJ_LAYER)
|
|
H.remove_overlay(BODY_ADJ_UPPER_LAYER)
|
|
H.remove_overlay(BODY_FRONT_LAYER)
|
|
|
|
if(!mutant_bodyparts)
|
|
return
|
|
|
|
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
|
|
var/tauric = mutant_bodyparts["taur"] && H.dna.features["taur"] && H.dna.features["taur"] != "None"
|
|
|
|
if(mutant_bodyparts["tail_lizard"])
|
|
if((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)
|
|
bodyparts_to_add -= "tail_lizard"
|
|
|
|
if(mutant_bodyparts["waggingtail_lizard"])
|
|
if((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)
|
|
bodyparts_to_add -= "waggingtail_lizard"
|
|
else if (mutant_bodyparts["tail_lizard"])
|
|
bodyparts_to_add -= "waggingtail_lizard"
|
|
|
|
if(mutant_bodyparts["tail_human"])
|
|
if((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)
|
|
bodyparts_to_add -= "tail_human"
|
|
|
|
if(mutant_bodyparts["waggingtail_human"])
|
|
if((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)
|
|
bodyparts_to_add -= "waggingtail_human"
|
|
else if (mutant_bodyparts["tail_human"])
|
|
bodyparts_to_add -= "waggingtail_human"
|
|
|
|
if(mutant_bodyparts["spines"])
|
|
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR))
|
|
bodyparts_to_add -= "spines"
|
|
|
|
if(mutant_bodyparts["waggingspines"])
|
|
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR))
|
|
bodyparts_to_add -= "waggingspines"
|
|
else if (mutant_bodyparts["tail"])
|
|
bodyparts_to_add -= "waggingspines"
|
|
|
|
if(mutant_bodyparts["snout"]) //Take a closer look at that snout!
|
|
if((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.status == BODYPART_ROBOTIC)
|
|
bodyparts_to_add -= "snout"
|
|
|
|
if(mutant_bodyparts["frills"])
|
|
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(mutant_bodyparts["horns"])
|
|
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(mutant_bodyparts["ears"])
|
|
if(!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.status == BODYPART_ROBOTIC)
|
|
bodyparts_to_add -= "ears"
|
|
|
|
if(mutant_bodyparts["wings"])
|
|
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(mutant_bodyparts["wings_open"])
|
|
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 (mutant_bodyparts["wings"])
|
|
bodyparts_to_add -= "wings_open"
|
|
|
|
if(mutant_bodyparts["insect_fluff"])
|
|
if(!H.dna.features["insect_fluff"] || H.dna.features["insect_fluff"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
|
|
bodyparts_to_add -= "insect_fluff"
|
|
|
|
//CITADEL EDIT
|
|
//Race specific bodyparts:
|
|
//Xenos
|
|
if(mutant_bodyparts["xenodorsal"])
|
|
if(!H.dna.features["xenodorsal"] || H.dna.features["xenodorsal"] == "None" || (H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT)))
|
|
bodyparts_to_add -= "xenodorsal"
|
|
if(mutant_bodyparts["xenohead"])//This is an overlay for different castes using different head crests
|
|
if(!H.dna.features["xenohead"] || H.dna.features["xenohead"] == "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 -= "xenohead"
|
|
if(mutant_bodyparts["xenotail"])
|
|
if(!H.dna.features["xenotail"] || H.dna.features["xenotail"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
|
|
bodyparts_to_add -= "xenotail"
|
|
|
|
//Other Races
|
|
if(mutant_bodyparts["mam_tail"])
|
|
if((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)
|
|
bodyparts_to_add -= "mam_tail"
|
|
|
|
if(mutant_bodyparts["mam_waggingtail"])
|
|
if((H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)) || tauric)
|
|
bodyparts_to_add -= "mam_waggingtail"
|
|
else if (mutant_bodyparts["mam_tail"])
|
|
bodyparts_to_add -= "mam_waggingtail"
|
|
|
|
if(mutant_bodyparts["mam_ears"])
|
|
if(!H.dna.features["mam_ears"] || H.dna.features["mam_ears"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEARS)) || !HD || HD.status == BODYPART_ROBOTIC)
|
|
bodyparts_to_add -= "mam_ears"
|
|
|
|
if(mutant_bodyparts["mam_snouts"]) //Take a closer look at that snout!
|
|
if((H.wear_mask && (H.wear_mask.flags_inv & HIDESNOUT)) || (H.head && (H.head.flags_inv & HIDESNOUT)) || !HD || HD.status == BODYPART_ROBOTIC)
|
|
bodyparts_to_add -= "mam_snouts"
|
|
|
|
if(mutant_bodyparts["taur"])
|
|
if(!tauric || (H.wear_suit && (H.wear_suit.flags_inv & HIDETAUR)))
|
|
bodyparts_to_add -= "taur"
|
|
|
|
//END EDIT
|
|
|
|
//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)
|
|
if(!(H.wear_suit.mutantrace_variation & STYLE_DIGITIGRADE) || (tauric && (H.wear_suit.mutantrace_variation & STYLE_ALL_TAURIC))) //digitigrade/taur suits
|
|
should_be_squished = TRUE
|
|
if(H.w_uniform && !H.wear_suit)
|
|
if(!(H.w_uniform.mutantrace_variation & STYLE_DIGITIGRADE))
|
|
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/list/relevant_layers = list()
|
|
var/list/dna_feature_as_text_string = list()
|
|
|
|
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("deco_wings")
|
|
S = GLOB.deco_wings_list[H.dna.features["deco_wings"]]
|
|
if("legs")
|
|
S = GLOB.legs_list[H.dna.features["legs"]]
|
|
if("insect_wings")
|
|
S = GLOB.insect_wings_list[H.dna.features["insect_wings"]]
|
|
if("insect_fluff")
|
|
S = GLOB.insect_fluffs_list[H.dna.features["insect_fluff"]]
|
|
if("insect_markings")
|
|
S = GLOB.insect_markings_list[H.dna.features["insect_markings"]]
|
|
if("caps")
|
|
S = GLOB.caps_list[H.dna.features["caps"]]
|
|
if("ipc_screen")
|
|
S = GLOB.ipc_screens_list[H.dna.features["ipc_screen"]]
|
|
if("ipc_antenna")
|
|
S = GLOB.ipc_antennas_list[H.dna.features["ipc_antenna"]]
|
|
if("mam_tail")
|
|
S = GLOB.mam_tails_list[H.dna.features["mam_tail"]]
|
|
if("mam_waggingtail")
|
|
S = GLOB.mam_tails_animated_list[H.dna.features["mam_tail"]]
|
|
if("mam_body_markings")
|
|
S = GLOB.mam_body_markings_list[H.dna.features["mam_body_markings"]]
|
|
if("mam_ears")
|
|
S = GLOB.mam_ears_list[H.dna.features["mam_ears"]]
|
|
if("mam_snouts")
|
|
S = GLOB.mam_snouts_list[H.dna.features["mam_snouts"]]
|
|
if("taur")
|
|
S = GLOB.taur_list[H.dna.features["taur"]]
|
|
if("xenodorsal")
|
|
S = GLOB.xeno_dorsal_list[H.dna.features["xenodorsal"]]
|
|
if("xenohead")
|
|
S = GLOB.xeno_head_list[H.dna.features["xenohead"]]
|
|
if("xenotail")
|
|
S = GLOB.xeno_tail_list[H.dna.features["xenotail"]]
|
|
|
|
if(!S || S.icon_state == "none")
|
|
continue
|
|
|
|
for(var/L in S.relevant_layers)
|
|
LAZYADD(relevant_layers["[L]"], S)
|
|
if(!S.mutant_part_string)
|
|
dna_feature_as_text_string[S] = bodypart
|
|
|
|
var/static/list/layer_text = list("[BODY_BEHIND_LAYER]" = "BEHIND", "[BODY_ADJ_LAYER]" = "ADJ", \
|
|
"[BODY_ADJ_UPPER_LAYER]" = "ADJUP", "[BODY_FRONT_LAYER]" = "FRONT")
|
|
|
|
var/g = (H.dna.features["body_model"] == FEMALE) ? "f" : "m"
|
|
var/list/colorlist = list()
|
|
var/husk = HAS_TRAIT(H, TRAIT_HUSK)
|
|
colorlist += husk ? ReadRGB("#a3a3a3") :ReadRGB("[H.dna.features["mcolor"]]0")
|
|
colorlist += husk ? ReadRGB("#a3a3a3") :ReadRGB("[H.dna.features["mcolor2"]]0")
|
|
colorlist += husk ? ReadRGB("#a3a3a3") : ReadRGB("[H.dna.features["mcolor3"]]0")
|
|
colorlist += list(0,0,0, hair_alpha)
|
|
for(var/index in 1 to colorlist.len)
|
|
colorlist[index] /= 255
|
|
|
|
for(var/layer in relevant_layers)
|
|
var/list/standing = list()
|
|
var/layertext = layer_text[layer]
|
|
if(!layertext) //shouldn't happen
|
|
stack_trace("invalid layer '[layer]' found in the list of relevant layers on species.handle_mutant_bodyparts().")
|
|
continue
|
|
var/layernum = text2num(layer)
|
|
for(var/bodypart in relevant_layers[layer])
|
|
var/datum/sprite_accessory/S = bodypart
|
|
var/mutable_appearance/accessory_overlay = mutable_appearance(S.icon, layer = -layernum)
|
|
bodypart = S.mutant_part_string || dna_feature_as_text_string[S]
|
|
|
|
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(!husk)
|
|
if(!forced_colour)
|
|
switch(S.color_src)
|
|
if(SKINTONE)
|
|
accessory_overlay.color = SKINTONE2HEX(H.skin_tone)
|
|
if(MUTCOLORS)
|
|
if(fixed_mut_color)
|
|
accessory_overlay.color = "#[fixed_mut_color]"
|
|
else
|
|
accessory_overlay.color = "#[H.dna.features["mcolor"]]"
|
|
if(MUTCOLORS2)
|
|
if(fixed_mut_color2)
|
|
accessory_overlay.color = "#[fixed_mut_color2]"
|
|
else
|
|
accessory_overlay.color = "#[H.dna.features["mcolor2"]]"
|
|
if(MUTCOLORS3)
|
|
if(fixed_mut_color3)
|
|
accessory_overlay.color = "#[fixed_mut_color3]"
|
|
else
|
|
accessory_overlay.color = "#[H.dna.features["mcolor3"]]"
|
|
|
|
if(MATRIXED)
|
|
accessory_overlay.color = list(colorlist)
|
|
|
|
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]"
|
|
if(HORNCOLOR)
|
|
accessory_overlay.color = "#[H.dna.features["horns_color"]]"
|
|
if(WINGCOLOR)
|
|
accessory_overlay.color = "#[H.dna.features["wings_color"]]"
|
|
else
|
|
accessory_overlay.color = forced_colour
|
|
else
|
|
if(bodypart == "ears")
|
|
accessory_overlay.icon_state = "m_ears_none_[layertext]"
|
|
if(bodypart == "tail")
|
|
accessory_overlay.icon_state = "m_tail_husk_[layertext]"
|
|
if(S.color_src == MATRIXED)
|
|
accessory_overlay.color = colorlist
|
|
|
|
if(OFFSET_MUTPARTS in H.dna.species.offset_features)
|
|
accessory_overlay.pixel_x += H.dna.species.offset_features[OFFSET_MUTPARTS][1]
|
|
accessory_overlay.pixel_y += H.dna.species.offset_features[OFFSET_MUTPARTS][2]
|
|
|
|
standing += accessory_overlay
|
|
|
|
if(S.extra) //apply the extra overlay, if there is one
|
|
var/mutable_appearance/extra_accessory_overlay = mutable_appearance(S.icon, layer = -layernum)
|
|
if(S.gender_specific)
|
|
extra_accessory_overlay.icon_state = "[g]_[bodypart]_extra_[S.icon_state]_[layertext]"
|
|
else
|
|
extra_accessory_overlay.icon_state = "m_[bodypart]_extra_[S.icon_state]_[layertext]"
|
|
if(S.center)
|
|
extra_accessory_overlay = center_image(extra_accessory_overlay, S.dimension_x, S.dimension_y)
|
|
|
|
|
|
switch(S.extra_color_src) //change the color of the extra overlay
|
|
if(MUTCOLORS)
|
|
if(fixed_mut_color)
|
|
extra_accessory_overlay.color = "#[fixed_mut_color]"
|
|
else
|
|
extra_accessory_overlay.color = "#[H.dna.features["mcolor"]]"
|
|
if(MUTCOLORS2)
|
|
if(fixed_mut_color2)
|
|
extra_accessory_overlay.color = "#[fixed_mut_color2]"
|
|
else
|
|
extra_accessory_overlay.color = "#[H.dna.features["mcolor2"]]"
|
|
if(MUTCOLORS3)
|
|
if(fixed_mut_color3)
|
|
extra_accessory_overlay.color = "#[fixed_mut_color3]"
|
|
else
|
|
extra_accessory_overlay.color = "#[H.dna.features["mcolor3"]]"
|
|
if(HAIR)
|
|
if(hair_color == "mutcolor")
|
|
extra_accessory_overlay.color = "#[H.dna.features["mcolor3"]]"
|
|
else
|
|
extra_accessory_overlay.color = "#[H.hair_color]"
|
|
if(FACEHAIR)
|
|
extra_accessory_overlay.color = "#[H.facial_hair_color]"
|
|
if(EYECOLOR)
|
|
extra_accessory_overlay.color = "#[H.eye_color]"
|
|
|
|
if(HORNCOLOR)
|
|
extra_accessory_overlay.color = "#[H.dna.features["horns_color"]]"
|
|
if(WINGCOLOR)
|
|
extra_accessory_overlay.color = "#[H.dna.features["wings_color"]]"
|
|
|
|
if(OFFSET_MUTPARTS in H.dna.species.offset_features)
|
|
extra_accessory_overlay.pixel_x += H.dna.species.offset_features[OFFSET_MUTPARTS][1]
|
|
extra_accessory_overlay.pixel_y += H.dna.species.offset_features[OFFSET_MUTPARTS][2]
|
|
|
|
standing += extra_accessory_overlay
|
|
|
|
if(S.extra2) //apply the extra overlay, if there is one
|
|
var/mutable_appearance/extra2_accessory_overlay = mutable_appearance(S.icon, layer = -layernum)
|
|
if(S.gender_specific)
|
|
extra2_accessory_overlay.icon_state = "[g]_[bodypart]_extra2_[S.icon_state]_[layertext]"
|
|
else
|
|
extra2_accessory_overlay.icon_state = "m_[bodypart]_extra2_[S.icon_state]_[layertext]"
|
|
if(S.center)
|
|
extra2_accessory_overlay = center_image(extra2_accessory_overlay, S.dimension_x, S.dimension_y)
|
|
|
|
switch(S.extra2_color_src) //change the color of the extra overlay
|
|
if(MUTCOLORS)
|
|
if(fixed_mut_color)
|
|
extra2_accessory_overlay.color = "#[fixed_mut_color]"
|
|
else
|
|
extra2_accessory_overlay.color = "#[H.dna.features["mcolor"]]"
|
|
if(MUTCOLORS2)
|
|
if(fixed_mut_color2)
|
|
extra2_accessory_overlay.color = "#[fixed_mut_color2]"
|
|
else
|
|
extra2_accessory_overlay.color = "#[H.dna.features["mcolor2"]]"
|
|
if(MUTCOLORS3)
|
|
if(fixed_mut_color3)
|
|
extra2_accessory_overlay.color = "#[fixed_mut_color3]"
|
|
else
|
|
extra2_accessory_overlay.color = "#[H.dna.features["mcolor3"]]"
|
|
if(HAIR)
|
|
if(hair_color == "mutcolor3")
|
|
extra2_accessory_overlay.color = "#[H.dna.features["mcolor"]]"
|
|
else
|
|
extra2_accessory_overlay.color = "#[H.hair_color]"
|
|
if(HORNCOLOR)
|
|
extra2_accessory_overlay.color = "#[H.dna.features["horns_color"]]"
|
|
if(WINGCOLOR)
|
|
extra2_accessory_overlay.color = "#[H.dna.features["wings_color"]]"
|
|
|
|
if(OFFSET_MUTPARTS in H.dna.species.offset_features)
|
|
extra2_accessory_overlay.pixel_x += H.dna.species.offset_features[OFFSET_MUTPARTS][1]
|
|
extra2_accessory_overlay.pixel_y += H.dna.species.offset_features[OFFSET_MUTPARTS][2]
|
|
|
|
standing += extra2_accessory_overlay
|
|
|
|
|
|
H.overlays_standing[layernum] = standing
|
|
|
|
H.apply_overlay(BODY_BEHIND_LAYER)
|
|
H.apply_overlay(BODY_ADJ_LAYER)
|
|
H.apply_overlay(BODY_ADJ_UPPER_LAYER)
|
|
H.apply_overlay(BODY_FRONT_LAYER)
|
|
|
|
|
|
/*
|
|
* Equip the outfit required for life. Replaces items currently worn.
|
|
*/
|
|
/datum/species/proc/give_important_for_life(mob/living/carbon/human/human_to_equip)
|
|
if(!outfit_important_for_life)
|
|
return
|
|
outfit_important_for_life= new()
|
|
outfit_important_for_life.equip(human_to_equip)
|
|
|
|
/* TODO: Snowflake trail marks
|
|
// Impliments different trails for species depending on if they're wearing shoes.
|
|
/datum/species/proc/get_move_trail(var/mob/living/carbon/human/H)
|
|
if(H.lying)
|
|
return /obj/effect/decal/cleanable/blood/footprints/tracks/body
|
|
if(H.shoes || (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)))
|
|
var/obj/item/clothing/shoes/shoes = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) ? H.wear_suit : H.shoes // suits take priority over shoes
|
|
return shoes.move_trail
|
|
else
|
|
return move_trail */
|
|
|
|
/datum/species/proc/spec_life(mob/living/carbon/human/H)
|
|
if(HAS_TRAIT(H, TRAIT_NOBREATH))
|
|
H.setOxyLoss(0)
|
|
H.losebreath = 0
|
|
|
|
var/takes_crit_damage = !HAS_TRAIT(H, 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(!is_species(H, /datum/species/lizard/ashwalker))
|
|
H.update_inv_shoes()
|
|
else
|
|
return FALSE
|
|
return equip_delay_self_check(I, H, bypass_equip_delay_self)
|
|
if(SLOT_BELT)
|
|
if(H.belt)
|
|
return FALSE
|
|
if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED))
|
|
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
|
|
if(!CHECK_BITFIELD(I.item_flags, NO_UNIFORM_REQUIRED))
|
|
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(HAS_TRAIT(I, TRAIT_NODROP)) //Pockets aren't visible, so you can't move TRAIT_NODROP 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(HAS_TRAIT(I, TRAIT_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(HAS_TRAIT(I, TRAIT_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.type == exotic_blood && !istype(exotic_blood, /datum/reagent/blood))
|
|
H.blood_volume = min(H.blood_volume + round(chem.volume, 0.1), BLOOD_VOLUME_MAXIMUM)
|
|
H.reagents.del_reagent(chem.type)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/species/proc/check_weakness(obj/item, mob/living/attacker)
|
|
return FALSE
|
|
|
|
////////
|
|
//LIFE//
|
|
////////
|
|
|
|
/datum/species/proc/handle_digestion(mob/living/carbon/human/H)
|
|
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
|
|
return //hunger is for BABIES
|
|
|
|
//The fucking TRAIT_FAT mutation is the dumbest shit ever. It makes the code so difficult to work with
|
|
if(HAS_TRAIT(H, TRAIT_FAT))//I share your pain, past coder.
|
|
if(H.overeatduration < 100)
|
|
to_chat(H, "<span class='notice'>You feel fit again!</span>")
|
|
REMOVE_TRAIT(H, TRAIT_FAT, OBESITY)
|
|
H.remove_movespeed_modifier(/datum/movespeed_modifier/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>")
|
|
ADD_TRAIT(H, TRAIT_FAT, OBESITY)
|
|
H.add_movespeed_modifier(/datum/movespeed_modifier/obesity)
|
|
H.update_inv_w_uniform()
|
|
H.update_inv_wear_suit()
|
|
|
|
// nutrition decrease and satiety
|
|
if (H.nutrition > 0 && H.stat != DEAD && !HAS_TRAIT(H, TRAIT_NOHUNGER))
|
|
// THEY HUNGER
|
|
var/hunger_rate = HUNGER_FACTOR
|
|
var/datum/component/mood/mood = H.GetComponent(/datum/component/mood)
|
|
if(mood && mood.sanity > SANITY_DISTURBED)
|
|
hunger_rate *= max(0.5, 1 - 0.002 * mood.sanity) //0.85 to 0.75
|
|
|
|
// Whether we cap off our satiety or move it towards 0
|
|
if(H.satiety > MAX_SATIETY)
|
|
H.satiety = MAX_SATIETY
|
|
else if(H.satiety > 0)
|
|
H.satiety--
|
|
else if(H.satiety < -MAX_SATIETY)
|
|
H.satiety = -MAX_SATIETY
|
|
else 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.adjust_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 && !HAS_TRAIT(H, 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
|
|
|
|
//Hunger slowdown for if mood isn't enabled
|
|
if(CONFIG_GET(flag/disable_human_mood))
|
|
if(!HAS_TRAIT(H, TRAIT_NOHUNGER))
|
|
var/hungry = (500 - H.nutrition) / 5 //So overeat would be 100 and default level would be 80
|
|
if(hungry >= 70)
|
|
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/hunger, multiplicative_slowdown = (hungry / 50))
|
|
else
|
|
H.remove_movespeed_modifier(/datum/movespeed_modifier/hunger)
|
|
|
|
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)
|
|
if(0 to NUTRITION_LEVEL_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(HAS_TRAIT(H, TRAIT_RADIMMUNE))
|
|
radiation = 0
|
|
return TRUE
|
|
|
|
if(radiation > RAD_MOB_KNOCKDOWN && prob(RAD_MOB_KNOCKDOWN_PROB))
|
|
if(CHECK_MOBILITY(H, MOBILITY_STAND))
|
|
H.emote("collapse")
|
|
H.DefaultCombatKnockdown(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.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
|
|
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()
|
|
|
|
//////////////////
|
|
// 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 && !HAS_TRAIT(target, TRAIT_FAKEDEATH))
|
|
target.help_shake_act(user)
|
|
if(target != user)
|
|
log_combat(user, target, "shaked")
|
|
return 1
|
|
else
|
|
var/we_breathe = !HAS_TRAIT(user, 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_martial_melee_block())
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s grab attempt!</span>", target = user, \
|
|
target_message = "<span class='warning'>[target] blocks your 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(!attacker_style && HAS_TRAIT(user, TRAIT_PACIFISM))
|
|
to_chat(user, "<span class='warning'>You don't want to harm [target]!</span>")
|
|
return FALSE
|
|
if(IS_STAMCRIT(user)) //CITADEL CHANGE - makes it impossible to punch while in stamina softcrit
|
|
to_chat(user, "<span class='warning'>You're too exhausted.</span>") //CITADEL CHANGE - ditto
|
|
return FALSE //CITADEL CHANGE - ditto
|
|
if(target.check_martial_melee_block())
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s attack!</span>", target = user, \
|
|
target_message = "<span class='warning'>[target] blocks your attack!</span>")
|
|
return FALSE
|
|
|
|
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes punching cause staminaloss but funny martial artist types get a discount
|
|
user.adjustStaminaLossBuffered(1.5)
|
|
else
|
|
user.adjustStaminaLossBuffered(3.5)
|
|
|
|
if(attacker_style && attacker_style.harm_act(user,target))
|
|
return TRUE
|
|
else
|
|
|
|
var/atk_verb = user.dna.species.attack_verb
|
|
if(!(target.mobility_flags & MOBILITY_STAND))
|
|
atk_verb = ATTACK_EFFECT_KICK
|
|
|
|
switch(atk_verb)
|
|
if(ATTACK_EFFECT_KICK)
|
|
user.do_attack_animation(target, ATTACK_EFFECT_KICK)
|
|
if(ATTACK_EFFECT_CLAW)
|
|
user.do_attack_animation(target, ATTACK_EFFECT_CLAW)
|
|
if(ATTACK_EFFECT_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/puncherstam = user.getStaminaLoss()
|
|
var/puncherbrute = user.getBruteLoss()
|
|
var/punchedstam = target.getStaminaLoss()
|
|
var/punchedbrute = target.getBruteLoss()
|
|
|
|
//CITADEL CHANGES - makes resting and disabled combat mode reduce punch damage, makes being out of combat mode result in you taking more damage
|
|
if(!SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
|
damage *= 1.5
|
|
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
|
|
damage *= 0.5
|
|
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
|
damage *= 0.25
|
|
//END OF CITADEL CHANGES
|
|
|
|
var/obj/item/bodypart/affecting = target.get_bodypart(ran_zone(user.zone_selected))
|
|
|
|
var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases
|
|
if(user.dna.species.punchdamagelow)
|
|
if(HAS_TRAIT(user, TRAIT_PUGILIST)) //pugilists have a flat 10% miss chance
|
|
miss_chance = 10
|
|
if(atk_verb == ATTACK_EFFECT_KICK) //kicks never miss (provided your species deals more than 0 damage)
|
|
miss_chance = 0
|
|
else
|
|
miss_chance = min(10 + ((puncherstam + puncherbrute)*0.5), 100) //probability of miss has a base of 10, and modified based on half brute total. Capped at max 100 to prevent weirdness in prob()
|
|
|
|
if(!damage || !affecting || prob(miss_chance))//future-proofing for species that have 0 damage/weird cases where no zone is targeted
|
|
playsound(target.loc, user.dna.species.miss_sound, 25, TRUE, -1)
|
|
target.visible_message("<span class='danger'>[user]'s [atk_verb] misses [target]!</span>", \
|
|
"<span class='danger'>You avoid [user]'s [atk_verb]!</span>", "<span class='hear'>You hear a swoosh!</span>", null, COMBAT_MESSAGE_RANGE, null, \
|
|
user, "<span class='warning'>Your [atk_verb] misses [target]!</span>")
|
|
log_combat(user, target, "attempted to punch")
|
|
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] [atk_verb]s [target]!</span>", \
|
|
"<span class='userdanger'>[user] [atk_verb]s you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
|
|
user, "<span class='danger'>You [atk_verb] [target]!</span>")
|
|
|
|
target.lastattacker = user.real_name
|
|
target.lastattackerckey = user.ckey
|
|
user.dna.species.spec_unarmedattacked(user, target)
|
|
|
|
if(user.limb_destroyer)
|
|
target.dismembering_strike(user, affecting.body_zone)
|
|
|
|
if(atk_verb == ATTACK_EFFECT_KICK)//kicks deal 1.5x raw damage + 0.5x stamina damage
|
|
target.apply_damage(damage*1.5, BRUTE, affecting, armor_block)
|
|
target.apply_damage(damage*0.5, STAMINA, affecting, armor_block)
|
|
log_combat(user, target, "kicked")
|
|
else//other attacks deal full raw damage + 2x in stamina damage
|
|
target.apply_damage(damage, BRUTE, affecting, armor_block)
|
|
target.apply_damage(damage*2, STAMINA, affecting, armor_block)
|
|
log_combat(user, target, "punched")
|
|
|
|
if((target.stat != DEAD) && damage >= user.dna.species.punchstunthreshold)
|
|
if((punchedstam > 50) && prob(punchedstam*0.5)) //If our punch victim has been hit above the threshold, and they have more than 50 stamina damage, roll for stun, probability of 1% per 2 stamina damage
|
|
|
|
target.visible_message("<span class='danger'>[user] knocks [target] down!</span>", \
|
|
"<span class='userdanger'>You're knocked down by [user]!</span>",
|
|
"<span class='hear'>You hear aggressive shuffling followed by a loud thud!</span>", COMBAT_MESSAGE_RANGE, null,
|
|
user, "<span class='danger'>You knock [target] down!</span>")
|
|
|
|
var/knockdown_duration = 40 + (punchedstam + (punchedbrute*0.5))*0.8 - armor_block
|
|
target.DefaultCombatKnockdown(knockdown_duration)
|
|
target.forcesay(GLOB.hit_appends)
|
|
log_combat(user, target, "got a stun punch with their previous punch")
|
|
|
|
if(HAS_TRAIT(user, TRAIT_KI_VAMPIRE) && !HAS_TRAIT(target, TRAIT_NOBREATH) && (punchedbrute < 100)) //If we're a ki vampire we also sap them of lifeforce, but only if they're not too beat up. Also living organics only.
|
|
user.adjustBruteLoss(-5)
|
|
user.adjustFireLoss(-5)
|
|
user.adjustStaminaLoss(-20)
|
|
|
|
target.adjustCloneLoss(10)
|
|
target.adjustBruteLoss(10)
|
|
|
|
else if(!(target.mobility_flags & MOBILITY_STAND))
|
|
target.forcesay(GLOB.hit_appends)
|
|
|
|
/datum/species/proc/spec_unarmedattacked(mob/living/carbon/human/user, mob/living/carbon/human/target)
|
|
return
|
|
|
|
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
// CITADEL EDIT slap mouthy gits and booty
|
|
var/aim_for_mouth = user.zone_selected == "mouth"
|
|
var/target_on_help = target.a_intent == INTENT_HELP
|
|
var/target_aiming_for_mouth = target.zone_selected == "mouth"
|
|
var/target_restrained = target.restrained()
|
|
var/same_dir = (target.dir & user.dir)
|
|
var/aim_for_groin = user.zone_selected == "groin"
|
|
var/target_aiming_for_groin = target.zone_selected == "groin"
|
|
|
|
if(target.check_martial_melee_block()) //END EDIT
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s disarm attempt!</span>", target = user, \
|
|
target_message = "<span class='warning'>[target] blocks your disarm attempt!</span>")
|
|
return FALSE
|
|
if(IS_STAMCRIT(user))
|
|
to_chat(user, "<span class='warning'>You're too exhausted!</span>")
|
|
return FALSE
|
|
|
|
else if(aim_for_mouth && ( target_on_help || target_restrained || target_aiming_for_mouth))
|
|
playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1)
|
|
|
|
target.visible_message(\
|
|
"<span class='danger'>\The [user] slaps [user == target ? "[user.p_them()]self" : "\the [target]"] in the face!</span>",\
|
|
"<span class='notice'>[user] slaps you in the face! </span>",\
|
|
"You hear a slap.", target = user, target_message = "<span class='notice'>You slap [user == target ? "yourself" : "\the [target]"] in the face! </span>")
|
|
user.do_attack_animation(target, ATTACK_EFFECT_FACE_SLAP)
|
|
user.adjustStaminaLossBuffered(3)
|
|
if (!HAS_TRAIT(target, TRAIT_PERMABONER))
|
|
stop_wagging_tail(target)
|
|
return FALSE
|
|
else if(aim_for_groin && (target == user || target.lying || same_dir) && (target_on_help || target_restrained || target_aiming_for_groin))
|
|
if(target.client?.prefs.cit_toggles & NO_ASS_SLAP)
|
|
to_chat(user,"A force stays your hand, preventing you from slapping \the [target]'s ass!")
|
|
return FALSE
|
|
user.do_attack_animation(target, ATTACK_EFFECT_ASS_SLAP)
|
|
user.adjustStaminaLossBuffered(3)
|
|
target.adjust_arousal(20,maso = TRUE)
|
|
if (ishuman(target) && HAS_TRAIT(target, TRAIT_MASO) && target.has_dna() && prob(10))
|
|
target.mob_climax(forced_climax=TRUE)
|
|
if (!HAS_TRAIT(target, TRAIT_PERMABONER))
|
|
stop_wagging_tail(target)
|
|
playsound(target.loc, 'sound/weapons/slap.ogg', 50, 1, -1)
|
|
target.visible_message(\
|
|
"<span class='danger'>\The [user] slaps [user == target ? "[user.p_their()] own" : "\the [target]'s"] ass!</span>",\
|
|
"<span class='notice'>[user] slaps your ass! </span>",\
|
|
"You hear a slap.", target = user, target_message = "<span class='notice'>You slap [user == target ? "your own" : "\the [target]'s"] ass! </span>")
|
|
|
|
return FALSE
|
|
|
|
else
|
|
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
|
|
|
|
if(HAS_TRAIT(user, TRAIT_PUGILIST))//CITADEL CHANGE - makes disarmspam cause staminaloss, pugilists can do it almost effortlessly
|
|
user.adjustStaminaLossBuffered(1)
|
|
else
|
|
user.adjustStaminaLossBuffered(3)
|
|
|
|
if(attacker_style && attacker_style.disarm_act(user,target))
|
|
return TRUE
|
|
|
|
if(target.w_uniform)
|
|
target.w_uniform.add_fingerprint(user)
|
|
//var/randomized_zone = ran_zone(user.zone_selected) CIT CHANGE - comments out to prevent compiling errors
|
|
SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected)
|
|
if(target.pulling == user)
|
|
target.visible_message("<span class='warning'>[user] wrestles out of [target]'s grip!</span>", \
|
|
"<span class='warning'>[user] wrestles out of your grip!</span>", target = user, \
|
|
target_message = "<span class='warning'>You wrestle out of [target]'s grip!</span>")
|
|
target.stop_pulling()
|
|
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
|
log_combat(user, target, "disarmed out of grab from")
|
|
return
|
|
var/randn = rand(1, 100)
|
|
if(SEND_SIGNAL(target, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) // CITADEL CHANGE
|
|
randn += -10 //CITADEL CHANGE - being out of combat mode makes it easier for you to get disarmed
|
|
if(!CHECK_MOBILITY(user, MOBILITY_STAND)) //CITADEL CHANGE
|
|
randn += 100 //CITADEL CHANGE - No kosher disarming if you're resting
|
|
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE)) //CITADEL CHANGE
|
|
randn += 25 //CITADEL CHANGE - Makes it harder to disarm outside of combat mode
|
|
if(user.pulling == target)
|
|
randn -= 20 //If you have the time to get someone in a grab, you should have a greater chance at snatching the thing in their hand. Will be made completely obsolete by the grab rework but i've got a poor track record for releasing big projects on time so w/e i guess
|
|
if(HAS_TRAIT(user, TRAIT_PUGILIST))
|
|
randn -= 25 //if you are a pugilist, you're slapping that item from them pretty reliably
|
|
if(HAS_TRAIT(target, TRAIT_PUGILIST))
|
|
randn += 25 //meanwhile, pugilists are less likely to get disarmed
|
|
|
|
if(randn <= 35)//CIT CHANGE - changes this back to a 35% chance to accomodate for the above being commented out in favor of right-click pushing
|
|
var/obj/item/I = null
|
|
if(target.pulling)
|
|
target.visible_message("<span class='warning'>[user] has broken [target]'s grip on [target.pulling]!</span>", \
|
|
"<span class='warning'>[user] has broken your grip on [target.pulling]!</span>", target = user, \
|
|
target_message = "<span class='warning'>You have 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 you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
|
|
user, "<span class='danger'>You have disarmed [target]!</span>")
|
|
else
|
|
I = null
|
|
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
|
log_combat(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, null, \
|
|
user, "<span class='danger'>You attempted to disarm [target]!</span>")
|
|
log_combat(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(attacker_style?.pacifism_check && HAS_TRAIT(M, TRAIT_PACIFISM)) // most martial arts are quite harmful, alas.
|
|
attacker_style = null
|
|
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)
|
|
var/totitemdamage = H.pre_attacked_by(I, user)
|
|
// Allows you to put in item-specific reactions based on species
|
|
if(user != H)
|
|
var/list/block_return = list()
|
|
if(H.mob_run_block(I, totitemdamage, "the [I.name]", ATTACK_TYPE_MELEE, I.armour_penetration, user, affecting.body_zone, block_return) & BLOCK_SUCCESS)
|
|
return 0
|
|
totitemdamage = block_calculate_resultant_damage(totitemdamage, block_return)
|
|
if(H.check_martial_melee_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(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage
|
|
|
|
H.send_item_attack_message(I, user, hit_area)
|
|
|
|
I.do_stagger_action(H, user, totitemdamage)
|
|
|
|
if(!totitemdamage)
|
|
return 0 //item force is zero
|
|
|
|
//dismemberment
|
|
var/probability = I.get_dismemberment_chance(affecting)
|
|
if(prob(probability) || (HAS_TRAIT(H, 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(!I.get_sharpness() && armor_block < 50)
|
|
if(prob(I.force))
|
|
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20)
|
|
if(H.stat == CONSCIOUS)
|
|
H.visible_message("<span class='danger'>[H] has been knocked senseless!</span>", \
|
|
"<span class='userdanger'>You have been knocked senseless!</span>")
|
|
H.confused = max(H.confused, 20)
|
|
H.adjust_blurriness(10)
|
|
if(prob(10))
|
|
H.gain_trauma(/datum/brain_trauma/mild/concussion)
|
|
else
|
|
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, I.force * 0.2)
|
|
|
|
if(H.stat == CONSCIOUS && H != user && prob(I.force + ((100 - H.health) * 0.5))) // rev deconversion through blunt trauma.
|
|
var/datum/antagonist/rev/rev = H.mind.has_antag_datum(/datum/antagonist/rev)
|
|
var/datum/antagonist/gang/gang = H.mind.has_antag_datum(/datum/antagonist/gang && !/datum/antagonist/gang/boss)
|
|
if(rev)
|
|
rev.remove_revolutionary(FALSE, user)
|
|
if(gang)
|
|
H.mind.remove_antag_datum(/datum/antagonist/gang)
|
|
|
|
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 && !I.get_sharpness() && 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/alt_spec_attack_hand(mob/living/carbon/human/M, mob/living/carbon/human/H, datum/martial_art/attacker_style)
|
|
if(!istype(M))
|
|
return TRUE
|
|
CHECK_DNA_AND_SPECIES(M)
|
|
CHECK_DNA_AND_SPECIES(H)
|
|
|
|
if(!istype(M)) //sanity check for drones.
|
|
return TRUE
|
|
if(M.mind)
|
|
attacker_style = M.mind.martial_art
|
|
if((M != H) && M.a_intent != INTENT_HELP && (H.mob_run_block(M, 0, "[M]", ATTACK_TYPE_UNARMED, 0, M, M.zone_selected, null) & BLOCK_SUCCESS))
|
|
log_combat(M, H, "attempted to touch")
|
|
H.visible_message("<span class='warning'>[M] attempted to touch [H]!</span>", \
|
|
"<span class='warning'>[M] attempted to touch you!</span>", target = M, \
|
|
target_message = "<span class='warning'>You attempted to touch [H]!</span>")
|
|
return TRUE
|
|
switch(M.a_intent)
|
|
if(INTENT_HELP)
|
|
if(M == H)
|
|
althelp(M, H, attacker_style)
|
|
return TRUE
|
|
return FALSE
|
|
if(INTENT_DISARM)
|
|
altdisarm(M, H, attacker_style)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/datum/species/proc/althelp(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
if(user == target && istype(user))
|
|
if(IS_STAMCRIT(user))
|
|
to_chat(user, "<span class='warning'>You're too exhausted for that.</span>")
|
|
return
|
|
if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_INACTIVE))
|
|
to_chat(user, "<span class='warning'>You need combat mode to be active to that!</span>")
|
|
return
|
|
if(user.IsKnockdown() || user.IsParalyzed() || user.IsStun())
|
|
to_chat(user, "<span class='warning'>You can't seem to force yourself up right now!</span>")
|
|
return
|
|
if(CHECK_MOBILITY(user, MOBILITY_STAND))
|
|
to_chat(user, "<span class='notice'>You can only force yourself up if you're on the ground.</span>")
|
|
return
|
|
user.visible_message("<span class='notice'>[user] forces [p_them()]self up to [p_their()] feet!</span>", "<span class='notice'>You force yourself up to your feet!</span>")
|
|
user.set_resting(FALSE, TRUE)
|
|
user.adjustStaminaLossBuffered(user.stambuffer) //Rewards good stamina management by making it easier to instantly get up from resting
|
|
playsound(user, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
|
|
|
/datum/species/proc/altdisarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
|
|
if(IS_STAMCRIT(user))
|
|
to_chat(user, "<span class='warning'>You're too exhausted.</span>")
|
|
return FALSE
|
|
if(target.check_martial_melee_block())
|
|
target.visible_message("<span class='warning'>[target] blocks [user]'s shoving attempt!</span>", \
|
|
"<span class='warning'>You block [user]'s shoving attempt!</span>", target = user, \
|
|
target_message = "<span class='warning'>[target] blocks your shoving attempt!</span>")
|
|
return FALSE
|
|
if(attacker_style && attacker_style.disarm_act(user,target))
|
|
return TRUE
|
|
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
|
|
return FALSE
|
|
else
|
|
if(user == target)
|
|
return
|
|
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
|
|
user.adjustStaminaLossBuffered(4)
|
|
playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
|
|
|
if(target.w_uniform)
|
|
target.w_uniform.add_fingerprint(user)
|
|
SEND_SIGNAL(target, COMSIG_HUMAN_DISARM_HIT, user, user.zone_selected)
|
|
|
|
if(CHECK_MOBILITY(target, MOBILITY_STAND))
|
|
target.adjustStaminaLoss(5)
|
|
|
|
if(target.is_shove_knockdown_blocked())
|
|
return
|
|
|
|
var/turf/target_oldturf = target.loc
|
|
var/shove_dir = get_dir(user.loc, target_oldturf)
|
|
var/turf/target_shove_turf = get_step(target.loc, shove_dir)
|
|
var/mob/living/carbon/human/target_collateral_human
|
|
var/shove_blocked = FALSE //Used to check if a shove is blocked so that if it is knockdown logic can be applied
|
|
|
|
//Thank you based whoneedsspace
|
|
target_collateral_human = locate(/mob/living/carbon/human) in target_shove_turf.contents
|
|
if(target_collateral_human && CHECK_MOBILITY(target_collateral_human, MOBILITY_STAND))
|
|
shove_blocked = TRUE
|
|
else
|
|
target_collateral_human = null
|
|
target.Move(target_shove_turf, shove_dir)
|
|
if(get_turf(target) == target_oldturf)
|
|
shove_blocked = TRUE
|
|
|
|
var/append_message = ""
|
|
if(shove_blocked && !target.buckled)
|
|
var/directional_blocked = !target.Adjacent(target_shove_turf)
|
|
var/targetatrest = !CHECK_MOBILITY(target, MOBILITY_STAND)
|
|
if((directional_blocked || !(target_collateral_human || target_shove_turf.shove_act(target, user))) && !targetatrest)
|
|
target.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_SOLID)
|
|
target.visible_message("<span class='danger'>[user.name] shoves [target.name], knocking them down!</span>",
|
|
"<span class='danger'>[user.name] shoves you, knocking you down!</span>", null, COMBAT_MESSAGE_RANGE, null,
|
|
user, "<span class='danger'>You shove [target.name], knocking them down!</span>")
|
|
log_combat(user, target, "shoved", "knocking them down")
|
|
else if(target_collateral_human && !targetatrest)
|
|
target.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_HUMAN)
|
|
target_collateral_human.DefaultCombatKnockdown(SHOVE_KNOCKDOWN_COLLATERAL)
|
|
target.visible_message("<span class='danger'>[user.name] shoves [target.name] into [target_collateral_human.name]!</span>",
|
|
"<span class='danger'>[user.name] shoves you into [target_collateral_human.name]!</span>", null, COMBAT_MESSAGE_RANGE, null,
|
|
user, "<span class='danger'>You shove [target.name] into [target_collateral_human.name]!</span>")
|
|
append_message += ", into [target_collateral_human.name]"
|
|
|
|
else
|
|
target.visible_message("<span class='danger'>[user.name] shoves [target.name]!</span>",
|
|
"<span class='danger'>[user.name] shoves you!</span>", null, COMBAT_MESSAGE_RANGE, null,
|
|
user, "<span class='danger'>You shove [target.name]!</span>")
|
|
var/obj/item/target_held_item = target.get_active_held_item()
|
|
if(!is_type_in_typecache(target_held_item, GLOB.shove_disarming_types))
|
|
target_held_item = null
|
|
if(!target.has_movespeed_modifier(/datum/movespeed_modifier/shove))
|
|
target.add_movespeed_modifier(/datum/movespeed_modifier/shove)
|
|
if(target_held_item)
|
|
if(!HAS_TRAIT(target_held_item, TRAIT_NODROP))
|
|
target.visible_message("<span class='danger'>[target.name]'s grip on \the [target_held_item] loosens!</span>",
|
|
"<span class='danger'>Your grip on \the [target_held_item] loosens!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
append_message += ", loosening their grip on [target_held_item]"
|
|
else
|
|
append_message += ", but couldn't loose their grip on [target_held_item]"
|
|
addtimer(CALLBACK(target, /mob/living/carbon/human/proc/clear_shove_slowdown), SHOVE_SLOWDOWN_LENGTH)
|
|
else if(target_held_item)
|
|
if(target.dropItemToGround(target_held_item))
|
|
target.visible_message("<span class='danger'>[target.name] drops \the [target_held_item]!!</span>",
|
|
"<span class='danger'>You drop \the [target_held_item]!!</span>", null, COMBAT_MESSAGE_RANGE)
|
|
append_message += ", causing them to drop [target_held_item]"
|
|
log_combat(user, target, "shoved", append_message)
|
|
|
|
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE)
|
|
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
|
|
var/hit_percent = (100-(blocked+armor))/100
|
|
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
|
|
if(!forced && hit_percent <= 0)
|
|
return 0
|
|
|
|
var/obj/item/bodypart/BP = null
|
|
if(isbodypart(def_zone))
|
|
if(damagetype == STAMINA && istype(def_zone, /obj/item/bodypart/head))
|
|
BP = H.get_bodypart(check_zone(BODY_ZONE_CHEST))
|
|
else
|
|
BP = def_zone
|
|
else
|
|
if(!def_zone)
|
|
def_zone = ran_zone(def_zone)
|
|
if(damagetype == STAMINA && def_zone == BODY_ZONE_HEAD)
|
|
def_zone = BODY_ZONE_CHEST
|
|
BP = H.get_bodypart(check_zone(def_zone))
|
|
|
|
if(!BP)
|
|
BP = H.bodyparts[1]
|
|
|
|
switch(damagetype)
|
|
if(BRUTE)
|
|
H.damageoverlaytemp = 20
|
|
var/damage_amount = forced ? damage : damage * hit_percent * brutemod * H.physiology.brute_mod
|
|
if(BP)
|
|
if(damage > 0 ? BP.receive_damage(damage_amount, 0) : BP.heal_damage(abs(damage_amount), 0))
|
|
H.update_damage_overlays()
|
|
if(HAS_TRAIT(H, TRAIT_MASO) && prob(damage_amount))
|
|
H.mob_climax(forced_climax=TRUE)
|
|
|
|
else//no bodypart, we deal damage with a more general method.
|
|
H.adjustBruteLoss(damage_amount)
|
|
if(BURN)
|
|
H.damageoverlaytemp = 20
|
|
var/damage_amount = forced ? damage : damage * hit_percent * burnmod * H.physiology.burn_mod
|
|
if(BP)
|
|
if(damage > 0 ? BP.receive_damage(0, damage_amount) : BP.heal_damage(0, abs(damage_amount)))
|
|
H.update_damage_overlays()
|
|
else
|
|
H.adjustFireLoss(damage_amount)
|
|
if(TOX)
|
|
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.tox_mod
|
|
H.adjustToxLoss(damage_amount)
|
|
if(OXY)
|
|
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.oxy_mod
|
|
H.adjustOxyLoss(damage_amount)
|
|
if(CLONE)
|
|
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.clone_mod
|
|
H.adjustCloneLoss(damage_amount)
|
|
if(STAMINA)
|
|
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.stamina_mod
|
|
if(BP)
|
|
if(damage > 0 ? BP.receive_damage(0, 0, damage_amount) : BP.heal_damage(0, 0, abs(damage * hit_percent * H.physiology.stamina_mod), only_robotic = FALSE, only_organic = FALSE))
|
|
H.update_stamina()
|
|
else
|
|
H.adjustStaminaLoss(damage_amount)
|
|
if(BRAIN)
|
|
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.brain_mod
|
|
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, damage_amount)
|
|
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
|
|
|
|
/////////////
|
|
//BREATHING//
|
|
/////////////
|
|
|
|
/datum/species/proc/breathe(mob/living/carbon/human/H)
|
|
if(HAS_TRAIT(H, 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_thermal_protection(loc_temp, TRUE) //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_thermal_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))
|
|
switch((loc_temp - H.bodytemperature)*thermal_protection)
|
|
if(-INFINITY to -50)
|
|
H.throw_alert("temp", /obj/screen/alert/cold, 3)
|
|
if(-50 to -35)
|
|
H.throw_alert("temp", /obj/screen/alert/cold, 2)
|
|
if(-35 to -20)
|
|
H.throw_alert("temp", /obj/screen/alert/cold, 1)
|
|
if(-20 to 0) //This is the sweet spot where air is considered normal
|
|
H.clear_alert("temp")
|
|
if(0 to 15) //When the air around you matches your body's temperature, you'll start to feel warm.
|
|
H.throw_alert("temp", /obj/screen/alert/hot, 1)
|
|
if(15 to 30)
|
|
H.throw_alert("temp", /obj/screen/alert/hot, 2)
|
|
if(30 to INFINITY)
|
|
H.throw_alert("temp", /obj/screen/alert/hot, 3)
|
|
|
|
// +/- 50 degrees from 310K is the 'safe' zone, where no damage is dealt.
|
|
if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !HAS_TRAIT(H, 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)
|
|
|
|
H.remove_movespeed_modifier(/datum/movespeed_modifier/cold)
|
|
|
|
var/burn_damage
|
|
var/firemodifier = H.fire_stacks / 50
|
|
if (H.on_fire)
|
|
burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0)
|
|
else
|
|
firemodifier = min(firemodifier, 0)
|
|
burn_damage = max(log(2-firemodifier,(H.bodytemperature-BODYTEMP_NORMAL))-5,0) // this can go below 5 at log 2.5
|
|
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 && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
|
|
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
|
|
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "cold", /datum/mood_event/cold)
|
|
//Apply cold slowdown
|
|
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
|
|
switch(H.bodytemperature)
|
|
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
|
|
H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod*H.physiology.cold_mod, BURN)
|
|
if(120 to 200)
|
|
H.apply_damage(COLD_DAMAGE_LEVEL_2*coldmod*H.physiology.cold_mod, BURN)
|
|
else
|
|
H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod*H.physiology.cold_mod, BURN)
|
|
|
|
else
|
|
H.remove_movespeed_modifier(/datum/movespeed_modifier/cold)
|
|
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(!HAS_TRAIT(H, 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(HAS_TRAIT(H, 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(HAS_TRAIT(H, 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.easy_thermal_protection()
|
|
|
|
if(thermal_protection >= FIRE_IMMUNITY_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(HAS_TRAIT(H, 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
|
|
|
|
////////////////
|
|
//Tail Wagging//
|
|
////////////////
|
|
|
|
/datum/species/proc/can_wag_tail(mob/living/carbon/human/H)
|
|
return FALSE
|
|
|
|
/datum/species/proc/is_wagging_tail(mob/living/carbon/human/H)
|
|
return FALSE
|
|
|
|
/datum/species/proc/start_wagging_tail(mob/living/carbon/human/H)
|
|
|
|
/datum/species/proc/stop_wagging_tail(mob/living/carbon/human/H)
|