Files
CHOMPStation2/code/modules/mob/living/carbon/human/human_defense.dm
CHOMPStation2StaffMirrorBot d7cd22d2d0 [MIRROR] Forensics Datum (#11015)
Co-authored-by: Will <7099514+Willburd@users.noreply.github.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
2025-06-06 16:18:13 +02:00

702 lines
27 KiB
Plaintext

/*
Contains most of the procs that are called when a mob is attacked by something
bullet_act
ex_act
meteor_act
emp_act
*/
/mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone)
def_zone = check_zone(def_zone)
if(!has_organ(def_zone))
return PROJECTILE_FORCE_MISS //if they don't have the organ in question then the projectile just passes by.
var/obj/item/organ/external/organ = get_organ()
//Shields
var/shield_check = check_shields(P.damage, P, null, def_zone, "the [P.name]")
if(shield_check) // If the block roll succeeded, this is true.
if(shield_check < 0) // The shield did something weird and the bullet needs to keep doing things (e.g. it was reflected).
return shield_check // Likely equal to PROJECTILE_FORCE_MISS or PROJECTILE_CONTINUE.
else // Otherwise we blocked normally and stopped all the damage.
return 0
if(!P.nodamage)
organ.add_autopsy_data("[P.name]", P.damage)
// Tell clothing we're wearing that it got hit by a bullet/laser/etc
var/list/clothing = get_clothing_list_organ(organ)
for(var/obj/item/clothing/C in clothing)
C.clothing_impact(P, P.damage)
//Shrapnel
if(P.can_embed())
var/armor = getarmor_organ(organ, "bullet")
if(!prob(armor/2)) //Even if the armor doesn't stop the bullet from hurting you, it might stop it from embedding.
var/hit_embed_chance = P.embed_chance + (P.damage - armor) //More damage equals more chance to embed
//Modifiers can make bullets less likely to embed! These are the normal modifiers and shouldn't be related to energy stuff, but they can be anyways!
for(var/datum/modifier/M in modifiers)
if(!isnull(M.incoming_damage_percent))
if(M.energy_based)
M.energy_source.use(M.energy_cost) //We use energy_cost here for special effects, such as embedding.
hit_embed_chance = hit_embed_chance*M.incoming_damage_percent
if(P.damage_type == BRUTE && (!isnull(M.incoming_brute_damage_percent)))
if(M.energy_based)
M.energy_source.use(M.energy_cost)
hit_embed_chance = hit_embed_chance*M.incoming_brute_damage_percent
if(prob(max(hit_embed_chance, 0)))
var/obj/item/material/shard/shrapnel/SP = new()
SP.name = (P.name != "shrapnel")? "[P.name] shrapnel" : "shrapnel"
SP.desc = "[SP.desc] It looks like it was fired from [P.shot_from]."
SP.loc = organ
organ.embed(SP)
return (..(P , def_zone))
/mob/living/carbon/human/stun_effect_act(var/stun_amount, var/agony_amount, var/def_zone)
var/obj/item/organ/external/affected = get_organ(check_zone(def_zone))
var/siemens_coeff = get_siemens_coefficient_organ(affected)
if(fire_stacks < 0) // Water makes you more conductive.
siemens_coeff *= 1.5
stun_amount *= siemens_coeff
agony_amount *= siemens_coeff
switch (def_zone)
if(BP_HEAD)
agony_amount *= 1.50
if(BP_L_HAND, BP_R_HAND)
var/c_hand
if (def_zone == BP_L_HAND)
c_hand = l_hand
else
c_hand = r_hand
if(c_hand && (stun_amount || agony_amount > 10))
msg_admin_attack("[key_name(src)] was disarmed by a stun effect")
drop_from_inventory(c_hand)
if(!isbelly(loc)) //VOREStation Add
if (affected.robotic >= ORGAN_ROBOT)
automatic_custom_emote(VISIBLE_MESSAGE, "drops what they were holding, their [affected.name] malfunctioning!", check_stat = TRUE)
else
var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ")
automatic_custom_emote(VISIBLE_MESSAGE, "[affected.organ_can_feel_pain() ? "" : emote_scream] drops what they were holding in their [affected.name]!", check_stat = TRUE)
..(stun_amount, agony_amount, def_zone)
/mob/living/carbon/human/getarmor(var/def_zone, var/type)
var/armorval = 0
var/total = 0
if(def_zone)
if(isorgan(def_zone))
return getarmor_organ(def_zone, type)
var/obj/item/organ/external/affecting = get_organ(def_zone)
if(affecting)
return getarmor_organ(affecting, type)
//If a specific bodypart is targetted, check how that bodypart is protected and return the value.
//If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values
for(var/organ_name in organs_by_name)
if (organ_name in organ_rel_size)
var/obj/item/organ/external/organ = organs_by_name[organ_name]
if(organ)
var/weight = organ_rel_size[organ_name]
armorval += (getarmor_organ(organ, type) * weight)
total += weight
return (armorval/max(total, 1))
//Like getarmor, but the value it returns will be numerical damage reduction
/mob/living/carbon/human/getsoak(var/def_zone, var/type)
var/soakval = 0
var/total = 0
if(def_zone)
if(isorgan(def_zone))
return getsoak_organ(def_zone, type)
var/obj/item/organ/external/affecting = get_organ(def_zone)
if(affecting)
return getsoak_organ(affecting, type)
//If a specific bodypart is targetted, check how that bodypart is protected and return the value.
//If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values
for(var/organ_name in organs_by_name)
if (organ_name in organ_rel_size)
var/obj/item/organ/external/organ = organs_by_name[organ_name]
if(organ)
var/weight = organ_rel_size[organ_name]
soakval += getsoak_organ(organ, type) * weight
total += weight
return (soakval/max(total, 1))
//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ.
/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone)
if (!def_zone)
return 1.0
var/siemens_coefficient = max(species.siemens_coefficient,0)
var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking?
for(var/obj/item/clothing/C in clothing_items)
if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered?
siemens_coefficient *= C.siemens_coefficient
// Modifiers.
for(var/datum/modifier/M as anything in modifiers)
if(!isnull(M.siemens_coefficient))
siemens_coefficient *= M.siemens_coefficient
return siemens_coefficient
// Similar to above but is for the mob's overall protection, being the average of all slots.
/mob/living/carbon/human/proc/get_siemens_coefficient_average()
var/siemens_value = 0
var/total = 0
for(var/organ_name in organs_by_name)
if(organ_name in organ_rel_size)
var/obj/item/organ/external/organ = organs_by_name[organ_name]
if(organ)
var/weight = organ_rel_size[organ_name]
siemens_value += get_siemens_coefficient_organ(organ) * weight
total += weight
if(fire_stacks < 0) // Water makes you more conductive.
siemens_value *= 1.5
return (siemens_value / max(total, 1))
// Returns a number between 0 to 1, with 1 being total protection.
/mob/living/carbon/human/get_shock_protection()
return min(1 - get_siemens_coefficient_average(), 1) // Don't go above 1, but negatives are fine.
// Returns a list of clothing that is currently covering def_zone.
/mob/living/carbon/human/proc/get_clothing_list_organ(var/obj/item/organ/external/def_zone, var/type)
var/list/results = list()
var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes)
for(var/obj/item/clothing/C in clothing_items)
if(istype(C) && (C.body_parts_covered & def_zone.body_part))
results.Add(C)
return results
//this proc returns the armour value for a particular external organ.
/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type)
if(!type || !def_zone)
return 0
var/protection = 0
var/list/protective_gear = def_zone.get_covering_clothing()
for(var/obj/item/clothing/gear in protective_gear)
protection += gear.armor[type]
for(var/datum/modifier/M as anything in modifiers)
var/modifier_armor = LAZYACCESS(M.armor_percent, type)
if(modifier_armor)
protection += modifier_armor
return protection
/mob/living/carbon/human/proc/getsoak_organ(var/obj/item/organ/external/def_zone, var/type)
if(!type || !def_zone)
return 0
var/soaked = 0
var/list/protective_gear = def_zone.get_covering_clothing()
for(var/obj/item/clothing/gear in protective_gear)
soaked += gear.armorsoak[type]
for(var/datum/modifier/M as anything in modifiers)
var/modifier_armor = LAZYACCESS(M.armor_flat, type)
if(modifier_armor)
soaked += modifier_armor
return soaked
// Checked in borer code
/mob/living/carbon/human/proc/check_head_coverage()
var/obj/item/organ/external/H = organs_by_name[BP_HEAD]
var/list/body_parts = H.get_covering_clothing(EYES)
if(LAZYLEN(body_parts))
return 1
return 0
//Used to check if they can be fed food/drinks/pills
/mob/living/carbon/human/proc/check_mouth_coverage()
var/obj/item/organ/external/H = organs_by_name[BP_HEAD]
var/list/protective_gear = H.get_covering_clothing(FACE)
for(var/obj/item/gear in protective_gear)
if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL))
return gear
return null
/mob/living/carbon/human/proc/check_mouth_coverage_survival()
var/obj/item/organ/external/H = organs_by_name[BP_HEAD]
var/list/protective_gear = H.get_covering_clothing(FACE)
for(var/obj/item/gear in protective_gear)
if(istype(gear) && (gear.body_parts_covered & FACE) && !(gear.item_flags & FLEXIBLEMATERIAL) && !(gear.item_flags & ALLOW_SURVIVALFOOD))
return gear
return null
/mob/living/carbon/human/proc/check_shields(var/damage = 0, var/atom/damage_source = null, var/mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
for(var/obj/item/shield in list(l_hand, r_hand, wear_suit, l_ear, r_ear)) //CHOMPEdit - included ears for the headset/event item
if(!shield) continue
. = shield.handle_shield(src, damage, damage_source, attacker, def_zone, attack_text)
if(.) return
return 0
/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone)
if(check_neckgrab_attack(I, user, target_zone))
return null
if(user == src) // Attacking yourself can't miss
return target_zone
var/hit_zone = get_zone_with_miss_chance(target_zone, src, user.get_accuracy_penalty(), attacker = user)
if(!hit_zone)
user.do_attack_animation(src)
playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
visible_message(span_danger("\The [user] misses [src] with \the [I]!"))
return null
if(check_shields(I.force, I, user, target_zone, "the [I.name]"))
return
var/obj/item/organ/external/affecting = get_organ(hit_zone)
if (!affecting || affecting.is_stump())
to_chat(user, span_danger("They are missing that limb!"))
return null
return hit_zone
/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone)
var/obj/item/organ/external/affecting = get_organ(hit_zone)
if(!affecting)
return //should be prevented by attacked_with_item() but for sanity.
visible_message(span_danger("[src] has been [LAZYLEN(I.attack_verb) ? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!"))
var/soaked = get_armor_soak(hit_zone, "melee", I.armor_penetration)
var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].")
standard_weapon_hit_effects(I, user, effective_force, blocked, soaked, hit_zone)
return blocked
/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/soaked, var/hit_zone)
var/obj/item/organ/external/affecting = get_organ(hit_zone)
if(!affecting)
return 0
// Allow clothing to respond to being hit.
// This is done up here so that clothing damage occurs even if fully blocked.
var/list/clothing = get_clothing_list_organ(affecting)
for(var/obj/item/clothing/C in clothing)
C.clothing_impact(I, effective_force)
if(soaked >= round(effective_force*0.8))
effective_force -= round(effective_force*0.8)
// Handle striking to cripple.
if(user.a_intent == I_DISARM)
effective_force *= 0.5 //reduced effective force...
if(!..(I, user, effective_force, blocked, soaked, hit_zone))
return 0
//set the dislocate mult less than the effective force mult so that
//dislocating limbs on disarm is a bit easier than breaking limbs on harm
attack_joint(affecting, I, effective_force, 0.75, blocked, soaked) //...but can dislocate joints
else if(!..())
return 0
if(effective_force > 10 || effective_force >= 5 && prob(33))
forcesay(GLOB.hit_appends) //forcesay checks stat already
if(prob(25 + (effective_force * 2)))
if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS)))
return
if(!(I.flags & NOBLOODY))
I.add_blood(src)
var/bloody = 0
if(prob(33))
bloody = 1
var/turf/location = loc
if(istype(location, /turf/simulated))
location.add_blood(src)
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood
H.bloody_body(src)
H.bloody_hands(src)
if(!stat && !(I.no_random_knockdown))
switch(hit_zone)
if(BP_HEAD)//Harder to score a stun but if you do it lasts a bit longer
if(prob(effective_force))
apply_effect(20, PARALYZE, blocked, soaked)
visible_message(span_danger("\The [src] has been knocked unconscious!"))
if(bloody)//Apply blood
if(wear_mask)
wear_mask.add_blood(src)
update_inv_wear_mask(0)
if(head)
head.add_blood(src)
update_inv_head(0)
if(glasses && prob(33))
glasses.add_blood(src)
update_inv_glasses(0)
if(BP_TORSO)//Easier to score a stun but lasts less time
if(prob(effective_force + 10))
apply_effect(6, WEAKEN, blocked, soaked)
visible_message(span_danger("\The [src] has been knocked down!"))
if(bloody)
bloody_body(src)
return 1
/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked, var/soaked)
if(!organ || (organ.dislocated == 1) || (organ.dislocated == -1) || blocked >= 100) //VOREStation Edit Bugfix
return 0
if(W.damtype != BRUTE)
return 0
if(soaked >= round(effective_force*0.8))
effective_force -= round(effective_force*0.8)
//want the dislocation chance to be such that the limb is expected to dislocate after dealing a fraction of the damage needed to break the limb
var/dislocate_chance = effective_force/(dislocate_mult * organ.min_broken_damage * CONFIG_GET(number/organ_health_multiplier))*100
if(prob(dislocate_chance * (100 - blocked)/100))
visible_message(span_danger("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!"))
organ.dislocate(1)
return 1
return 0
/mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source)
var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting)
if(!affecting || !(affecting.robotic >= ORGAN_ROBOT))
to_chat(user, span_warning("That limb isn't robotic."))
return -1
if(affecting.sabotaged)
to_chat(user, span_warning("[src]'s [affecting.name] is already sabotaged!"))
return -1
to_chat(user, span_notice("You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties."))
affecting.sabotaged = 1
return 1
//this proc handles being hit by a thrown atom
/mob/living/carbon/human/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR)
if(src.is_incorporeal())
return
// if(buckled && buckled == AM)
// return // Don't get hit by the thing we're buckled to.
//VORESTATION EDIT START - Allows for thrown vore!
//Throwing a prey into a pred takes priority. After that it checks to see if the person being thrown is a pred.
// I put more comments here for ease of reading.
if(isliving(AM))
var/mob/living/thrown_mob = AM
if(isanimal(thrown_mob) && !allowmobvore && !thrown_mob.ckey) //Is the thrown_mob an animal and we don't allow mobvore?
return
// PERSON BEING HIT: CAN BE DROP PRED, ALLOWS THROW VORE.
// PERSON BEING THROWN: DEVOURABLE, ALLOWS THROW VORE, CAN BE DROP PREY.
if((can_be_drop_pred && throw_vore && vore_selected) && (thrown_mob.devourable && thrown_mob.throw_vore && thrown_mob.can_be_drop_prey)) //Prey thrown into pred.
vore_selected.nom_mob(thrown_mob) //Eat them!!!
visible_message(span_vwarning("[thrown_mob] is thrown right into [src]'s [lowertext(vore_selected.name)]!"))
if(thrown_mob.loc != vore_selected)
thrown_mob.forceMove(vore_selected) //Double check. Should never happen but...Weirder things have happened!
add_attack_logs(thrown_mob.thrower,src,"Devoured [thrown_mob.name] via throw vore.")
return //We can stop here. We don't need to calculate damage or anything else. They're eaten.
// PERSON BEING HIT: CAN BE DROP PREY, ALLOWS THROW VORE, AND IS DEVOURABLE.
// PERSON BEING THROWN: CAN BE DROP PRED, ALLOWS THROW VORE.
else if((can_be_drop_prey && throw_vore && devourable) && (thrown_mob.can_be_drop_pred && thrown_mob.throw_vore && thrown_mob.vore_selected)) //Pred thrown into prey.
visible_message(span_vwarning("[src] suddenly slips inside of [thrown_mob]'s [lowertext(thrown_mob.vore_selected.name)] as [thrown_mob] flies into them!"))
thrown_mob.vore_selected.nom_mob(src) //Eat them!!!
if(src.loc != thrown_mob.vore_selected)
src.forceMove(thrown_mob.vore_selected) //Double check. Should never happen but...Weirder things have happened!
add_attack_logs(thrown_mob.LAssailant,src,"Was Devoured by [thrown_mob.name] via throw vore.")
return
//VORESTATION EDIT END - Allows for thrown vore!
if(istype(AM,/obj/))
var/obj/O = AM
if(stat != DEAD && istype(O,/obj/item) && trash_catching && vore_selected) //Ported from chompstation
var/obj/item/I = O
if(adminbus_trash || is_type_in_list(I,edible_trash) && I.trash_eatable && !is_type_in_list(I,item_vore_blacklist))
visible_message(span_vwarning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) //CHOMPEdit
I.throwing = 0
I.forceMove(vore_selected)
return
if(in_throw_mode && speed <= THROWFORCE_SPEED_DIVISOR) //empty active hand and we're in throw mode
if(canmove && !restrained() && !src.is_incorporeal())
if(isturf(O.loc))
if(can_catch(O))
put_in_active_hand(O)
visible_message(span_warning("[src] catches [O]!"))
throw_mode_off()
return
var/dtype = O.damtype
var/throw_damage = O.throwforce*(speed/THROWFORCE_SPEED_DIVISOR)
if(species && species.throwforce_absorb_threshold >= throw_damage)
visible_message(span_infoplain(span_bold("\The [O]") + " simply bounces off of [src]'s body!"))
return
var/zone
if (isliving(O.thrower))
var/mob/living/L = O.thrower
zone = check_zone(L.zone_sel.selecting)
else
zone = ran_zone(BP_TORSO,75) //Hits a random part of the body, geared towards the chest
//check if we hit
var/miss_chance = 15
if (O.throw_source)
var/distance = get_dist(O.throw_source, loc)
miss_chance = max(15*(distance-2), 0)
zone = get_zone_with_miss_chance(zone, src, miss_chance, ranged_attack=1, attacker = O.thrower)
if(zone && O.thrower != src)
var/shield_check = check_shields(throw_damage, O, thrower, zone, "[O]")
if(shield_check == PROJECTILE_FORCE_MISS)
zone = null
else if(shield_check)
return
if(!zone)
visible_message(span_infoplain(span_bold("\The [O]") + " misses [src] narrowly!"))
return
O.throwing = 0 //it hit, so stop moving
var/obj/item/organ/external/affecting = get_organ(zone)
var/hit_area = affecting.name
src.visible_message(span_filter_warning("[span_red("[src] has been hit in the [hit_area] by [O].")]"))
if(ismob(O.thrower))
add_attack_logs(O.thrower,src,"Hit with thrown [O.name]")
//If the armor absorbs all of the damage, skip the rest of the calculations
var/soaked = get_armor_soak(affecting, "melee", O.armor_penetration)
if(soaked >= throw_damage)
to_chat(src, span_warning("Your armor absorbs the force of [O.name]!"))
return
var/armor = run_armor_check(affecting, "melee", O.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") //I guess "melee" is the best fit here
if(armor < 100)
apply_damage(throw_damage, dtype, zone, armor, soaked, is_sharp(O), has_edge(O), O)
//thrown weapon embedded object code.
if(dtype == BRUTE && istype(O,/obj/item))
var/obj/item/I = O
if (!is_robot_module(I))
var/sharp = is_sharp(I)
var/damage = throw_damage
if (soaked)
damage -= soaked
if (armor)
damage /= armor+1
//blunt objects should really not be embedding in things unless a huge amount of force is involved
var/embed_chance = sharp? damage/I.w_class : damage/(I.w_class*3)
var/embed_threshold = sharp? 5*I.w_class : 15*I.w_class
//Sharp objects will always embed if they do enough damage.
//Thrown sharp objects have some momentum already and have a small chance to embed even if the damage is below the threshold
if((sharp && prob(damage/(10*I.w_class)*100)) || (damage > embed_threshold && prob(embed_chance)))
affecting.embed(I)
// Begin BS12 momentum-transfer code.
var/mass = 1.5
if(istype(O, /obj/item))
var/obj/item/I = O
mass = I.w_class/THROWNOBJ_KNOCKBACK_DIVISOR
var/momentum = speed*mass
if(O.throw_source && momentum >= THROWNOBJ_KNOCKBACK_SPEED && !buckled)
var/dir = get_dir(O.throw_source, src)
visible_message(span_filter_warning("[span_red("[src] staggers under the impact!")]"),span_filter_warning("[span_red("You stagger under the impact!")]"))
src.throw_at(get_edge_target_turf(src,dir),1,momentum)
if(!O || !src) return
if(O.loc == src && O.sharp) //Projectile is embedded and suitable for pinning.
var/turf/T = near_wall(dir,2)
if(T)
src.loc = T
visible_message(span_warning("[src] is pinned to the wall by [O]!"),span_warning("You are pinned to the wall by [O]!"))
src.anchored = TRUE
src.pinned += O
// This does a prob check to catch the thing flying at you, with a minimum of 1%
/mob/living/carbon/human/proc/can_catch(var/obj/O)
if(!get_active_hand()) // If active hand is empty
var/obj/item/organ/external/temp = organs_by_name[BP_R_HAND]
if (hand)
temp = organs_by_name[BP_L_HAND]
if(temp && !temp.is_usable())
return FALSE // The hand isn't working in the first place
if(!O.catchable)
return FALSE
// Alright, our hand works? Time to try the catching.
var/catch_chance = 90 // Default 90% catch rate
if(O.sharp)
catch_chance -= 50 // Catching knives is hard
catch_chance -= get_accuracy_penalty() // Same issues with shooting a gun, or swinging a weapon
catch_chance = between(1, catch_chance, 100)
if(prob(catch_chance))
return TRUE
return FALSE
/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null)
if(!def_zone) ..()
var/obj/item/organ/external/affecting = get_organ(def_zone)
if(affecting)
affecting.embed(O)
/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2)
if (istype(gloves, /obj/item/clothing/gloves))
var/obj/item/clothing/gloves/gl = gloves
gl.add_blood(source)
gl.transfer_blood = amount
else
add_blood(source)
bloody_hands = amount
update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves
/mob/living/carbon/human/proc/bloody_body(var/mob/living/source)
if(wear_suit)
wear_suit.add_blood(source)
update_inv_wear_suit(0)
if(w_uniform)
w_uniform.add_blood(source)
update_inv_w_uniform(0)
/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage, var/def_zone)
// Tox and oxy don't matter to suits.
if(damtype != BURN && damtype != BRUTE) return
// The rig might soak this hit, if we're wearing one.
if(istype(get_rig(),/obj/item/rig))
var/obj/item/rig/rig = get_rig()
rig.take_hit(damage)
// We may also be taking a suit breach.
if(!wear_suit) return
if(!istype(wear_suit,/obj/item/clothing/suit/space)) return
var/obj/item/clothing/suit/space/SS = wear_suit
var/penetrated_dam = max(0,(damage - SS.breach_threshold))
if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam)
/mob/living/carbon/human/reagent_permeability()
var/perm = 0
var/list/perm_by_part = list(
"head" = THERMAL_PROTECTION_HEAD,
"upper_torso" = THERMAL_PROTECTION_UPPER_TORSO,
"lower_torso" = THERMAL_PROTECTION_LOWER_TORSO,
"legs" = THERMAL_PROTECTION_LEG_LEFT + THERMAL_PROTECTION_LEG_RIGHT,
"feet" = THERMAL_PROTECTION_FOOT_LEFT + THERMAL_PROTECTION_FOOT_RIGHT,
"arms" = THERMAL_PROTECTION_ARM_LEFT + THERMAL_PROTECTION_ARM_RIGHT,
"hands" = THERMAL_PROTECTION_HAND_LEFT + THERMAL_PROTECTION_HAND_RIGHT
)
for(var/obj/item/clothing/C in src.get_equipped_items())
if(C.permeability_coefficient == 1 || !C.body_parts_covered)
continue
if(C.body_parts_covered & HEAD)
perm_by_part["head"] *= C.permeability_coefficient
if(C.body_parts_covered & UPPER_TORSO)
perm_by_part["upper_torso"] *= C.permeability_coefficient
if(C.body_parts_covered & LOWER_TORSO)
perm_by_part["lower_torso"] *= C.permeability_coefficient
if(C.body_parts_covered & LEGS)
perm_by_part["legs"] *= C.permeability_coefficient
if(C.body_parts_covered & FEET)
perm_by_part["feet"] *= C.permeability_coefficient
if(C.body_parts_covered & ARMS)
perm_by_part["arms"] *= C.permeability_coefficient
if(C.body_parts_covered & HANDS)
perm_by_part["hands"] *= C.permeability_coefficient
for(var/part in perm_by_part)
perm += perm_by_part[part]
return perm
// This is for preventing harm by being covered in water, which only prometheans need to deal with.
/mob/living/carbon/human/get_water_protection()
var/protection = species.water_resistance
if(protection == 1) // No point doing permeability checks if it won't matter.
return protection
// Wearing clothing with a low permeability_coefficient can protect from water.
var/converted_protection = 1 - protection
var/perm = reagent_permeability()
converted_protection *= perm
return CLAMP(1-converted_protection, 0, 1)
/mob/living/carbon/human/water_act(amount)
adjust_fire_stacks(-amount * 5)
for(var/atom/movable/AM in contents)
AM.water_act(amount)
remove_modifiers_of_type(/datum/modifier/fire)
species.handle_water_damage(src, amount)
/mob/living/carbon/human/shank_attack(obj/item/W, obj/item/grab/G, mob/user, hit_zone)
if(!..())
return 0
var/organ_chance = 50
var/damage = shank_armor_helper(W, G, user)
var/obj/item/organ/external/chest = get_organ(hit_zone)
if(W.edge)
organ_chance = 75
user.next_move = world.time + 20
user.visible_message(span_danger("\The [user] begins to twist \the [W] around inside [src]'s [chest]!"))
if(!do_after(user, 20))
return 0
if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab
return 0
user.visible_message(span_danger("\The [user] twists \the [W] around inside [src]'s [chest]!"))
if(prob(organ_chance))
var/obj/item/organ/internal/selected_organ = pick(chest.internal_organs)
selected_organ.damage = max(selected_organ.damage, damage * 0.5)
G.last_action = world.time
flick(G.hud.icon_state, G.hud)
return 1
/mob/living/carbon/human/is_mouth_covered(head_only, mask_only)
if(!check_has_mouth())
return TRUE
if((isobj(head) && head.body_parts_covered & FACE) || isobj(wear_mask) || (isobj(wear_suit) && wear_suit.body_parts_covered & FACE))
return TRUE
return FALSE