mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-12 16:37:19 +01:00
2f4a8d6f3d
title, h2 and manual font size brokey --------- Co-authored-by: Matt Atlas <liermattia@gmail.com>
1754 lines
57 KiB
Plaintext
1754 lines
57 KiB
Plaintext
/****************************************************
|
|
EXTERNAL ORGANS
|
|
****************************************************/
|
|
|
|
//These control the damage thresholds for the various ways of removing limbs
|
|
#define DROPLIMB_THRESHOLD_EDGE 5
|
|
#define DROPLIMB_THRESHOLD_TEAROFF 2
|
|
#define DROPLIMB_THRESHOLD_DESTROY 1
|
|
#define DROPLIMB_THRESHOLD_DESTROY_PROJECTILE 2
|
|
|
|
#define FRACTURE_AND_TENDON_DAM_THRESHOLD 30 // if a weapon does more than this amount of damage, it's powerful enough to sever the tendon AND fracture the bone, if it's a sharp weapon
|
|
|
|
///How long before a wound starts autohealing without treatment
|
|
#define HEALING_DELAY 60 SECONDS
|
|
|
|
/obj/item/organ/external
|
|
name = "external"
|
|
min_broken_damage = 30
|
|
max_damage = 0
|
|
dir = SOUTH
|
|
organ_tag = "limb"
|
|
|
|
var/force_prosthetic_name
|
|
|
|
var/icon_name = null
|
|
var/body_part = null
|
|
var/icon_position = 0
|
|
|
|
var/damage_state = "00"
|
|
|
|
//Damage variables. Do not modify brute_dam or burn_dam directly. Use take_damage.
|
|
|
|
///Actual current brute damage
|
|
var/brute_dam = 0
|
|
///Actual current burn damage
|
|
var/burn_dam = 0
|
|
|
|
///Ratio of current brute damage to max damage
|
|
var/brute_ratio = 0
|
|
///Ratio of current burn damage to max damage
|
|
var/burn_ratio = 0
|
|
|
|
/// Brute damage modifier.
|
|
var/brute_mod = 1
|
|
/// Burn damage modifier.
|
|
var/burn_mod = 1
|
|
|
|
var/last_dam = -1
|
|
|
|
///Amount of current genetic damage
|
|
var/genetic_degradation = 0
|
|
|
|
///How much the limb hurts
|
|
VAR_PRIVATE/pain = 0
|
|
|
|
///The amount of `pain` at which a limb becomes unusable
|
|
var/pain_disability_threshold
|
|
|
|
///Organ behaviour flags, see `ORGAN_CAN_*` and `ORGAN_HAS_*` in `code\__DEFINES\damage_organs.dm`
|
|
var/limb_flags = ORGAN_CAN_AMPUTATE | ORGAN_CAN_BREAK | ORGAN_CAN_MAIM
|
|
|
|
var/max_size = 0
|
|
|
|
///The `/icon` of the mob that has this organ
|
|
var/icon/mob_icon
|
|
|
|
///A list of overlays for the organ
|
|
var/list/mutable_appearance/mob_overlays
|
|
|
|
var/gendered_icon = 0
|
|
var/force_icon
|
|
|
|
/// If set, will use this as the robotized force icon instead of the robotype
|
|
var/override_robotize_force_icon
|
|
|
|
/// If set, will use tihs as the painted value instead of the robotype
|
|
var/override_robotize_painted
|
|
|
|
/// Will robotize the children of this limb if set to true
|
|
var/robotize_children = TRUE
|
|
|
|
var/limb_name
|
|
var/disfigured = 0
|
|
|
|
var/s_tone
|
|
var/skin_color
|
|
var/hair_color
|
|
|
|
/// A `/list` of wounds
|
|
var/list/wounds
|
|
|
|
/// A list of implants present in this organ
|
|
var/list/implants = list()
|
|
|
|
/// Cache the number of wounds, which is NOT wounds.len!
|
|
var/number_wounds = 0
|
|
|
|
var/perma_injury = 0
|
|
|
|
/// The parent organ
|
|
var/obj/item/organ/external/parent
|
|
|
|
/// A `/list` of organs that have this organ as a parent
|
|
var/list/obj/item/organ/external/children
|
|
|
|
/// Boolean, if this organ supports childrens, which will be added in `children`
|
|
var/supports_children = TRUE
|
|
|
|
/// Internal organs of this body part
|
|
var/list/obj/item/organ/internal/internal_organs = list()
|
|
|
|
/// The tendon
|
|
var/datum/tendon/tendon
|
|
|
|
/// A path of the type of tendon to create when this organ is created
|
|
var/tendon_path = /datum/tendon
|
|
|
|
/// Name of the limb's tendon. Achilles heel, etc.
|
|
var/tendon_name = "tendon"
|
|
|
|
/// HP value of the limb's tendon
|
|
var/tendon_health = 30
|
|
|
|
var/list/tendon_msgs = list("tore apart", "ripped away")
|
|
|
|
var/damage_msg = SPAN_WARNING("You feel an intense pain!")
|
|
var/broken_description
|
|
var/open = 0
|
|
var/stage = 0
|
|
var/cavity = 0
|
|
|
|
/// Boolean, if it was sabotaged (emagged), make it detonate when it fails
|
|
var/sabotaged = FALSE
|
|
|
|
/// Needs to be opened with a saw to access the organs
|
|
var/encased
|
|
|
|
/// Descriptive string used in dislocation
|
|
var/joint = "joint"
|
|
|
|
/// Name of the artery. Cartoid, etc.
|
|
var/artery_name = "artery"
|
|
|
|
/// Multiplier for bleeding in a limb
|
|
var/arterial_bleed_severity = 0.75
|
|
|
|
/// Descriptive string used in amputation
|
|
var/amputation_point
|
|
|
|
/// If the joint is dislocated
|
|
var/dislocated = FALSE
|
|
|
|
/// How often wounds should be updated, a higher number means less often
|
|
var/wound_update_accuracy = 1
|
|
var/body_hair
|
|
var/painted = 0
|
|
|
|
/// The amount of bandages on our sprite
|
|
var/bandage_level = BANDAGE_LEVEL_NONE
|
|
|
|
/// For special projectile gibbing calculation, dubbed "maiming"
|
|
var/maim_bonus = 0
|
|
|
|
/// Markings (body_markings) to apply to the icon
|
|
var/list/genetic_markings
|
|
|
|
/// Same as `genetic_markings`, but not preserved when cloning
|
|
var/list/temporary_markings
|
|
|
|
/// The `genetic_markings` and `temporary_markings` cached for perf. reasons
|
|
var/list/cached_markings
|
|
|
|
var/list/image/additional_images
|
|
|
|
/// Pressure applied to wounds. It'll make them bleed less, generally.
|
|
var/atom/movable/applied_pressure
|
|
|
|
var/image/hud_damage_image
|
|
|
|
/// How many augments you can fit inside this limb
|
|
var/augment_limit
|
|
|
|
var/obj/item/organ/internal/infect_target_internal //make internal organs become infected one at a time instead of all at once
|
|
var/obj/item/organ/external/infect_target_external //make child and parent organs become infected one at a time instead of all at once
|
|
|
|
/// Dionae limb
|
|
var/mob/living/carbon/alien/diona/nymph
|
|
|
|
var/nymph_child
|
|
|
|
/// Whether the limb has an emissive icon state associated with it
|
|
var/is_emissive = FALSE
|
|
/// Whether the limb has an active overlay icon state associated with it
|
|
var/is_overlay = FALSE
|
|
/// Whether the limb is a tesla limb (required for special handling)
|
|
var/is_tesla = FALSE
|
|
|
|
/obj/item/organ/external/Initialize(mapload)
|
|
if(robotize_type)
|
|
robotize(robotize_type)
|
|
drop_sound = 'sound/items/drop/prosthetic.ogg'
|
|
pickup_sound = 'sound/items/pickup/prosthetic.ogg'
|
|
else
|
|
//HACK: Make sure non-emissive organs, if swapped to, are not treated as emissive. Robotized organs have their own handling, but we still need to cover the case where it is somehow swapped to an organic limb
|
|
is_emissive = initial(is_emissive)
|
|
|
|
. = ..(mapload, FALSE)
|
|
if(isnull(pain_disability_threshold))
|
|
pain_disability_threshold = (max_damage * 0.75)
|
|
if(owner)
|
|
replaced(owner)
|
|
sync_colour_to_human(owner)
|
|
|
|
if ((status & ORGAN_PLANT))
|
|
limb_flags &= ~ORGAN_CAN_BREAK
|
|
|
|
get_icon()
|
|
|
|
RegisterSignal(src, COMSIG_UPDATE_LIMB_IMAGE, PROC_REF(modify_damage_hud_image_color))
|
|
|
|
get_damage_hud_image(TRUE)
|
|
|
|
if((limb_flags & ORGAN_HAS_TENDON) && !BP_IS_ROBOTIC(src) && tendon_path)
|
|
tendon = new tendon_path(src, tendon_name, tendon_health, tendon_msgs)
|
|
else if(limb_flags & ORGAN_HAS_TENDON)
|
|
limb_flags &= ~ORGAN_HAS_TENDON
|
|
|
|
/obj/item/organ/external/Destroy()
|
|
|
|
if(parent?.children)
|
|
parent.children -= src
|
|
|
|
if(istype(owner) && owner.bad_external_organs)
|
|
owner.bad_external_organs -= src
|
|
|
|
if(limb_name && istype(owner, /mob/living/carbon) && owner.organs_by_name)
|
|
owner.organs_by_name[limb_name] = null
|
|
owner.organs_by_name -= limb_name
|
|
|
|
parent = null
|
|
|
|
//Remove the images from the owner and clear them
|
|
if(istype(owner))
|
|
cut_additional_images(owner)
|
|
additional_images = null
|
|
|
|
genetic_markings = null
|
|
temporary_markings = null
|
|
cached_markings = null
|
|
mob_icon = null
|
|
|
|
if(wounds)
|
|
QDEL_NULL_LIST(wounds)
|
|
QDEL_LIST(children)
|
|
QDEL_LIST(internal_organs)
|
|
QDEL_LIST(implants)
|
|
|
|
infect_target_internal = null
|
|
infect_target_external = null
|
|
|
|
applied_pressure = null
|
|
|
|
QDEL_NULL(nymph)
|
|
|
|
QDEL_NULL(tendon)
|
|
return ..()
|
|
|
|
/obj/item/organ/external/proc/invalidate_marking_cache()
|
|
cached_markings = null
|
|
|
|
///If the organ requires amputation, returns TRUE. Otherwise, returns FALSE
|
|
/obj/item/organ/external/proc/CheckNeedsAmputation()
|
|
var/extreme_damage = FALSE
|
|
if(!(brute_ratio < 100))
|
|
extreme_damage = TRUE
|
|
if(!(burn_ratio < 100))
|
|
extreme_damage = TRUE
|
|
if(extreme_damage && !(limb_flags & ORGAN_HEALS_OVERKILL))
|
|
return TRUE // Limb took too much damage, nothing left to heal
|
|
return FALSE
|
|
|
|
/obj/item/organ/external/attack_self(var/mob/user)
|
|
if(!length(contents))
|
|
return ..()
|
|
var/list/removable_objects = list()
|
|
for(var/obj/item/organ/external/E in (contents + src))
|
|
if(!istype(E))
|
|
continue
|
|
for(var/obj/item/I in E.contents)
|
|
if(istype(I,/obj/item/organ))
|
|
continue
|
|
removable_objects |= I
|
|
if(length(removable_objects))
|
|
var/obj/item/I = pick(removable_objects)
|
|
I.forceMove(get_turf(user)) //just in case something was embedded that is not an item
|
|
if(istype(I))
|
|
if(!(user.l_hand && user.r_hand))
|
|
user.put_in_hands(I)
|
|
user.visible_message(SPAN_DANGER("\The [user] rips \the [I] out of \the [src]!"))
|
|
return //no eating the limb until everything's been removed
|
|
return ..()
|
|
|
|
/obj/item/organ/external/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
if(distance <= 1)
|
|
for(var/obj/item/I in contents)
|
|
if(istype(I, /obj/item/organ))
|
|
continue
|
|
. += SPAN_DANGER("There is \a [I] sticking out of it.")
|
|
|
|
/obj/item/organ/external/attackby(obj/item/attacking_item, mob/user)
|
|
switch(stage)
|
|
if(0)
|
|
if(istype(attacking_item, /obj/item/surgery/scalpel))
|
|
user.visible_message(SPAN_DANGER("<b>[user]</b> cuts [src] open with [attacking_item]!"))
|
|
stage++
|
|
return
|
|
if(1)
|
|
if(istype(attacking_item, /obj/item/surgery/retractor))
|
|
user.visible_message(SPAN_DANGER("<b>[user]</b> cracks [src] open like an egg with [attacking_item]!"))
|
|
stage++
|
|
return
|
|
if(2)
|
|
if(istype(attacking_item, /obj/item/surgery/hemostat))
|
|
var/list/organs = get_contents_recursive()
|
|
if(length(organs))
|
|
var/obj/item/removing = pick(organs)
|
|
var/obj/item/organ/external/current_child = removing.loc
|
|
|
|
current_child.implants.Remove(removing)
|
|
current_child.internal_organs.Remove(removing)
|
|
|
|
status |= ORGAN_CUT_AWAY
|
|
|
|
removing.forceMove(get_turf(user))
|
|
|
|
if(!(user.l_hand && user.r_hand))
|
|
user.put_in_hands(removing)
|
|
user.visible_message(SPAN_DANGER("<b>[user]</b> extracts [removing] from [src] with [attacking_item]!"))
|
|
else
|
|
user.visible_message(SPAN_DANGER("<b>[user]</b> fishes around fruitlessly in [src] with [attacking_item]."))
|
|
return
|
|
..()
|
|
|
|
/**
|
|
* Get a list of contents of this organ and all the child organs
|
|
*/
|
|
/obj/item/organ/external/proc/get_contents_recursive()
|
|
var/list/all_items = list()
|
|
|
|
all_items.Add(implants)
|
|
all_items.Add(internal_organs)
|
|
|
|
for(var/obj/item/organ/external/child in children)
|
|
all_items.Add(child.get_contents_recursive())
|
|
|
|
return all_items
|
|
|
|
/obj/item/organ/external/proc/dislocate(var/primary)
|
|
if(dislocated == -1)
|
|
return
|
|
if(primary)
|
|
dislocated = 2
|
|
else
|
|
dislocated = 1
|
|
add_verb(owner, /mob/living/carbon/human/proc/undislocate)
|
|
if(children && children.len)
|
|
for(var/obj/item/organ/external/child in children)
|
|
child.dislocate()
|
|
|
|
/obj/item/organ/external/proc/undislocate()
|
|
if(dislocated == -1)
|
|
return
|
|
|
|
dislocated = 0
|
|
if(children && children.len)
|
|
for(var/obj/item/organ/external/child in children)
|
|
if(child.dislocated == 1)
|
|
child.undislocate()
|
|
if(owner)
|
|
owner.shock_stage += 20
|
|
for(var/obj/item/organ/external/limb in owner.organs)
|
|
if(limb.dislocated == 2)
|
|
return
|
|
remove_verb(owner, /mob/living/carbon/human/proc/undislocate)
|
|
|
|
/obj/item/organ/external/update_organ_health()
|
|
damage = min(max_damage, (brute_dam + burn_dam))
|
|
|
|
/obj/item/organ/external/replaced(var/mob/living/carbon/human/target)
|
|
..()
|
|
|
|
if(istype(owner))
|
|
owner.organs_by_name[limb_name] = src
|
|
owner.organs |= src
|
|
if(!species)
|
|
species = owner.species
|
|
for(var/obj/item/organ/organ in internal_organs)
|
|
organ.replaced(owner,src)
|
|
|
|
for(var/obj/implant in implants)
|
|
implant.forceMove(owner)
|
|
|
|
if(istype(implant, /obj/item/implant))
|
|
var/obj/item/implant/imp_device = implant
|
|
|
|
// we can't use implanted() here since it's often interactive
|
|
imp_device.imp_in = owner
|
|
imp_device.implanted = 1
|
|
|
|
for(var/obj/item/organ/external/organ in children)
|
|
organ.replaced(owner)
|
|
|
|
if(!parent && parent_organ)
|
|
parent = owner.organs_by_name[src.parent_organ]
|
|
if(parent)
|
|
if(!parent.children)
|
|
parent.children = list()
|
|
parent.children.Add(src)
|
|
//Remove all stump wounds since limb is not missing anymore
|
|
for(var/datum/wound/lost_limb/W in parent.wounds)
|
|
qdel(W)
|
|
break
|
|
parent.update_damages()
|
|
|
|
action_button_name = initial(action_button_name)
|
|
owner.update_action_buttons()
|
|
|
|
/****************************************************
|
|
DAMAGE PROCS
|
|
****************************************************/
|
|
|
|
/obj/item/organ/external/proc/is_damageable(var/additional_damage = 0)
|
|
//Continued damage to vital organs can kill you, and robot organs don't count towards total damage so no need to cap them.
|
|
return (BP_IS_ROBOTIC(src) || brute_dam + burn_dam + additional_damage < max_damage * 4)
|
|
|
|
/obj/item/organ/external/take_damage(brute, burn, damage_flags, used_weapon = null, list/forbidden_limbs = list(), var/silent)
|
|
brute = round(brute * brute_mod, 0.1)
|
|
burn = round(burn * burn_mod, 0.1)
|
|
if((brute <= 0) && (burn <= 0))
|
|
return 0
|
|
|
|
if(damage_flags & DAMAGE_FLAG_IGNORE_PROSTHETICS && BP_IS_ROBOTIC(src))
|
|
return 0
|
|
|
|
var/laser = (damage_flags & DAMAGE_FLAG_LASER)
|
|
var/sharp = (damage_flags & DAMAGE_FLAG_SHARP)
|
|
var/edge = (damage_flags & DAMAGE_FLAG_EDGE)
|
|
var/psionic = (damage_flags & DAMAGE_FLAG_PSIONIC)
|
|
var/blunt = !!(brute && !sharp && !edge)
|
|
|
|
/// Psionics and psionically deaf species take varying amounts of damage from psionic abilities.
|
|
if(psionic)
|
|
// Damage modifier is 0.9 for no psionics at all, 1 for baseline psychic, and an extra 0.1 per equivalent rank above baseline.
|
|
var/psi_damage_modifier = 1 + (0.1 * (owner.check_psi_sensitivity() - 1))
|
|
brute *= psi_damage_modifier
|
|
burn *= psi_damage_modifier
|
|
|
|
if(status & ORGAN_BROKEN && prob(40) && brute)
|
|
if(owner && (owner.can_feel_pain()) && owner.stat == CONSCIOUS)
|
|
owner.emote(pick("scream", "groan")) //getting hit on broken hand hurts
|
|
|
|
if(used_weapon)
|
|
add_autopsy_data("[used_weapon]", brute + burn)
|
|
|
|
var/spillover = 0
|
|
if(!is_damageable(brute + burn))
|
|
spillover = brute_dam + burn_dam + brute - max_damage
|
|
if(spillover > 0)
|
|
brute = max(brute - spillover, 0)
|
|
else
|
|
spillover = brute_dam + burn_dam + brute + burn - max_damage
|
|
if(spillover > 0)
|
|
burn = max(burn - spillover, 0)
|
|
|
|
handle_limb_gibbing(used_weapon, brute, burn)
|
|
|
|
if(brute_dam + brute > min_broken_damage && prob(brute_dam + brute * (1 + blunt)))
|
|
if(blunt || brute > FRACTURE_AND_TENDON_DAM_THRESHOLD)
|
|
fracture()
|
|
|
|
// High brute damage or sharp objects may damage internal organs
|
|
if(length(internal_organs))
|
|
if(damage_internal_organs(brute, burn, damage_flags))
|
|
brute /= 2
|
|
burn /= 2
|
|
|
|
var/datum/wound/created_wound
|
|
var/can_cut = !BP_IS_ROBOTIC(src) && (sharp || prob(brute))
|
|
if(brute)
|
|
var/to_create = INJURY_TYPE_BRUISE
|
|
if(can_cut)
|
|
to_create = INJURY_TYPE_CUT
|
|
//need to check sharp again here so that blunt damage that was strong enough to break skin doesn't give puncture wounds
|
|
if(sharp && !edge)
|
|
to_create = INJURY_TYPE_PIERCE
|
|
created_wound = createwound(to_create, brute)
|
|
|
|
if(burn)
|
|
if(laser)
|
|
created_wound = createwound(INJURY_TYPE_LASER, burn)
|
|
else
|
|
created_wound = createwound(INJURY_TYPE_BURN, burn)
|
|
|
|
add_pain(0.6 * burn + 0.4 * brute)
|
|
|
|
if(owner)
|
|
SEND_SIGNAL(owner, COMSIG_DAMAGE_TO_ENDOSKELETON, burn + brute)
|
|
|
|
//If there are still hurties to dispense
|
|
if (spillover)
|
|
owner.shock_stage += spillover * GLOB.config.organ_damage_spillover_multiplier
|
|
|
|
// sync the organ's damage with its wounds
|
|
update_damages()
|
|
if(owner)
|
|
owner.updatehealth() //droplimb will call updatehealth() again if it does end up being called
|
|
if (update_icon())
|
|
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
|
|
|
|
return created_wound
|
|
|
|
/obj/item/organ/external/proc/damage_internal_organs(brute, burn, damage_flags)
|
|
if(!length(internal_organs))
|
|
return FALSE
|
|
|
|
var/damage_amt = brute
|
|
var/cur_damage = brute_dam
|
|
var/sharp = (damage_flags & DAMAGE_FLAG_SHARP)
|
|
var/laser = (damage_flags & DAMAGE_FLAG_LASER)
|
|
|
|
if(BP_IS_ROBOTIC(src) || laser)
|
|
damage_amt += burn
|
|
cur_damage += burn_dam
|
|
|
|
if(!damage_amt)
|
|
return FALSE
|
|
|
|
var/organ_damage_threshold = 10
|
|
if(sharp)
|
|
organ_damage_threshold *= 0.5
|
|
if(laser)
|
|
organ_damage_threshold *= 1.25
|
|
|
|
if(!(cur_damage + damage_amt >= max_damage) && !(damage_amt >= organ_damage_threshold))
|
|
return FALSE
|
|
|
|
var/list/victims = list()
|
|
var/organ_hit_chance = 0
|
|
for(var/obj/item/organ/internal/I in internal_organs)
|
|
if(I.damage < I.max_damage)
|
|
victims[I] = I.relative_size
|
|
organ_hit_chance += I.relative_size
|
|
|
|
//No damageable organs
|
|
if(!length(victims))
|
|
return FALSE
|
|
|
|
organ_hit_chance += 5 * damage_amt / organ_damage_threshold
|
|
|
|
if(!BP_IS_ROBOTIC(src))
|
|
if(encased && !(status & ORGAN_BROKEN)) //ribs protect
|
|
organ_hit_chance *= 0.2
|
|
else
|
|
organ_hit_chance *= 0.8 // robots should not have the same advantage
|
|
|
|
organ_hit_chance = min(organ_hit_chance, 100)
|
|
if(prob(organ_hit_chance))
|
|
var/obj/item/organ/internal/victim = pickweight(victims)
|
|
damage_amt -= max(damage_amt*victim.damage_reduction, 0)
|
|
victim.take_internal_damage(damage_amt)
|
|
return TRUE
|
|
|
|
/obj/item/organ/external/proc/handle_limb_gibbing(var/used_weapon, var/brute, var/burn)
|
|
//If limb took enough damage, try to cut or tear it off
|
|
if(owner && loc == owner && !is_stump())
|
|
if((limb_flags & ORGAN_CAN_AMPUTATE))
|
|
if((brute_dam + burn_dam) >= (max_damage * GLOB.config.organ_health_multiplier))
|
|
|
|
var/edge_eligible = FALSE
|
|
var/blunt_eligible = FALSE
|
|
var/dam_flags = 0
|
|
/// Certain limbs like zombie limbs have an integrated maiming bonus that make them easier to delimb. Add that.
|
|
var/maim_bonus_to_add = src.maim_bonus
|
|
|
|
if(isobj(used_weapon))
|
|
var/obj/W = used_weapon
|
|
dam_flags = W.damage_flags()
|
|
if(isprojectile(W))
|
|
var/obj/projectile/P = W
|
|
if(dam_flags & DAMAGE_FLAG_BULLET)
|
|
blunt_eligible = TRUE
|
|
maim_bonus_to_add += P.maim_rate
|
|
if(W.w_class >= w_class && (dam_flags & DAMAGE_FLAG_EDGE))
|
|
edge_eligible = TRUE
|
|
|
|
if(!blunt_eligible && edge_eligible && (brute >= max_damage / max((DROPLIMB_THRESHOLD_EDGE + maim_bonus_to_add), 0.1)))
|
|
droplimb(0, DROPLIMB_EDGE)
|
|
else if(burn >= max_damage / max((dam_flags & DAMAGE_FLAG_LASER ? DROPLIMB_THRESHOLD_DESTROY_PROJECTILE : DROPLIMB_THRESHOLD_DESTROY) + maim_bonus_to_add, 0.1))
|
|
droplimb(0, DROPLIMB_BURN)
|
|
else if(blunt_eligible && brute >= max_damage / max((dam_flags & DAMAGE_FLAG_BULLET ? DROPLIMB_THRESHOLD_DESTROY_PROJECTILE : DROPLIMB_THRESHOLD_DESTROY) + maim_bonus_to_add, 0.1))
|
|
droplimb(0, DROPLIMB_BLUNT)
|
|
else if(brute >= max_damage / max((DROPLIMB_THRESHOLD_TEAROFF + maim_bonus_to_add), 0.1))
|
|
droplimb(0, DROPLIMB_EDGE)
|
|
|
|
/obj/item/organ/external/heal_damage(brute, burn, internal = 0, robo_repair = 0)
|
|
if(status & ORGAN_ROBOT && !robo_repair)
|
|
return
|
|
|
|
//Heal damage on the individual wounds
|
|
for(var/datum/wound/W as anything in wounds)
|
|
if(brute == 0 && burn == 0)
|
|
break
|
|
|
|
// heal brute damage
|
|
if(W.damage_type == INJURY_TYPE_BURN)
|
|
burn = W.heal_damage(burn)
|
|
else
|
|
brute = W.heal_damage(brute)
|
|
|
|
if(internal)
|
|
status &= ~ORGAN_BROKEN
|
|
perma_injury = 0
|
|
|
|
//Sync the organ's damage with its wounds
|
|
update_damages()
|
|
|
|
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
|
|
|
|
owner.updatehealth()
|
|
|
|
return update_icon()
|
|
|
|
/*
|
|
This function completely restores a damaged organ to perfect condition.
|
|
*/
|
|
/obj/item/organ/external/rejuvenate()
|
|
damage_state = "00"
|
|
status &= ~ORGAN_DAMAGE_STATES
|
|
perma_injury = 0
|
|
brute_dam = 0
|
|
burn_dam = 0
|
|
germ_level = 0
|
|
QDEL_LIST(wounds)
|
|
number_wounds = 0
|
|
|
|
// handle internal organs
|
|
for(var/obj/item/organ/current_organ in internal_organs)
|
|
current_organ.rejuvenate()
|
|
|
|
// remove embedded objects and drop them on the floor
|
|
for(var/obj/implanted_object in implants)
|
|
if(!istype(implanted_object,/obj/item/implant)) // We don't want to remove REAL implants. Just shrapnel etc.
|
|
implanted_object.forceMove(owner.loc)
|
|
implants -= implanted_object
|
|
|
|
if(istype(tendon))
|
|
tendon.rejuvenate()
|
|
|
|
owner.updatehealth()
|
|
|
|
|
|
/obj/item/organ/external/proc/createwound(type = INJURY_TYPE_CUT, damage)
|
|
if(damage <= 0 || !owner)
|
|
return
|
|
|
|
//moved this before the open_wound check so that having many small wounds for example doesn't somehow protect you from taking internal damage (because of the return)
|
|
//Possibly trigger an internal wound, too.
|
|
var/local_damage = brute_dam + burn_dam + damage
|
|
|
|
var/is_burn_type_damage = (type in list(INJURY_TYPE_BURN, INJURY_TYPE_LASER))
|
|
var/is_brute_type_damage = (type in list(INJURY_TYPE_CUT, INJURY_TYPE_PIERCE, INJURY_TYPE_BRUISE))
|
|
|
|
if(damage > (min_broken_damage / 2) && local_damage > min_broken_damage && !(status & ORGAN_ROBOT) && is_brute_type_damage)
|
|
if(prob(damage) && sever_artery())
|
|
owner.custom_pain("You feel something rip in your [name]!", 25)
|
|
|
|
if(istype(tendon))
|
|
var/new_brute = damage
|
|
if(min_broken_damage - damage > 0)
|
|
// Only pass on damage that goes over the broken threshold
|
|
new_brute = damage - min_broken_damage
|
|
tendon.damage(new_brute)
|
|
|
|
//Burn damage can cause fluid loss due to blistering and cook-off
|
|
if(is_burn_type_damage && (damage > 5 || damage + burn_dam >= 15) && !BP_IS_ROBOTIC(src))
|
|
var/fluid_loss_severity
|
|
switch(type)
|
|
if(INJURY_TYPE_BURN)
|
|
fluid_loss_severity = FLUIDLOSS_WIDE_BURN
|
|
if(INJURY_TYPE_LASER)
|
|
fluid_loss_severity = FLUIDLOSS_CONC_BURN
|
|
var/fluid_loss = (damage/(owner.maxhealth - GLOB.config.health_threshold_dead)) * DEFAULT_BLOOD_AMOUNT * fluid_loss_severity
|
|
owner.remove_blood_simple(fluid_loss)
|
|
|
|
// first check whether we can widen an existing wound
|
|
if(LAZYLEN(wounds) && prob(max(50+(number_wounds-1)*10,90)))
|
|
if((type == INJURY_TYPE_CUT || type == INJURY_TYPE_BRUISE) && damage >= 5)
|
|
//we need to make sure that the wound we are going to worsen is compatible with the type of damage...
|
|
var/list/compatible_wounds = list()
|
|
for (var/datum/wound/W as anything in wounds)
|
|
if (W.can_worsen(type, damage))
|
|
compatible_wounds += W
|
|
|
|
if(length(compatible_wounds))
|
|
var/datum/wound/W = pick(compatible_wounds)
|
|
W.open_wound(damage)
|
|
|
|
if(bandage_level)
|
|
owner.visible_message(SPAN_WARNING("The bandages on [owner.name]'s [name] gets [is_burn_type_damage ? "burnt" : "ripped"] off!"), SPAN_WARNING("The bandages on your [name] gets [is_burn_type_damage ? "burnt" : "ripped"] off!"))
|
|
bandage_level = BANDAGE_LEVEL_NONE
|
|
owner.update_bandages()
|
|
|
|
if(prob(25))
|
|
if(status & ORGAN_ROBOT)
|
|
owner.visible_message(SPAN_WARNING("The damage to [owner.name]'s [name] worsens."),\
|
|
SPAN_WARNING("The damage to your [name] worsens."),\
|
|
"You hear the screech of abused metal.")
|
|
else
|
|
owner.visible_message(SPAN_WARNING("The wound on [owner.name]'s [name] widens with a nasty ripping noise."),\
|
|
SPAN_WARNING("The wound on your [name] widens with a nasty ripping noise."),\
|
|
"You hear a nasty ripping noise, as if flesh is being torn apart.")
|
|
|
|
return W
|
|
|
|
//Creating wound
|
|
var/wound_type = get_wound_type(type, damage)
|
|
|
|
if(wound_type)
|
|
var/datum/wound/W = new wound_type(damage, src)
|
|
|
|
//Check whether we can add the wound to an existing wound
|
|
for(var/datum/wound/other in wounds)
|
|
if(other.can_merge(W))
|
|
other.merge_wound(W)
|
|
W = other
|
|
break
|
|
|
|
if(!QDELETED(W))
|
|
if(!LAZYISIN(wounds, W))
|
|
LAZYADD(wounds, W)
|
|
|
|
if(bandage_level)
|
|
owner.visible_message(SPAN_WARNING("The bandages on [owner.name]'s [name] gets [is_burn_type_damage ? "burnt" : "ripped"] off!"), SPAN_WARNING("The bandages on your [name] gets [is_burn_type_damage ? "burnt" : "ripped"] off!"))
|
|
bandage_level = BANDAGE_LEVEL_NONE
|
|
owner.update_bandages()
|
|
return W
|
|
|
|
/****************************************************
|
|
PROCESSING & UPDATING
|
|
****************************************************/
|
|
|
|
//external organs handle brokenness a bit differently when it comes to damage. Instead brute_dam is checked inside process()
|
|
//this also ensures that an external organ cannot be "broken" without broken_description being set.
|
|
/obj/item/organ/external/is_broken()
|
|
if(status & ORGAN_CUT_AWAY)
|
|
return TRUE
|
|
if((status & ORGAN_BROKEN) && !(status & ORGAN_SPLINTED))
|
|
for(var/obj/item/organ/internal/augment/aug in internal_organs)
|
|
if(aug.supports_limb)
|
|
if(aug.is_broken())
|
|
continue
|
|
if(aug.is_bruised() && prob(60))
|
|
continue
|
|
return FALSE
|
|
return TRUE
|
|
return FALSE
|
|
|
|
//Determines if we even need to process this organ.
|
|
/obj/item/organ/external/proc/need_process()
|
|
if(BP_IS_ROBOTIC(src) && surge_damage)
|
|
return TRUE
|
|
if(is_tesla && owner)
|
|
return TRUE
|
|
if(BP_IS_ROBOTIC(src))
|
|
return FALSE
|
|
if(get_pain())
|
|
return TRUE
|
|
if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_SPLINTED|ORGAN_DEAD|ORGAN_MUTATED))
|
|
return TRUE
|
|
if(brute_dam || burn_dam)
|
|
return TRUE
|
|
if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up.
|
|
last_dam = brute_dam + burn_dam
|
|
return TRUE
|
|
else
|
|
last_dam = brute_dam + burn_dam
|
|
if(germ_level)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/item/organ/external/process(seconds_per_tick)
|
|
if(owner)
|
|
//Specialized handling for tesla limbs. Checks if the limb currently has an associated tesla spine. Else, will disable the emissive and active overlays
|
|
if(is_tesla)
|
|
var/obj/item/organ/internal/augment/tesla/T = owner.internal_organs_by_name[BP_AUG_TESLA]
|
|
if(T && !T.is_broken())
|
|
is_emissive = initial(is_emissive)
|
|
is_overlay = initial(is_overlay)
|
|
else
|
|
is_emissive = FALSE
|
|
is_overlay = FALSE
|
|
return
|
|
|
|
// Process wounds, doing healing etc. Only do this every few ticks to save processing power
|
|
if(owner.life_tick % wound_update_accuracy == 0)
|
|
update_wounds()
|
|
|
|
//Chem traces slowly vanish
|
|
if(owner.life_tick % 10 == 0)
|
|
for(var/chemID in trace_chemicals)
|
|
trace_chemicals[chemID] = trace_chemicals[chemID] - 1
|
|
if(trace_chemicals[chemID] <= 0)
|
|
trace_chemicals.Remove(chemID)
|
|
|
|
if(!(status & ORGAN_BROKEN))
|
|
perma_injury = 0
|
|
|
|
if(status & ORGAN_NYMPH)
|
|
var/datum/component/nymph_limb/N = GetComponent(/datum/component/nymph_limb)
|
|
N.handle_nymph(src)
|
|
|
|
if(surge_damage && (status & ORGAN_ASSISTED))
|
|
tick_surge_damage(seconds_per_tick) //Yes, this being here is intentional since this proc does not call ..() unless the owner is null.
|
|
|
|
//Infections
|
|
update_germs()
|
|
|
|
//check if an online RIG can splint the broken bone
|
|
check_rigsplints()
|
|
else
|
|
..()
|
|
|
|
/obj/item/organ/external/do_surge_effects()
|
|
if(prob(surge_damage))
|
|
owner.custom_pain("The artificial nerves in your [name] scream out in pain!", surge_damage/6)
|
|
|
|
/obj/item/organ/external/proc/check_rigsplints()
|
|
if((status & ORGAN_BROKEN) && !(status & ORGAN_SPLINTED))
|
|
if(istype(owner,/mob/living/carbon/human))
|
|
var/mob/living/carbon/human/H = owner
|
|
if(H.back && istype(H.back, /obj/item/rig))
|
|
var/obj/item/rig/R = H.back
|
|
if(R.offline)
|
|
return
|
|
if(H.wear_suit && istype(H.wear_suit,/obj/item/clothing/suit/space))
|
|
var/obj/item/clothing/suit/space/suit = H.wear_suit
|
|
if(isnull(suit.supporting_limbs))
|
|
return
|
|
to_chat(owner, SPAN_WARNING("You feel \the [suit] constrict about your [name], supporting it."))
|
|
status |= ORGAN_SPLINTED
|
|
suit.supporting_limbs |= src
|
|
owner.update_hud_hands()
|
|
|
|
//Updating germ levels. Handles organ germ levels and necrosis.
|
|
/*
|
|
The INFECTION_LEVEL values defined in setup.dm control the time it takes to reach the different
|
|
infection levels. Since infection growth is exponential, you can adjust the time it takes to get
|
|
from one germ_level to another using the rough formula:
|
|
|
|
desired_germ_level = initial_germ_level*e^(desired_time_in_seconds/1000)
|
|
|
|
So if I wanted it to take an average of 15 minutes to get from level one (100) to level two
|
|
I would set INFECTION_LEVEL_TWO to 100*e^(15*60/1000) = 245. Note that this is the average time,
|
|
the actual time is dependent on RNG.
|
|
|
|
INFECTION_LEVEL_ONE below this germ level nothing happens, and the infection doesn't grow
|
|
INFECTION_LEVEL_TWO above this germ level the infection will start to spread to internal and adjacent organs
|
|
INFECTION_LEVEL_THREE above this germ level the player will take additional toxin damage per second, and will die in minutes without
|
|
antitox. also, above this germ level you will need to overdose on thetamycin to reduce the germ_level.
|
|
|
|
Note that amputating the affected organ does in fact remove the infection from the player's body.
|
|
*/
|
|
/obj/item/organ/external/proc/update_germs()
|
|
|
|
if(status & (ORGAN_ROBOT) || (owner.species && owner.species.flags & NO_BLOOD)) //Robotic limbs shouldn't be infected, nor should nonexistant limbs, or bloodless species.
|
|
germ_level = 0
|
|
return
|
|
|
|
if(germ_level <= 0) //Catch any weirdness that might happen with negative values
|
|
germ_level = 0
|
|
|
|
if(owner.bodytemperature >= 170) //cryo stops germs from moving and doing their bad stuffs
|
|
//** Syncing germ levels with external wounds
|
|
handle_germ_sync()
|
|
|
|
//** Handle antibiotics and curing infections
|
|
handle_antibiotics()
|
|
|
|
//** Handle the effects of infections
|
|
handle_germ_effects()
|
|
|
|
/obj/item/organ/external/proc/handle_germ_sync()
|
|
var/antibiotics = 0
|
|
if(CE_ANTIBIOTIC in owner.chem_effects)
|
|
antibiotics = owner.chem_effects[CE_ANTIBIOTIC]
|
|
var/increased_own_germs = FALSE
|
|
for(var/datum/wound/W as anything in wounds)
|
|
//Open wounds can become infected
|
|
if (owner.germ_level > W.germ_level && W.infection_check())
|
|
W.germ_level++
|
|
if(!increased_own_germs && antibiotics < 5 && W.germ_level > germ_level)
|
|
germ_level++
|
|
increased_own_germs = TRUE // limit increase to 1/tick
|
|
|
|
/obj/item/organ/external/proc/get_infect_target(var/list/infect_candidates = list())
|
|
var/obj/item/organ/temp_target
|
|
shuffle(infect_candidates) //Slightly randomizes since if all germ levels are zero, it'll always be the first pick of the list
|
|
|
|
//figure out which organs we can spread germs to
|
|
for (var/obj/item/organ/I as anything in infect_candidates)
|
|
if(I.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //Only choose organs that have less germs than us AND are below level two
|
|
//The below will always be the organ with the highest germ level. It picks a temp_target first then cycles through to find which, if any, has more germs.
|
|
if(!temp_target || I.germ_level > temp_target.germ_level)
|
|
temp_target = I //This will always be the organ with the highest germ level
|
|
|
|
return temp_target
|
|
|
|
/obj/item/organ/external/handle_germ_effects()
|
|
|
|
var/antibiotics = 0
|
|
if(CE_ANTIBIOTIC in owner.chem_effects)
|
|
antibiotics = owner.chem_effects[CE_ANTIBIOTIC]
|
|
|
|
if(germ_level < INFECTION_LEVEL_TWO)
|
|
//null out the infect targets since at this point we're not in danger of spreading our infection.
|
|
infect_target_internal = null
|
|
infect_target_external = null
|
|
return ..()
|
|
|
|
germ_level++
|
|
|
|
if(germ_level >= INFECTION_LEVEL_TWO && REAGENT_VOLUME(owner.reagents, /singleton/reagent/thetamycin) < 5) //The presence of 5 units of thetamycin will stop infections spreading
|
|
//SPREADING TO INTERNAL ORGANS
|
|
if(isnull(infect_target_internal) || QDELETED(infect_target_internal))
|
|
infect_target_internal = get_infect_target(internal_organs)
|
|
|
|
else
|
|
if(prob(25) || !infect_target_internal.is_infected()) //Increase steadily until infection_level_one, then only bump the germ level now and then
|
|
infect_target_internal.germ_level++ //slowly increase the infection level
|
|
//Check to see if the other organ is as infected or more infected than us. If so, we need to get a new target on the next process
|
|
if(infect_target_internal.germ_level >= min(germ_level, INFECTION_LEVEL_TWO))
|
|
infect_target_internal = null
|
|
|
|
//SPREADING TO CHILD AND PARENT EXTERNAL ORGANS
|
|
if(isnull(infect_target_external) || QDELETED(infect_target_internal))
|
|
var/list/temp_targets = list()
|
|
if(children)
|
|
for (var/obj/item/organ/external/child in children)
|
|
if (child.germ_level < germ_level && !(child.status & ORGAN_ROBOT))
|
|
temp_targets += child
|
|
|
|
if(parent)
|
|
if(parent.germ_level < germ_level && !(parent.status & ORGAN_ROBOT))
|
|
temp_targets += parent
|
|
if(length(temp_targets))
|
|
infect_target_external = get_infect_target(temp_targets)
|
|
|
|
else
|
|
if(prob(25) || !infect_target_external.is_infected()) //Increase steadily until sufficiently infected, then only bump the germ level now and then
|
|
infect_target_external.germ_level++ //slowly increase the infection level
|
|
//Check to see if the other organ is as infected or more infected than us. If so, we need to get a new target on the next process
|
|
if(infect_target_external.germ_level >= min(germ_level, INFECTION_LEVEL_TWO))
|
|
infect_target_external = null
|
|
|
|
if(germ_level >= INFECTION_LEVEL_THREE && antibiotics < 20) //overdosing is necessary to stop severe infections
|
|
if (!(status & ORGAN_DEAD))
|
|
status |= ORGAN_DEAD
|
|
to_chat(owner, SPAN_NOTICE("You can't feel your [name] anymore..."))
|
|
owner.update_body(1)
|
|
|
|
germ_level++
|
|
owner.adjustToxLoss(1)
|
|
|
|
/obj/item/organ/external/proc/body_part_class()
|
|
return null
|
|
|
|
/obj/item/organ/external/proc/covered_bleed_report(var/blood_type)
|
|
return "[owner.get_pronoun("has")] [blood_type] soaking through the clothes on [owner.get_pronoun("his")] [src]!"
|
|
|
|
//Updating wounds. Handles wound natural I had some free spachealing, internal bleedings and infections
|
|
/obj/item/organ/external/proc/update_wounds()
|
|
if(status & (ORGAN_ROBOT|ORGAN_ADV_ROBOT|ORGAN_PLANT))
|
|
return //Robotic limbs don't heal or get worse. Diona limbs heal using their own mechanic
|
|
|
|
var/updatehud
|
|
var/num_total_wounds = LAZYLEN(wounds)
|
|
for(var/datum/wound/W as anything in wounds)
|
|
// wounds can disappear after 10 minutes at the earliest
|
|
if(W.damage <= 0 && W.created + (10 MINUTES) <= world.time)
|
|
qdel(W)
|
|
continue
|
|
// let the GC handle the deletion of the wound
|
|
|
|
if (W.damage > 0)
|
|
updatehud = TRUE //If there are any wounds with damage to heal, then we'll update health huds
|
|
|
|
// slow healing
|
|
var/heal_amt = 0
|
|
|
|
// if damage >= 50 AFTER treatment then it's probably too severe to heal within the timeframe of a round.
|
|
// Only autoheal if wound was created or enlarged more than 10 seconds ago, this is so damage over time effects don't need to be larger than the autoheal speed
|
|
if (updatehud && brute_ratio < 50 && burn_ratio < 50 && W.can_autoheal())
|
|
if (W.created + HEALING_DELAY <= world.time || W.bandaged || W.salved)
|
|
heal_amt += 0.5
|
|
|
|
//we only update wounds once in [wound_update_accuracy] ticks so have to emulate realtime
|
|
heal_amt *= wound_update_accuracy
|
|
//configurable regen speed woo, no-regen hardcore or instaheal hugbox, choose your destiny
|
|
heal_amt *= GLOB.config.organ_regeneration_multiplier
|
|
// amount of healing is spread over all the wounds
|
|
heal_amt /= ((number_wounds / num_total_wounds) + 1)
|
|
// making it look prettier on scanners
|
|
heal_amt = round(heal_amt, 0.1)
|
|
var/dam_type = W.damage_type == INJURY_TYPE_BURN ? DAMAGE_BURN : DAMAGE_BRUTE
|
|
|
|
if((heal_amt > 0) && owner.can_autoheal(dam_type))
|
|
W.heal_damage(heal_amt)
|
|
|
|
// Salving also helps against infection
|
|
if(W.germ_level > 0 && W.salved && prob(2))
|
|
W.disinfected = TRUE
|
|
W.germ_level = 0
|
|
|
|
// sync the organ's damage with its wounds
|
|
src.update_damages()
|
|
|
|
if (updatehud)
|
|
owner.hud_updateflag = 1022
|
|
|
|
if(update_icon())
|
|
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
|
|
owner.UpdateDamageIcon(1)
|
|
|
|
//Updates brute_damn and burn_damn from wound damages. Updates BLEEDING status.
|
|
/obj/item/organ/external/proc/update_damages()
|
|
number_wounds = 0
|
|
brute_dam = 0
|
|
burn_dam = 0
|
|
var/cut_dam = 0
|
|
status &= ~ORGAN_BLEEDING
|
|
var/clamped = 0
|
|
|
|
var/mob/living/carbon/human/H = astype(owner)
|
|
var/can_bleed = !BP_IS_ROBOTIC(src) && (H && !(H.species.flags & NO_BLOOD))
|
|
|
|
//update damage counts
|
|
for(var/datum/wound/W as anything in wounds)
|
|
|
|
if(W.damage <= 0)
|
|
qdel(W)
|
|
continue
|
|
|
|
if(W.damage_type == INJURY_TYPE_BURN)
|
|
burn_dam += W.damage
|
|
else
|
|
brute_dam += W.damage
|
|
if(W.damage_type == INJURY_TYPE_CUT)
|
|
cut_dam += W.damage
|
|
|
|
if(can_bleed && W.bleeding())
|
|
W.handle_bleeding(H, src)
|
|
|
|
clamped |= W.clamped
|
|
|
|
number_wounds += W.amount
|
|
|
|
//things tend to bleed if they are CUT OPEN
|
|
if (open && !clamped && can_bleed)
|
|
status |= ORGAN_BLEEDING
|
|
|
|
if (tendon)
|
|
tendon.update_damage(cut_dam - min_broken_damage)
|
|
|
|
update_damage_ratios()
|
|
|
|
/obj/item/organ/external/proc/update_damage_ratios()
|
|
var/limb_loss_threshold = max_damage * 2
|
|
brute_ratio = AS_PCT(brute_dam, limb_loss_threshold)
|
|
burn_ratio = AS_PCT(burn_dam, limb_loss_threshold)
|
|
|
|
// new damage icon system
|
|
// returns just the brute/burn damage code
|
|
/obj/item/organ/external/proc/damage_state_text()
|
|
|
|
var/tburn = 0
|
|
var/tbrute = 0
|
|
|
|
if(burn_dam ==0)
|
|
tburn =0
|
|
else if (burn_dam < (max_damage * 0.25 / 2))
|
|
tburn = 1
|
|
else if (burn_dam < (max_damage * 0.75 / 2))
|
|
tburn = 2
|
|
else
|
|
tburn = 3
|
|
|
|
if (brute_dam == 0)
|
|
tbrute = 0
|
|
else if (brute_dam < (max_damage * 0.25 / 2))
|
|
tbrute = 1
|
|
else if (brute_dam < (max_damage * 0.75 / 2))
|
|
tbrute = 2
|
|
else
|
|
tbrute = 3
|
|
return "[tbrute][tburn]"
|
|
|
|
/****************************************************
|
|
DISMEMBERMENT
|
|
****************************************************/
|
|
|
|
/obj/item/organ/external/proc/post_droplimb(mob/living/carbon/human/victim)
|
|
victim.updatehealth()
|
|
victim.UpdateDamageIcon()
|
|
victim.regenerate_icons()
|
|
dir = 2
|
|
|
|
//Handles dismemberment
|
|
/obj/item/organ/external/proc/droplimb(clean, disintegrate = DROPLIMB_EDGE, ignore_children = null)
|
|
if(!(limb_flags & ORGAN_CAN_AMPUTATE) || !owner)
|
|
return
|
|
|
|
switch(disintegrate)
|
|
if(DROPLIMB_EDGE)
|
|
if(!clean)
|
|
var/gore_sound = "[(status & ORGAN_ROBOT) ? "tortured metal" : "ripping tendons and flesh"]"
|
|
owner.visible_message(
|
|
SPAN_DANGER("\The [owner]'s [src.name] flies off in an arc!"),\
|
|
"<span class='moderate'><b><font size=5>Your [src.name] goes flying off!</font></b></span>",\
|
|
SPAN_DANGER("You hear the terrible sound of [gore_sound]."))
|
|
if(DROPLIMB_BURN)
|
|
var/gore = "[(status & ORGAN_ROBOT) ? "": " of burning flesh"]"
|
|
owner.visible_message(
|
|
SPAN_DANGER("\The [owner]'s [src.name] flashes away into ashes!"),\
|
|
"<span class='moderate'><b><font size=5>Your [src.name] flashes away into ashes!</font></b></span>",\
|
|
SPAN_DANGER("You hear the crackling sound[gore]."))
|
|
if(DROPLIMB_BLUNT)
|
|
var/gore = "[(status & ORGAN_ROBOT) ? "": " in a shower of gore"]"
|
|
var/gore_sound = "[(status & ORGAN_ROBOT) ? "rending sound of tortured metal" : "sickening splatter of gore"]"
|
|
owner.visible_message(
|
|
SPAN_DANGER("\The [owner]'s [src.name] explodes[gore]!"),\
|
|
"<span class='moderate'><b><font size=5>Your [src.name] explodes[gore]!</font></b></span>",\
|
|
SPAN_DANGER("You hear the [gore_sound]."))
|
|
|
|
var/mob/living/carbon/human/victim = owner //Keep a reference for post-removed().
|
|
var/obj/item/organ/external/original_parent = parent
|
|
|
|
if(!clean)
|
|
victim.shock_stage += min_broken_damage
|
|
victim.flash_strong_pain()
|
|
|
|
var/mob/living/carbon/human/last_owner = owner
|
|
removed(null, ignore_children)
|
|
if(istype(last_owner) && !QDELETED(last_owner) && length(last_owner.organs) <= 1)
|
|
last_owner.drop_all_limbs(disintegrate) // drops the last remaining part, usually the torso, as an item
|
|
|
|
if(original_parent)
|
|
var/datum/wound/lost_limb/W = new(src, disintegrate, clean)
|
|
var/obj/item/organ/external/damaged_organ = original_parent
|
|
if(!clean)
|
|
var/obj/item/organ/external/stump/stump = new (victim, 0, src)
|
|
stump.add_pain(max_damage)
|
|
damaged_organ = stump
|
|
if(disintegrate != DROPLIMB_BURN)
|
|
stump.sever_artery()
|
|
W.parent_organ = damaged_organ
|
|
LAZYADD(damaged_organ.wounds, W)
|
|
damaged_organ.update_damages()
|
|
|
|
post_droplimb(victim)
|
|
SEND_SIGNAL(victim, COMSIG_LIMB_LOSS)
|
|
|
|
switch(disintegrate)
|
|
if(DROPLIMB_EDGE)
|
|
// compile_icon() used to be here, but it's causing issues, so RIP.
|
|
add_blood(victim)
|
|
var/matrix/M = matrix()
|
|
M.Turn(rand(180))
|
|
src.transform = M
|
|
forceMove(get_turf(src))
|
|
if(!clean)
|
|
//Throw limb around.
|
|
if(src && isturf(loc))
|
|
INVOKE_ASYNC(src, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src,pick(GLOB.alldirs)), rand(1,3), 4)
|
|
dir = 2
|
|
if(DROPLIMB_BURN)
|
|
new /obj/effect/decal/cleanable/ash(get_turf(victim))
|
|
for(var/obj/item/I in src)
|
|
if(I.w_class > 2 && !istype(I,/obj/item/organ))
|
|
I.forceMove(get_turf(src))
|
|
qdel(src)
|
|
if(DROPLIMB_BLUNT)
|
|
var/obj/effect/decal/cleanable/blood/gibs/gore = new victim.species.single_gib_type(get_turf(victim))
|
|
if(victim.species.flesh_color)
|
|
gore.fleshcolor = victim.species.flesh_color
|
|
if(victim.get_blood_color())
|
|
gore.basecolor = victim.get_blood_color()
|
|
gore.update_icon()
|
|
// Tiny amount of blood and not impacted by anything like coagulants, etc- we just want it for effect.
|
|
victim.blood_squirt(2, loc, rand(2,5))
|
|
add_blood(victim)
|
|
|
|
INVOKE_ASYNC(gore, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
|
|
|
|
var/turf/Tloc = get_turf(src)
|
|
for(var/obj/item/I in src)
|
|
I.dropInto(Tloc)
|
|
INVOKE_ASYNC(I, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
|
|
|
|
qdel(src)
|
|
|
|
/****************************************************
|
|
HELPERS
|
|
****************************************************/
|
|
|
|
/obj/item/organ/external/proc/is_stump()
|
|
return FALSE
|
|
|
|
/obj/item/organ/external/proc/release_restraints(var/mob/living/carbon/human/holder)
|
|
if(!holder)
|
|
holder = owner
|
|
if(!holder)
|
|
return
|
|
if (holder.handcuffed && (body_part in list(ARM_LEFT, ARM_RIGHT, HAND_LEFT, HAND_RIGHT)))
|
|
holder.visible_message(\
|
|
"\The [holder.handcuffed.name] falls off of [holder.name].",\
|
|
"\The [holder.handcuffed.name] falls off you.")
|
|
holder.drop_from_inventory(holder.handcuffed)
|
|
if (holder.legcuffed && (body_part in list(FOOT_LEFT, FOOT_RIGHT, LEG_LEFT, LEG_RIGHT)))
|
|
holder.visible_message(\
|
|
"\The [holder.legcuffed.name] falls off of [holder.name].",\
|
|
"\The [holder.legcuffed.name] falls off you.")
|
|
holder.drop_from_inventory(holder.legcuffed)
|
|
|
|
// checks if all wounds on the organ are bandaged
|
|
/obj/item/organ/external/proc/is_bandaged()
|
|
for(var/datum/wound/W as anything in wounds)
|
|
if(!W.bandaged)
|
|
return 0
|
|
return 1
|
|
|
|
// checks if all wounds on the organ are salved
|
|
/obj/item/organ/external/proc/is_salved()
|
|
for(var/datum/wound/W as anything in wounds)
|
|
if(!W.salved)
|
|
return 0
|
|
return 1
|
|
|
|
// checks if all wounds on the organ are disinfected
|
|
/obj/item/organ/external/proc/is_disinfected()
|
|
for(var/datum/wound/W as anything in wounds)
|
|
if(!W.disinfected)
|
|
return 0
|
|
return 1
|
|
|
|
/obj/item/organ/external/proc/bandage()
|
|
var/rval = 0
|
|
status &= ~ORGAN_BLEEDING
|
|
for(var/datum/wound/W as anything in wounds)
|
|
rval |= !W.bandaged
|
|
W.bandage()
|
|
return rval
|
|
|
|
/obj/item/organ/external/proc/salve()
|
|
var/rval = 0
|
|
for(var/datum/wound/W as anything in wounds)
|
|
rval |= !W.salved
|
|
W.salve()
|
|
return rval
|
|
|
|
/obj/item/organ/external/proc/disinfect()
|
|
var/rval = 0
|
|
for(var/datum/wound/W as anything in wounds)
|
|
rval |= !W.disinfected
|
|
W.disinfect()
|
|
W.germ_level = 0
|
|
return rval
|
|
|
|
/obj/item/organ/external/proc/clamp_organ()
|
|
var/rval = 0
|
|
src.status &= ~ORGAN_BLEEDING
|
|
for(var/datum/wound/W as anything in wounds)
|
|
rval |= !W.clamped
|
|
W.clamped = 1
|
|
return rval
|
|
|
|
/// Fractures the bone, so long as it isn't robotic or already broken. When the silent flag is set, no message or sound will be played, and there will be no pain effects
|
|
/obj/item/organ/external/proc/fracture(var/silent = FALSE)
|
|
if(status & ORGAN_ROBOT)
|
|
return //ORGAN_BROKEN doesn't have the same meaning for robot limbs
|
|
if((status & ORGAN_BROKEN) || !(limb_flags & ORGAN_CAN_BREAK))
|
|
return
|
|
if(QDELETED(owner))
|
|
return
|
|
|
|
if(!silent)
|
|
var/message = pick("broke in half", "shattered")
|
|
owner.visible_message(\
|
|
SPAN_WARNING("<font size=4>You hear a loud cracking sound coming from \the [owner]!</font>"),\
|
|
SPAN_DANGER("<font size=5>Something feels like it [message] in your [name]!</font>"),\
|
|
"You hear a sickening crack!")
|
|
if(owner.species && owner.can_feel_pain())
|
|
owner.emote("scream")
|
|
owner.flash_strong_pain()
|
|
playsound(src.loc, SFX_FRACTURE, 100, 1, -2)
|
|
|
|
status |= ORGAN_BROKEN
|
|
broken_description = pick("broken", "fracture", "hairline fracture")
|
|
perma_injury = brute_dam
|
|
|
|
// Fractures have a chance of getting you out of restraints
|
|
if(prob(25))
|
|
release_restraints()
|
|
|
|
// This is mostly for the ninja suit to stop ninja being so crippled by breaks.
|
|
check_rigsplints()
|
|
|
|
/obj/item/organ/external/proc/mend_fracture()
|
|
if(status & ORGAN_ROBOT)
|
|
return 0 //ORGAN_BROKEN doesn't have the same meaning for robot limbs
|
|
if(brute_dam > min_broken_damage * GLOB.config.organ_health_multiplier)
|
|
return 0 //will just immediately fracture again
|
|
|
|
status &= ~ORGAN_BROKEN
|
|
|
|
return 1
|
|
|
|
/obj/item/organ/external/robotize(var/company)
|
|
..()
|
|
|
|
if(company)
|
|
model = company
|
|
var/datum/robolimb/R = GLOB.all_robolimbs[company]
|
|
|
|
if(R)
|
|
if(!force_skintone || override_robotize_force_icon)
|
|
force_icon = override_robotize_force_icon ? override_robotize_force_icon : R.icon
|
|
if(R.lifelike)
|
|
status |= ORGAN_LIFELIKE
|
|
if(force_prosthetic_name)
|
|
name = force_prosthetic_name
|
|
else
|
|
name = "[initial(name)]"
|
|
else
|
|
if(force_prosthetic_name)
|
|
name = force_prosthetic_name
|
|
else
|
|
name = "[R.company] [initial(name)]"
|
|
desc = "[R.desc]"
|
|
if(R.paintable || !isnull(override_robotize_painted))
|
|
painted = !isnull(override_robotize_painted) ? override_robotize_painted : TRUE
|
|
if(R.emissive)
|
|
is_emissive = TRUE
|
|
else
|
|
is_emissive = FALSE
|
|
if(R.overlay)
|
|
is_overlay = TRUE
|
|
else
|
|
is_overlay = FALSE
|
|
if(R.is_tesla)
|
|
is_tesla = TRUE
|
|
else
|
|
is_tesla = FALSE
|
|
brute_mod = R.brute_mod
|
|
burn_mod = R.burn_mod
|
|
robotize_type = company
|
|
augment_limit += 1 //robotic limbs get one extra augment capacity
|
|
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
|
|
|
|
dislocated = -1 //TODO, make robotic limbs a separate type, remove snowflake
|
|
limb_flags &= ~ORGAN_CAN_BREAK
|
|
get_icon()
|
|
unmutate()
|
|
if(robotize_children)
|
|
for (var/obj/item/organ/external/T in children)
|
|
T.robotize(company)
|
|
|
|
/obj/item/organ/external/mechassist()
|
|
..()
|
|
limb_flags |= ORGAN_CAN_BREAK
|
|
|
|
/obj/item/organ/external/proc/robotize_advanced()
|
|
status |= ORGAN_ADV_ROBOT
|
|
for (var/obj/item/organ/external/T in children)
|
|
if (T)
|
|
T.robotize_advanced()
|
|
|
|
/obj/item/organ/external/proc/mutate()
|
|
if(BP_IS_ROBOTIC(src))
|
|
return
|
|
src.status |= ORGAN_MUTATED
|
|
if(owner) owner.update_body()
|
|
|
|
/obj/item/organ/external/proc/unmutate()
|
|
if(!BP_IS_ROBOTIC(src))
|
|
src.status &= ~ORGAN_MUTATED
|
|
if(owner) owner.update_body()
|
|
|
|
/obj/item/organ/external/proc/get_damage() //returns total damage
|
|
return max(brute_dam + burn_dam - perma_injury, perma_injury) //could use max_damage?
|
|
|
|
/obj/item/organ/external/proc/has_infected_wound()
|
|
for(var/datum/wound/W as anything in wounds)
|
|
if(W.germ_level > INFECTION_LEVEL_ONE)
|
|
return 1
|
|
return 0
|
|
|
|
/obj/item/organ/external/is_usable()
|
|
if(is_stump())
|
|
return FALSE
|
|
if(ORGAN_IS_DISLOCATED(src))
|
|
return FALSE
|
|
if(tendon_status() & TENDON_CUT)
|
|
return FALSE
|
|
if(parent && (parent.tendon_status() & TENDON_CUT))
|
|
return FALSE
|
|
if(get_pain() > pain_disability_threshold)
|
|
return FALSE
|
|
if(brute_ratio >= 100)
|
|
return FALSE
|
|
if(burn_ratio >= 100)
|
|
return FALSE
|
|
return ..()
|
|
|
|
/obj/item/organ/external/proc/is_malfunctioning()
|
|
if(BP_IS_ROBOTIC(src) && (brute_ratio + burn_ratio) >= 10 && prob(brute_dam + burn_dam) || (surge_damage > (MAXIMUM_SURGE_DAMAGE * 0.25)))
|
|
return TRUE
|
|
if(robotize_type)
|
|
var/datum/robolimb/R = GLOB.all_robolimbs[robotize_type]
|
|
if(R.malfunctioning_check(owner))
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
#define EMBED_BASE_DAMAGE 5
|
|
|
|
/obj/item/organ/external/proc/embed(obj/item/W, silent = FALSE, supplied_message, datum/wound/supplied_wound)
|
|
if(!owner || loc != owner)
|
|
return
|
|
if(!W.canremove || is_robot_module(W)) //Modules and augments cannot embed
|
|
return
|
|
if(species.flags & NO_EMBED)
|
|
return
|
|
if(!silent)
|
|
if(supplied_message)
|
|
owner.visible_message(SPAN_DANGER("[supplied_message]"))
|
|
else
|
|
owner.visible_message(SPAN_DANGER("\The [W] sticks in [owner]'s wound!"),
|
|
SPAN_DANGER("\The [W] sticks in your wound!"))
|
|
|
|
//Did we not get a supplied wound? Start by finding an existing wound that fits.
|
|
if(!supplied_wound)
|
|
for(var/datum/wound/wound as anything in wounds)
|
|
if ((wound.damage_type == INJURY_TYPE_CUT || wound.damage_type == INJURY_TYPE_PIERCE) && wound.damage >= W.w_class * EMBED_BASE_DAMAGE)
|
|
supplied_wound = wound
|
|
break
|
|
//Nothing still? Make a new wound for this object to embed in.
|
|
if(!supplied_wound)
|
|
supplied_wound = createwound(INJURY_TYPE_PIERCE, W.w_class * EMBED_BASE_DAMAGE)
|
|
|
|
if(!supplied_wound || (W in supplied_wound.embedded_objects)) // Just in case.
|
|
return
|
|
|
|
LAZYADD(supplied_wound.embedded_objects, W)
|
|
implants += W
|
|
owner.embedded_flag = 1
|
|
add_verb(owner, /mob/proc/yank_out_object)
|
|
W.add_blood(owner)
|
|
if(ismob(W.loc))
|
|
var/mob/living/H = W.loc
|
|
H.drop_from_inventory(W,owner)
|
|
W.forceMove(owner)
|
|
|
|
#undef EMBED_BASE_DAMAGE
|
|
|
|
/obj/item/organ/external/removed(var/mob/living/user, var/ignore_children = 0)
|
|
|
|
if(!owner)
|
|
return
|
|
var/is_robotic = BP_IS_ROBOTIC(src)
|
|
var/mob/living/carbon/human/victim = owner
|
|
|
|
..(null, user)
|
|
|
|
victim.bad_external_organs -= src
|
|
|
|
for(var/atom/movable/implant in implants)
|
|
//large items and non-item objs fall to the floor, everything else stays
|
|
var/obj/item/I = implant
|
|
if(istype(I) && I.w_class < WEIGHT_CLASS_NORMAL)
|
|
implant.forceMove(src)
|
|
|
|
// let actual implants still inside know they're no longer implanted
|
|
if(istype(I, /obj/item/implant))
|
|
var/obj/item/implant/imp_device = I
|
|
imp_device.removed()
|
|
else
|
|
implants.Remove(implant)
|
|
implant.forceMove(get_turf(src))
|
|
|
|
// Attached organs also fly off.
|
|
if(!ignore_children)
|
|
for(var/obj/item/organ/external/O in children)
|
|
O.removed()
|
|
if(!QDELETED(O))
|
|
O.forceMove(src)
|
|
// if we didn't lose the organ we still want it as a child
|
|
children += O
|
|
O.parent = src
|
|
|
|
// Grab all the internal giblets too.
|
|
for(var/obj/item/organ/organ in internal_organs)
|
|
organ.removed(user, FALSE, FALSE) // Organ stays inside and connected
|
|
organ.forceMove(src)
|
|
|
|
// Remove parent references
|
|
parent?.children -= src
|
|
parent = null
|
|
|
|
release_restraints(victim)
|
|
victim.organs -= src
|
|
victim.organs_by_name[limb_name] = null // Remove from owner's vars.
|
|
victim.organs_by_name -= organ_tag
|
|
|
|
//Robotic limbs explode if sabotaged.
|
|
if(is_robotic && sabotaged)
|
|
victim.visible_message(
|
|
SPAN_DANGER("\The [victim]'s [src.name] explodes violently!"),\
|
|
SPAN_DANGER("Your [src.name] explodes!"),\
|
|
SPAN_DANGER("You hear an explosion!"))
|
|
explosion(get_turf(owner),-1,-1,2,3)
|
|
spark(victim, 5)
|
|
qdel(src)
|
|
|
|
/obj/item/organ/external/proc/disfigure(var/type = "brute")
|
|
if (disfigured)
|
|
return
|
|
if(owner)
|
|
if(type == "brute")
|
|
owner.visible_message(SPAN_WARNING("You hear a sickening cracking sound coming from \the [owner]'s [name]."), \
|
|
SPAN_DANGER("Your [name] becomes a mangled mess!"), \
|
|
SPAN_WARNING("You hear a sickening crack."))
|
|
else
|
|
owner.visible_message(SPAN_WARNING("\The [owner]'s [name] melts away, turning into a mangled mess!"), \
|
|
SPAN_DANGER("Your [name] melts away!"), \
|
|
SPAN_WARNING("You hear a sickening sizzle."))
|
|
disfigured = 1
|
|
|
|
/obj/item/organ/external/proc/get_wounds_desc()
|
|
. = ""
|
|
if(status & ORGAN_DESTROYED && !is_stump())
|
|
. += "tear at [amputation_point] so severe that it hangs by a scrap of flesh"
|
|
//Handle robotic and synthetic organ damage
|
|
if(status & ORGAN_ASSISTED)
|
|
var/LL = status & ORGAN_LIFELIKE
|
|
if(brute_dam)
|
|
switch(brute_dam)
|
|
if(0 to 20)
|
|
. += "some [LL ? pick ("cuts","bruises","scars") : "dents"]"
|
|
if(21 to INFINITY)
|
|
. += "[LL ? pick("exposed wiring","torn-back synthflesh") : pick("a lot of dents","severe denting")]"
|
|
if(brute_dam && burn_dam)
|
|
. += " and "
|
|
if(burn_dam)
|
|
switch(burn_dam)
|
|
if(0 to 20)
|
|
. += "some [LL ? pick ("fresh skins","burn scars","healing burns") : "burns"]"
|
|
if(21 to INFINITY)
|
|
. += "[LL ? pick("roasted synth-flesh","melted internal wiring") : pick("many burns","scorched metal")]"
|
|
if(open)
|
|
if(brute_dam || burn_dam)
|
|
. += " and "
|
|
if(open == 1)
|
|
. += "some exposed screws"
|
|
else
|
|
. += "an open panel"
|
|
|
|
return
|
|
|
|
var/list/wound_descriptors = list()
|
|
if(open > 1)
|
|
wound_descriptors["an open incision"] = 1
|
|
else if (open)
|
|
wound_descriptors["an incision"] = 1
|
|
for(var/datum/wound/W as anything in wounds)
|
|
var/this_wound_desc = W.desc
|
|
if(W.damage_type == DAMAGE_BURN && W.salved)
|
|
this_wound_desc = "salved [this_wound_desc]"
|
|
if(W.bleeding())
|
|
if(W.wound_damage() > W.bleed_threshold)
|
|
this_wound_desc = "<b>bleeding</b> [this_wound_desc]"
|
|
else
|
|
this_wound_desc = "bleeding [this_wound_desc]"
|
|
else if(W.bandaged == TRUE)
|
|
this_wound_desc = "bandaged [this_wound_desc]"
|
|
if(W.germ_level > 600)
|
|
this_wound_desc = "badly infected [this_wound_desc]"
|
|
else if(W.germ_level > 330)
|
|
this_wound_desc = "lightly infected [this_wound_desc]"
|
|
|
|
if(wound_descriptors[this_wound_desc])
|
|
wound_descriptors[this_wound_desc] += W.amount
|
|
else
|
|
wound_descriptors[this_wound_desc] = W.amount
|
|
|
|
if(open > 1)
|
|
var/bone = encased ? encased : "bone"
|
|
if(status & ORGAN_BROKEN)
|
|
bone = "broken [bone]"
|
|
wound_descriptors["a [bone] exposed"] = 1
|
|
|
|
if(!encased || open > 1)
|
|
var/list/bits = list()
|
|
for(var/obj/item/organ/internal/organ in internal_organs)
|
|
bits += organ.get_visible_state()
|
|
if(length(bits))
|
|
wound_descriptors["[english_list(bits)] visible in the wounds"] = 1
|
|
|
|
if(wound_descriptors.len)
|
|
var/list/flavor_text = list()
|
|
var/list/no_exclude = list("gaping wound", "big gaping wound", "massive wound", "large bruise",\
|
|
"huge bruise", "massive bruise", "severe burn", "large burn", "deep burn", "carbonised area") //note to self make this more robust
|
|
for(var/wound in wound_descriptors)
|
|
switch(wound_descriptors[wound])
|
|
if(1)
|
|
flavor_text += "[prob(10) && !(wound in no_exclude) ? "what might be " : ""]a [wound]"
|
|
if(2)
|
|
flavor_text += "[prob(10) && !(wound in no_exclude) ? "what might be " : ""]a pair of [wound]s"
|
|
if(3 to 5)
|
|
flavor_text += "several [wound]s"
|
|
if(6 to INFINITY)
|
|
flavor_text += "a ton of [wound]\s"
|
|
return english_list(flavor_text)
|
|
|
|
/obj/item/organ/external/listen()
|
|
var/list/sounds = list()
|
|
for(var/obj/item/organ/internal/I in internal_organs)
|
|
var/gutsound = I.listen()
|
|
if(gutsound)
|
|
sounds += gutsound
|
|
if(!sounds.len)
|
|
if(owner.pulse())
|
|
sounds += "faint pulse"
|
|
return sounds
|
|
|
|
/obj/item/organ/external/proc/sever_artery()
|
|
if((status & ORGAN_ROBOT) || (status & ORGAN_ARTERY_CUT) || !species || species.flags & NO_BLOOD || species.flags & NO_ARTERIES)
|
|
return FALSE
|
|
else
|
|
status |= ORGAN_ARTERY_CUT
|
|
return TRUE
|
|
|
|
/obj/item/organ/external/proc/tendon_status()
|
|
if(!(limb_flags & ORGAN_HAS_TENDON))
|
|
return
|
|
|
|
if(!istype(tendon))
|
|
// Somehow this limb should have a tendon but doesn't. Assume it was destroyed
|
|
return TENDON_CUT
|
|
|
|
return tendon.status
|
|
|
|
// Damage procs
|
|
/obj/item/organ/external/proc/get_brute_damage()
|
|
return brute_dam
|
|
|
|
/obj/item/organ/external/proc/get_burn_damage()
|
|
return burn_dam
|
|
|
|
/obj/item/organ/external/proc/get_genetic_damage()
|
|
return BP_IS_ROBOTIC(src) ? 0 : genetic_degradation
|
|
|
|
/obj/item/organ/external/proc/remove_genetic_damage(var/amount)
|
|
if(BP_IS_ROBOTIC(src) || (species.flags & NO_SCAN))
|
|
genetic_degradation = 0
|
|
status &= ~ORGAN_MUTATED
|
|
return
|
|
var/last_gene_dam = genetic_degradation
|
|
genetic_degradation = min(100,max(0,genetic_degradation - amount))
|
|
if(genetic_degradation <= 30)
|
|
if(status & ORGAN_MUTATED)
|
|
unmutate()
|
|
to_chat(src, SPAN_NOTICE("Your [name] is shaped normally again."))
|
|
return -(genetic_degradation - last_gene_dam)
|
|
|
|
/obj/item/organ/external/proc/add_genetic_damage(var/amount)
|
|
if(BP_IS_ROBOTIC(src) || (species.flags & NO_SCAN))
|
|
genetic_degradation = 0
|
|
status &= ~ORGAN_MUTATED
|
|
return
|
|
var/last_gene_dam = genetic_degradation
|
|
genetic_degradation = min(100,max(0,genetic_degradation + amount))
|
|
if(genetic_degradation > 10)
|
|
owner.infest_with_parasite(owner, BP_TUMOUR_SPREADING, src)
|
|
if(genetic_degradation > 20)
|
|
if(!(status & ORGAN_MUTATED) && prob(genetic_degradation))
|
|
mutate()
|
|
to_chat(owner, SPAN_NOTICE("Something is not right with your [name]..."))
|
|
return (genetic_degradation - last_gene_dam)
|
|
|
|
// Pain/halloss
|
|
/obj/item/organ/external/proc/get_pain()
|
|
if(!ORGAN_CAN_FEEL_PAIN(src) || BP_IS_ROBOTIC(src))
|
|
return 0
|
|
. = pain + 0.7 * brute_dam + 0.8 * burn_dam + 0.5 * get_genetic_damage()
|
|
if(is_broken())
|
|
. += 10
|
|
else if(ORGAN_IS_DISLOCATED(src))
|
|
. += 5
|
|
for(var/obj/item/organ/internal/I as anything in internal_organs)
|
|
. += 0.3 * I.getToxLoss()
|
|
|
|
/obj/item/organ/external/proc/remove_pain(var/amount)
|
|
if(!ORGAN_CAN_FEEL_PAIN(src))
|
|
pain = 0
|
|
return
|
|
var/last_pain = pain
|
|
pain = max(0, min(species.total_health * 2, pain - amount))
|
|
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
|
|
return -(pain-last_pain)
|
|
|
|
/obj/item/organ/external/proc/add_pain(var/amount)
|
|
if(!ORGAN_CAN_FEEL_PAIN(src))
|
|
pain = 0
|
|
return
|
|
var/last_pain = pain
|
|
if(owner)
|
|
amount *= owner.species.pain_mod
|
|
if(HAS_TRAIT(owner, TRAIT_ORIGIN_PAIN_RESISTANCE))
|
|
amount = max(amount-1, 1)
|
|
amount -= (owner.chem_effects[CE_PAINKILLER]/3)
|
|
if(amount <= 0)
|
|
return
|
|
pain = max(0, min(pain + amount, species.total_health * 2))
|
|
if(owner && ((amount > 15 && prob(20)) || (amount > 30 && prob(60))))
|
|
owner.emote("scream")
|
|
if(amount > 5 && owner)
|
|
owner.undo_srom_pull()
|
|
SEND_SIGNAL(src, COMSIG_UPDATE_LIMB_IMAGE)
|
|
return pain-last_pain
|
|
|
|
/mob/living/carbon/human/proc/undo_srom_pull()
|
|
if(srom_pulled_by)
|
|
to_chat(src, SPAN_DANGER("You are ripped out of the Srom by a sudden shock!"))
|
|
var/mob/living/carbon/human/srom_puller = srom_pulling.resolve()
|
|
srom_puller.srom_pulling = null
|
|
to_chat(srom_puller, SPAN_WARNING("A vibration like a jackhammer resonates in your consciousness, and the person you pulled into the Srom disappears in the next instant."))
|
|
srom_pulled_by = null
|
|
if(srom_pulling)
|
|
to_chat(src, SPAN_DANGER("Your Srom pull is disturbed by a sudden shock!"))
|
|
var/mob/living/carbon/human/victim = srom_pulling.resolve()
|
|
victim.srom_pulled_by = null
|
|
srom_pulling = null
|