Files
CHOMPStation2/code/modules/organs/organ_external.dm
CHOMPStation2StaffMirrorBot f72988f992 [MIRROR] More surgery tweaks and fixes (#12054)
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-11-28 00:07:28 -05:00

1520 lines
54 KiB
Plaintext

/****************************************************
EXTERNAL ORGANS
****************************************************/
//These control the damage thresholds for the various ways of removing limbs
/// <summary>
/// Arms and legs have 80 damage, which is a good baseline to go off of.
/// The droplimb_threshold is "Divide the limb's max health by this number"
/// That is the damage required (in ONE hit) to tear off or destroy a limb.
/// If the damage dealt per hit is below that, it can NOT remove limbs.
/// </summary>
#define DROPLIMB_THRESHOLD_EDGE 5 //For limb of 80(arm/leg) requires 16 or more damage to cut off.
#define DROPLIMB_THRESHOLD_DESTROY 3.34 //Requires 24 damage or more to DESTROY a arm/leg with a blunt object. Blunt is going to DESTROY over just knocking something off!
/obj/item/organ/external
name = "external"
min_broken_damage = 60 // CHOMPEdit: Flat doubling of all min_broken_damage
max_damage = 0
dir = SOUTH
organ_tag = "limb"
var/brokenpain = 50 //CHOMPEdit
// Strings
var/broken_description // fracture string if any.
var/damage_state = "00" // Modifier used for generating the on-mob damage overlay for this limb.
// Damage vars.
var/brute_mod = 1 // Multiplier for incoming brute damage.
var/burn_mod = 1 // As above for burn.
var/brute_dam = 0 // Actual current brute damage.
var/burn_dam = 0 // Actual current burn damage.
var/last_dam = -1 // used in healing/processing calculations.
var/spread_dam = 0
// Appearance vars.
var/nonsolid // Snowflake warning, reee. Used for slime limbs.
var/transparent // As above, so below. Used for transparent limbs.
var/icon_name = null // Icon state base.
var/body_part = null // Part flag
var/icon_position = 0 // Used in mob overlay layering calculations.
var/model // Used when caching robolimb icons.
var/force_icon // Used to force override of species-specific limb icons (for prosthetics). Also used for any limbs chopped from a simple mob, and then attached to humans.
var/icon/mob_icon // Cached icon for use in mob overlays.
var/gendered_icon = 0 // Whether or not the icon state appends a gender.
var/s_tone // Skin tone.
var/list/s_col // skin colour
var/s_col_blend = ICON_ADD // How the skin colour is applied.
var/list/h_col // hair colour
var/body_hair // Icon blend for body hair if any.
var/mob/living/applied_pressure
var/list/markings = list() // Markings (body_markings) to apply to the icon
var/skip_robo_icon = FALSE //to force it to use the normal species icon
var/digi_prosthetic = FALSE //is it a prosthetic that can be digitigrade
// Wound and structural data.
var/wound_update_accuracy = 1 // how often wounds should be updated, a higher number means less often
var/list/wounds = list() // wound datum list.
var/number_wounds = 0 // number of wounds, which is NOT wounds.len!
var/obj/item/organ/external/parent // Master-limb.
var/list/children = list() // Sub-limbs.
var/list/internal_organs = list() // Internal organs of this body part
var/sabotaged = 0 // If a prosthetic limb is emagged, it will detonate when it fails.
var/list/implants = list() // Currently implanted objects.
var/organ_rel_size = 25 // Relative size of the organ.
var/base_miss_chance = 20 // Chance of missing.
var/atom/movable/splinted
// Joint/state stuff.
var/can_grasp // It would be more appropriate if these two were named "affects_grasp" and "affects_stand" at this point
var/can_stand // Modifies stance tally/ability to stand.
var/disfigured = 0 // Scarred/burned beyond recognition.
var/cannot_amputate // Impossible to amputate.
var/cannot_break // Impossible to fracture.
var/cannot_gib // Impossible to gib, distinct from amputation.
var/joint = "joint" // Descriptive string used in dislocation.
var/amputation_point // Descriptive string used in amputation.
var/dislocated = 0 // If you target a joint, you can dislocate the limb, impairing it's usefulness and causing pain
var/encased // Needs to be opened with a saw to access the organs.
// Surgery vars.
var/open = 0
var/stage = 0
var/cavity = 0
var/burn_stage = 0 //Surgical repair stage for burn.
var/brute_stage = 0 //Surgical repair stage for brute.
var/remove_necrosis = 0 //Surgical stage for necrosis removal.
// HUD element variable, see organ_icon.dm get_damage_hud_image()
var/image/hud_damage_image
/obj/item/organ/external/Destroy()
if(parent && parent.children)
parent.children -= src
parent = null
if(wounds)
QDEL_LIST(wounds)
if(children)
for(var/obj/item/organ/external/C in children)
children -= C
C.parent = null
qdel(C)
if(internal_organs)
for(var/obj/item/organ/O in internal_organs)
internal_organs -= O
if(isobj(O))
qdel(O)
if(splinted && splinted.loc == src)
splinted.loc = null
qdel(splinted)
splinted = null
if(istype(owner))
owner.organs -= src
owner.organs_by_name[organ_tag] = null
owner.organs_by_name -= organ_tag
while(null in owner.organs)
owner.organs -= null
for(var/obj/item/implant/I as anything in implants)
if(!istype(I))
continue
I.imp_in = I.part = null
implants.Cut()
return ..()
/obj/item/organ/external/emp_act(severity, recursive)
for(var/obj/O as anything in src.contents)
O.emp_act(severity, recursive)
if(!(robotic >= ORGAN_ROBOT))
return
var/burn_damage = 0
switch (severity)
if (1)
burn_damage += rand(5, 8)
if (2)
burn_damage += rand(4, 6)
if(3)
burn_damage += rand(2, 5)
if(4)
burn_damage += rand(1, 3)
if(burn_damage)
take_damage(0, burn_damage)
/obj/item/organ/external/attack_self(var/mob/living/user)
if(!contents.len)
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(removable_objects.len)
var/obj/item/I = pick(removable_objects)
I.loc = get_turf(user) //just in case something was embedded that is not an item
if(istype(I))
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/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
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/W as obj, mob/living/user as mob)
switch(stage)
if(0)
if(istype(W,/obj/item/surgical/scalpel))
user.visible_message(span_danger(span_bold("[user]") + " cuts [src] open with [W]!"))
stage++
return
if(1)
if(istype(W,/obj/item/surgical/retractor))
user.visible_message(span_danger(span_bold("[user]") + " cracks [src] open like an egg with [W]!"))
stage++
return
if(2)
if(istype(W,/obj/item/surgical/hemostat))
if(contents.len)
var/obj/item/removing = pick(contents)
removing.loc = get_turf(user.loc)
user.put_in_hands(removing)
user.visible_message(span_danger(span_bold("[user]") + " extracts [removing] from [src] with [W]!"))
else
user.visible_message(span_danger(span_bold("[user]") + " fishes around fruitlessly in [src] with [W]."))
return
..()
/obj/item/organ/external/proc/is_dislocated()
if(dislocated > 0)
return 1
if(is_parent_dislocated())
return 1//if any parent is dislocated, we are considered dislocated as well
return 0
/obj/item/organ/external/proc/is_parent_dislocated()
var/obj/item/organ/external/O = parent
while(O && O.dislocated != -1)
if(O.dislocated == 1)
return 1
O = O.parent
return 0
//new function to check for markings
/obj/item/organ/external/proc/is_hidden_by_markings()
for(var/M in markings)
if(!markings[M]["on"]) //If the marking is off, the organ isn't hidden by it.
continue
var/datum/sprite_accessory/marking/mark_style = markings[M]["datum"]
if(istype(mark_style,/datum/sprite_accessory/marking) && (organ_tag in mark_style.hide_body_parts))
return 1
/obj/item/organ/external/proc/dislocate()
if(dislocated == -1)
return
dislocated = 1
if(istype(owner))
add_verb(owner, /mob/living/carbon/human/proc/relocate)
/obj/item/organ/external/proc/relocate()
if(dislocated == -1)
return
dislocated = 0
if(istype(owner))
owner.shock_stage += 20
//check to see if we still need the verb
for(var/obj/item/organ/external/limb in owner.organs)
if(limb.dislocated == 1)
return
remove_verb(owner, /mob/living/carbon/human/proc/relocate)
/obj/item/organ/external/update_health()
damage = min(max_damage, (brute_dam + burn_dam))
/obj/item/organ/external/Initialize(mapload, var/internal)
..(mapload, 0)
if(istype(owner))
replaced(owner)
sync_colour_to_human(owner)
return INITIALIZE_HINT_LATELOAD
/obj/item/organ/external/LateInitialize()
if(!QDELETED(src))
get_icon()
/obj/item/organ/external/replaced(var/mob/living/carbon/human/target)
owner = target
forceMove(owner)
if(istype(owner))
owner.organs_by_name[organ_tag] = src
owner.organs |= src
for(var/obj/item/organ/organ in src)
organ.replaced(owner,src)
owner.refresh_modular_limb_verbs()
if(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)
parent.wounds -= W
qdel(W)
break
parent.update_damages()
/****************************************************
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 (vital || (robotic >= ORGAN_ROBOT) || brute_dam + burn_dam + additional_damage < max_damage)
/obj/item/organ/external/proc/is_fracturable()
if(robotic >= ORGAN_ROBOT)
return FALSE //ORGAN_BROKEN doesn't have the same meaning for robot limbs
if((status & ORGAN_BROKEN) || cannot_break)
return FALSE
return TRUE
/obj/item/organ/external/take_damage(brute, burn, sharp, edge, used_weapon = null, list/forbidden_limbs = list(), permutation = FALSE, projectile)
if(owner)
if(SEND_SIGNAL(owner, COMSIG_EXTERNAL_ORGAN_PRE_DAMAGE_APPLICATION, brute, burn, sharp, edge, used_weapon, forbidden_limbs, permutation, projectile) & COMPONENT_CANCEL_EXTERNAL_ORGAN_DAMAGE)
return 0 // If the signal returns, we don't apply damage, it's done its own special thing.
brute = round(brute * brute_mod, 0.1)
burn = round(burn * burn_mod, 0.1)
if((brute <= 0) && (burn <= 0))
return 0
//This tells us how damaged we are prior to this attack.
var/prior_damage = brute_dam + burn_dam
// High brute damage or sharp objects may damage internal organs
if(internal_organs && (brute_dam >= max_damage || (((sharp && brute >= 5) || brute >= 10) && prob(5))))
// Damage an internal organ
if(internal_organs && internal_organs.len)
var/obj/item/organ/I = pick(internal_organs)
brute *= 0.5
I.take_damage(brute)
if(status & ORGAN_BROKEN && brute)
jostle_bone(brute)
if(organ_can_feel_pain() && prob(40) && !isbelly(owner.loc) && !istype(owner.loc, /obj/item/dogborg/sleeper))
owner.emote("scream") //getting hit on broken hand hurts
if(used_weapon)
add_autopsy_data("[used_weapon]", brute + burn)
var/can_cut = (sharp) && (robotic < ORGAN_ROBOT)
// If the limbs can break, make sure we don't exceed the maximum damage a limb can take before breaking
// Non-vital organs are limited to max_damage. You can't kill someone by bludeonging their arm all the way to 200 -- you can
// push them faster into paincrit though, as the additional damage is converted into shock.
var/brute_overflow = 0
var/burn_overflow = 0
if(is_damageable(brute + burn) || !CONFIG_GET(flag/limbs_can_break))
if(brute)
if(can_cut)
if(sharp && !edge)
createwound( PIERCE, brute )
else
createwound( CUT, brute )
else
createwound( BRUISE, brute )
if(burn)
createwound( BURN, burn )
else
//If we can't inflict the full amount of damage, spread the damage in other ways
//How much damage can we actually cause?
var/can_inflict = max_damage * CONFIG_GET(number/organ_health_multiplier) - (brute_dam + burn_dam)
var/spillover = 0
if(can_inflict)
if (brute > 0)
//Inflict all burte damage we can
if(can_cut)
if(sharp && !edge)
createwound( PIERCE, min(brute,can_inflict) )
else
createwound( CUT, min(brute,can_inflict) )
else
createwound( BRUISE, min(brute,can_inflict) )
//How much more damage can we inflict
brute_overflow = max(0, brute - can_inflict)
//How much brute damage is left to inflict
spillover += max(0, brute - can_inflict)
can_inflict = max_damage * CONFIG_GET(number/organ_health_multiplier) - (brute_dam + burn_dam) //Refresh the can_inflict var, so burn doesn't overload the limb if it is set to take both.
if (burn > 0 && can_inflict)
//Inflict all burn damage we can
createwound(BURN, min(burn,can_inflict))
//How much burn damage is left to inflict
burn_overflow = max(0, burn - can_inflict)
spillover += burn_overflow
//If there is pain to dispense.
if(spillover)
owner.shock_stage += spillover * CONFIG_GET(number/organ_damage_spillover_multiplier)
// sync the organ's damage with its wounds
src.update_damages()
if(owner)
owner.updatehealth() //droplimb will call updatehealth() again if it does end up being called
//If limb took enough damage, try to cut or tear it off
if(owner && loc == owner && !is_stump())
/// <summary>
/// This determines if the limb is ELIGIBLE to be chopped off or not.
/// It checks if it's amputatable, if the config setting is set, then continues down the proc.
/// </summary>
if(!cannot_amputate && CONFIG_GET(flag/limbs_can_break))
// By default, limbs aren't knocked off unless certain criteria is met.
var/destruction_eligible = FALSE
// These are adjusted in case we get hit with a projectile.
// Projectiles have suffered MASSIVE damage creep and as a result, throw ALL the numbers off.
// This code was - primarily - intended for melee weapons, which have MUCH lower numbers.
var/modifed_brute = brute
var/modifed_burn = burn
// Let's calculate how INJURED our limb is accounting for AFTER the damage we just took. Determines the chance the next attack will take our limb off!
var/damage_factor = ((brute_dam + burn_dam)/(max_damage*CONFIG_GET(number/organ_health_multiplier)))*100
if(brute_dam > max_damage || burn_dam > max_damage) //This is in case we go OVER our max. This doesn't EVER happen except on VITAL organs.
damage_factor = 100
// Max_damage of 80 and brute_dam of 80? || Factor = 100 Max_damage of 80 and brute_dam of 40? Factor = 50 || Max_damage of 80 and brute_dam of 5? Factor = 5
// This lowers our chances of having our limb removed when it has less damage. The more damaged the limb, the higher the chance it falls off!
//Check edge eligibility
var/edge_eligible = FALSE
if(edge)
if(istype(used_weapon,/obj/item))
var/obj/item/W = used_weapon
if(W.w_class >= w_class)
edge_eligible = 1
else
edge_eligible = 1
// Due to the afformentioned damage creep, projectile damage is halved.
// UNLESS The projectile does over the limb's max damage in the first place, then you're in danger of it going bye bye.
if(projectile && (brute + burn) < max_damage)
modifed_brute = modifed_brute/2
modifed_burn = modifed_burn/2
// Vital organs have a lower chance of getting causing removals AND require much higher damaging attacks.
// For reference, the head has 75 max_damage.
if(vital)
modifed_brute = modifed_brute/1.5
modifed_burn = modifed_burn/1.5
// So, limbs have this issue where "If my limb is already at max damage, it doesn't matter how much more damage it takes, it won't go up"
// While that is FINE, there should be some benefit to repeatedly hitting the same limb when it's aleady maxed out. Thus, time comes in.
if(prior_damage >= max_damage)
modifed_brute = modifed_brute*3
modifed_burn = modifed_burn*3
// Our limb has OVER 1/3 it's max health in damage already, we are eligible for removal.
if(prior_damage > max_damage/3)
destruction_eligible = TRUE
// If an attack is doing OVER half our max damage in ONE hit, we are eligible for removal.
else if((modifed_brute + modifed_burn) > max_damage/2)
destruction_eligible = TRUE
if(destruction_eligible)
if(nonsolid && damage >= max_damage)
droplimb(TRUE, DROPLIMB_EDGE)
else if (robotic >= ORGAN_NANOFORM && damage >= max_damage)
droplimb(TRUE, DROPLIMB_BURN)
//Hit with a sharp object.
else if(edge_eligible && modifed_brute >= max_damage / DROPLIMB_THRESHOLD_EDGE && prob(modifed_brute*0.15) && prob(damage_factor))
droplimb(FALSE, DROPLIMB_EDGE)
//Hit with burn. Such as by getting shocked by a door.
else if((modifed_burn >= max_damage / DROPLIMB_THRESHOLD_DESTROY) && prob(modifed_burn*0.75) && prob(damage_factor))
droplimb(FALSE, DROPLIMB_BURN)
//These are the 'Hit with a big, blunt weapon.' Sharp objects don't get to do this. Walls do enough to do this if someone gets thrown against it enough times.
else if((!edge_eligible && modifed_brute >= max_damage / DROPLIMB_THRESHOLD_DESTROY && prob(modifed_brute*0.25)) && prob(damage_factor))
droplimb(FALSE, DROPLIMB_BLUNT)
else if(spread_dam && owner && parent && (brute_overflow || burn_overflow) && (brute_overflow >= 5 || burn_overflow >= 5) && !permutation) //No infinite damage loops.
var/brute_third = brute_overflow * 0.33
var/burn_third = burn_overflow * 0.33
if(children && children.len)
var/brute_on_children = brute_third / children.len
var/burn_on_children = burn_third / children.len
spawn()
for(var/obj/item/organ/external/C in children)
if(!C.is_stump())
C.take_damage(brute_on_children, burn_on_children, FALSE, FALSE, null, forbidden_limbs, 1) //Splits the damage to each individual 'child', incase multiple exist.
parent.take_damage(brute_third, burn_third, FALSE, FALSE, null, forbidden_limbs, 1)
if(owner)
SEND_SIGNAL(owner, COMSIG_EXTERNAL_ORGAN_POST_DAMAGE_APPLICATION, brute, burn, sharp, edge, used_weapon, forbidden_limbs, permutation, projectile)
return update_icon()
/obj/item/organ/external/proc/heal_damage(brute, burn, internal = FALSE, robo_repair = FALSE)
if(robotic >= ORGAN_ROBOT && !robo_repair)
return
//Heal damage on the individual wounds
for(var/datum/wound/W in wounds)
if(brute == 0 && burn == 0)
break
// heal brute damage
if(W.damage_type == BURN)
burn = W.heal_damage(burn)
else
brute = W.heal_damage(brute)
if(internal)
status &= ~ORGAN_BROKEN
//Sync the organ's damage with its wounds
src.update_damages()
src.update_wounds()
owner.updatehealth()
var/result = update_icon()
return result
//Helper proc used by various tools for repairing robot limbs
/obj/item/organ/external/proc/robo_repair(var/repair_amount, var/damage_type, var/damage_desc, obj/item/tool, mob/living/user)
if((src.robotic < ORGAN_ROBOT))
return 0
var/damage_amount
switch(damage_type)
if(BRUTE) damage_amount = brute_dam
if(BURN) damage_amount = burn_dam
if("omni") damage_amount = max(brute_dam,burn_dam)
else return 0
if(!damage_amount)
to_chat(user, span_notice("Nothing to fix!"))
return 0
if(brute_dam + burn_dam >= min_broken_damage) //VOREStation Edit - Makes robotic limb damage scalable
to_chat(user, span_danger("The damage is far too severe to patch over externally."))
return 0
if(user == src.owner)
var/grasp
if(user.l_hand == tool && (src.body_part & (ARM_LEFT|HAND_LEFT)))
grasp = BP_L_HAND
else if(user.r_hand == tool && (src.body_part & (ARM_RIGHT|HAND_RIGHT)))
grasp = BP_R_HAND
if(grasp)
to_chat(user, span_warning("You can't reach your [src.name] while holding [tool] in your [owner.get_bodypart_name(grasp)]."))
return 0
user.setClickCooldown(user.get_attack_speed(tool))
if(!do_after(user, 1 SECOND, src))
to_chat(user, span_warning("You must stand still to do that."))
return 0
switch(damage_type)
if(BRUTE) src.heal_damage(repair_amount, 0, 0, 1)
if(BURN) src.heal_damage(0, repair_amount, 0, 1)
if("omni")src.heal_damage(repair_amount, repair_amount, 0, 1)
if(damage_desc)
var/fix_verb = (damage_amount > repair_amount) ? "patches" : "finishes patching"
if(user == src.owner)
user.visible_message(span_infoplain(span_bold("\The [user]") + " [fix_verb] [damage_desc] on [user.p_their()] [src.name] with [tool]."))
else
user.visible_message(span_infoplain(span_bold("\The [user]") + " [fix_verb] [damage_desc] on [owner]'s [src.name] with [tool]."))
return 1
/*
This function completely restores a damaged organ to perfect condition.
*/
/obj/item/organ/external/rejuvenate(var/ignore_prosthetic_prefs)
damage_state = "00"
status = 0
brute_dam = 0
burn_dam = 0
germ_level = 0
wounds.Cut()
number_wounds = 0
// handle internal organs
for(var/obj/item/organ/current_organ in internal_organs)
current_organ.rejuvenate(ignore_prosthetic_prefs)
// remove embedded objects and drop them on the floor
for(var/obj/implanted_object in implants)
if(istype(implanted_object,/obj/item/implant) || istype(implanted_object,/obj/item/nif)) // We don't want to remove REAL implants. Just shrapnel etc. //VOREStation Edit - NIFs pls
continue
implanted_object.loc = get_turf(src)
implants -= implanted_object
if(!owner.has_embedded_objects())
owner.clear_alert("embeddedobject")
if(owner && !ignore_prosthetic_prefs)
if(owner.client && owner.client.prefs && owner.client.prefs.real_name == owner.real_name)
var/status = owner.client.prefs.organ_data[organ_tag]
if(status == "amputated")
remove_rejuv()
else if(status == "cyborg")
var/robodata = owner.client.prefs.rlimb_data[organ_tag]
if(robodata)
robotize(robodata)
else
robotize()
owner.updatehealth()
/obj/item/organ/external/remove_rejuv()
if(owner)
owner.organs -= src
owner.organs_by_name[organ_tag] = null
owner.organs_by_name -= organ_tag
while(null in owner.organs) owner.organs -= null
if(children && children.len)
for(var/obj/item/organ/external/E in children)
E.remove_rejuv()
children.Cut()
for(var/obj/item/organ/internal/I in internal_organs)
I.remove_rejuv()
..()
/obj/item/organ/external/proc/createwound(var/type = CUT, var/damage)
if(damage == 0) 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
if((damage > 15) && (type != BURN) && (local_damage > 30) && prob(damage) && (robotic < ORGAN_ROBOT) && !(data.get_species_flags() & NO_BLOOD))
var/datum/wound/internal_bleeding/I = new (min(damage - 15, 15))
wounds += I
owner.custom_pain("Something ruptures inside of your [name]. You get the feeling you'll need more than just a bandage to fix it.", 15, TRUE)
to_chat(owner, span_bolddanger(span_massive("OH GOD! Something just tore in your [name]!"))) //Let's make this CLEAR that an artery was severed. This was vague enough that most players didn't realize they had IB.
//Burn damage can cause fluid loss due to blistering and cook-off
if(owner && (damage > 5 || damage + burn_dam >= 15) && type == BURN && (robotic < ORGAN_ROBOT) && !(data.get_species_flags() & NO_BLOOD))
var/fluid_loss = 0.1 * (damage/(owner.getMaxHealth() - (-owner.getMaxHealth()))) * owner.species.blood_volume*(1 - owner.species.blood_level_fatal) //CHOMPedit reduce fluid loss 4-fold so lasers dont suck your blood
owner.remove_blood(fluid_loss)
// first check whether we can widen an existing wound
if(wounds.len > 0 && prob(max(50+(number_wounds-1)*10,90)))
if((type == CUT || 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 in wounds)
if (W.can_worsen(type, damage))
compatible_wounds += W
if(compatible_wounds.len)
var/datum/wound/W = pick(compatible_wounds)
W.open_wound(damage)
if(prob(25))
if(robotic >= ORGAN_ROBOT)
owner.visible_message(span_danger("The damage to [owner.name]'s [name] worsens."),\
span_danger("The damage to your [name] worsens."),\
span_danger("You hear the screech of abused metal."))
else
owner.visible_message(span_danger("The wound on [owner.name]'s [name] widens with a nasty ripping noise."),\
span_danger("The wound on your [name] widens with a nasty ripping noise."),\
span_danger("You hear a nasty ripping noise, as if flesh is being torn apart."))
return
//Creating wound
var/wound_type = get_wound_type(type, damage)
if(wound_type)
var/datum/wound/W = new wound_type(damage)
//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 = null // to signify that the wound was added
break
if(W)
wounds += 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()
return ((status & ORGAN_CUT_AWAY) || (status & ORGAN_BROKEN) && (!splinted || (splinted && (splinted in src.contents) && prob(30))))
//Determines if we even need to process this organ.
/obj/item/organ/external/proc/need_process()
if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_DEAD|ORGAN_MUTATED))
return 1
if(brute_dam || burn_dam)//VOREStation Edit - But they do for medichines! ---&& (robotic < ORGAN_ROBOT)) //Robot limbs don't autoheal and thus don't need to process when damaged
return 1
if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up.
last_dam = brute_dam + burn_dam
return 1
else
last_dam = brute_dam + burn_dam
if(germ_level)
return 1
return 0
/obj/item/organ/external/process()
if(owner)
// Process wounds, doing healing etc. Only do this every few ticks to save processing power
if(owner.stat != DEAD && 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)
//Infections
update_germs()
else
..()
//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 spaceacillin 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(robotic >= ORGAN_ROBOT || (owner.species && (owner.species.flags & IS_PLANT || (owner.species.flags & NO_INFECT)))) //Robotic limbs shouldn't be infected, nor should nonexistant limbs.
germ_level = 0
return
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()
if(owner && isbelly(owner.loc)) //If we're in a belly, just skip infection spreading. This leads to extended vore scenes killing via infection.
return
var/antibiotics = owner.chem_effects[CE_ANTIBIOTIC]
for(var/datum/wound/W in wounds)
//Open wounds can become infected
if(owner.germ_level > W.germ_level && W.infection_check())
W.germ_level++
if(!antibiotics)
for(var/datum/wound/W in wounds)
//Infected wounds raise the organ's germ level
if (W.germ_level > germ_level)
germ_level++
break //limit increase to a maximum of one per second
/obj/item/organ/external/handle_germ_effects()
. = ..() //May be null or an infection level, if null then no specific processing needed here
if(!.) return
var/antibiotics = owner.chem_effects[CE_ANTIBIOTIC]
if(. >= 2 && antibiotics < ANTIBIO_NORM) //INFECTION_LEVEL_TWO
//spread the infection to internal organs
var/obj/item/organ/target_organ = null //make internal organs become infected one at a time instead of all at once
for (var/obj/item/organ/I in internal_organs)
if (I.germ_level > 0 && I.germ_level < min(germ_level, INFECTION_LEVEL_TWO)) //once the organ reaches whatever we can give it, or level two, switch to a different one
if (!target_organ || I.germ_level > target_organ.germ_level) //choose the organ with the highest germ_level
target_organ = I
if (!target_organ)
//figure out which organs we can spread germs to and pick one at random
var/list/candidate_organs = list()
for (var/obj/item/organ/I in internal_organs)
if (I.germ_level < germ_level)
candidate_organs |= I
if (candidate_organs.len)
target_organ = pick(candidate_organs)
if (target_organ)
target_organ.germ_level++
//spread the infection to child and parent organs
if (children)
for (var/obj/item/organ/external/child in children)
if (child.germ_level < germ_level && (child.robotic < ORGAN_ROBOT))
if (child.germ_level < INFECTION_LEVEL_ONE*2 || prob(30))
child.germ_level++
if (parent)
if (parent.germ_level < germ_level && (parent.robotic < ORGAN_ROBOT))
if (parent.germ_level < INFECTION_LEVEL_ONE*2 || prob(30))
parent.germ_level++
if(. >= 3 && antibiotics < ANTIBIO_OD) //INFECTION_LEVEL_THREE
if (!(status & ORGAN_DEAD))
status |= ORGAN_DEAD
to_chat(owner, span_notice("You can't feel your [name] anymore..."))
owner.update_icons_body()
for (var/obj/item/organ/external/child in children)
child.germ_level += 110 //Burst of infection from a parent organ becoming necrotic
//Updating wounds. Handles wound natural I had some free spachealing, internal bleedings and infections
/obj/item/organ/external/proc/update_wounds()
if((robotic >= ORGAN_ROBOT) || (data.get_species_flags() & UNDEAD)) //Robotic and dead limbs don't heal or get worse.
for(var/datum/wound/W in wounds) //Repaired wounds disappear though
if(W.damage <= 0) //and they disappear right away
wounds -= W //TODO: robot wounds for robot limbs
src.update_damages()
if (update_icon())
owner.UpdateDamageIcon(1)
return
for(var/datum/wound/W in wounds)
// wounds can disappear after 10 minutes at the earliest
if(W.damage <= 0 && W.created + 10 MINUTES <= world.time)
wounds -= W
continue
// let the GC handle the deletion of the wound
// 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.
if (W.can_autoheal() && W.wound_damage() < 50)
heal_amt += 0.5
//we only update wounds once in [wound_update_accuracy] ticks so have to emulate realtime
heal_amt = heal_amt * wound_update_accuracy
//configurable regen speed woo, no-regen hardcore or instaheal hugbox, choose your destiny
heal_amt = heal_amt * CONFIG_GET(number/organ_regeneration_multiplier)
// amount of healing is spread over all the wounds
heal_amt = heal_amt / (wounds.len + 1)
// making it look prettier on scanners
heal_amt = round(heal_amt,0.1)
W.heal_damage(heal_amt)
// Salving also helps against infection
if(W.germ_level > 0 && W.salved && prob(2))
W.disinfected = 1
W.germ_level = 0
// sync the organ's damage with its wounds
src.update_damages()
if (update_icon())
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
status &= ~ORGAN_BLEEDING
var/clamped = 0
var/mob/living/carbon/human/H
if(ishuman(owner))
H = owner
//update damage counts
for(var/datum/wound/W in wounds)
if(!W.internal) //so IB doesn't count towards crit/paincrit
if(W.damage_type == BURN)
burn_dam += W.damage
else
brute_dam += W.damage
if(!(robotic >= ORGAN_ROBOT) && W.bleeding() && (H && H.should_have_organ(O_HEART)) && !(H.species.flags & NO_BLOOD))
W.bleed_timer--
status |= ORGAN_BLEEDING
clamped |= W.clamped
number_wounds += W.amount
//things tend to bleed if they are CUT OPEN
if (open && !clamped && (H && H.should_have_organ(O_HEART)))
status |= ORGAN_BLEEDING
//Bone fractures
if(CONFIG_GET(flag/bones_can_break) && brute_dam > min_broken_damage * CONFIG_GET(number/organ_health_multiplier) && !(robotic >= ORGAN_ROBOT))
src.fracture()
update_health()
// new damage icon system
// adjusted to set damage_state to brute/burn code only (without r_name0 as before)
/obj/item/organ/external/update_icon()
var/n_is = damage_state_text()
if (n_is != damage_state)
damage_state = n_is
return 1
return 0
// 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
****************************************************/
//Handles dismemberment
/obj/item/organ/external/proc/droplimb(var/clean, var/disintegrate = DROPLIMB_EDGE, var/ignore_children = null)
if(cannot_amputate || !owner)
return
//VOREStation Add
if(robotic >= ORGAN_NANOFORM)
disintegrate = DROPLIMB_BURN //Ashes will be fine
else if(disintegrate == DROPLIMB_EDGE && nonsolid) //VOREStation Add End
disintegrate = DROPLIMB_BLUNT //splut
GLOB.lost_limbs_shift_roundstat++
if(owner && !owner.transforming)
switch(disintegrate)
if(DROPLIMB_EDGE)
if(!clean)
var/gore_sound = "[(robotic >= ORGAN_ROBOT) ? "tortured metal" : "ripping tendons and flesh"]"
owner.visible_message(
span_danger("\The [owner]'s [src.name] flies off in an arc!"),\
span_bolddanger("Your [src.name] goes flying off!"),\
span_danger("You hear a terrible sound of [gore_sound]."))
if(DROPLIMB_BURN)
if(cannot_gib)
return
var/gore = "[(robotic >= ORGAN_ROBOT) ? "": " of burning flesh"]"
owner.visible_message(
span_danger("\The [owner]'s [src.name] flashes away into ashes!"),\
span_bolddanger("Your [src.name] flashes away into ashes!"),\
span_danger("You hear a crackling sound[gore]."))
if(DROPLIMB_BLUNT)
if(cannot_gib)
return
var/gore = "[(robotic >= ORGAN_ROBOT) ? "": " in 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_bolddanger("Your [src.name] explodes[gore]!"),\
span_danger("You hear the [gore_sound]."))
if(DROPLIMB_ACID)
if(cannot_gib)
return
var/gore = "[(robotic >= ORGAN_ROBOT) ? "": " in gush of gore"]"
var/gore_sound = "[(status >= ORGAN_ROBOT) ? "sizzling sound of melting metal" : "sickening drips of melting flesh"]"
owner.visible_message(
span_danger("\The [owner]'s [src.name] sloughs off[gore]!"),\
span_bolddanger("<b>Your [src.name] sloughs off of your body[gore]!</b>"),\
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/parent_organ = parent
var/use_flesh_colour = data.get_species_flesh_colour(owner)
var/use_blood_colour = data.get_species_blood_colour(owner)
removed(null, ignore_children)
victim?.shock_stage += 60
if(parent_organ)
var/datum/wound/lost_limb/W = new (src, disintegrate, clean)
if(clean)
parent_organ.wounds |= W
parent_organ.update_damages()
else
var/obj/item/organ/external/stump/stump = new (victim, 0, src)
if(robotic >= ORGAN_ROBOT)
stump.robotize()
stump.wounds |= W
victim.organs |= stump
stump.update_damages()
spawn(1)
if(istype(victim))
victim.updatehealth()
victim.UpdateDamageIcon()
victim.update_icons_body()
else
victim.update_icons()
dir = 2
var/atom/droploc = victim.drop_location()
switch(disintegrate)
if(DROPLIMB_EDGE)
appearance_flags &= ~PIXEL_SCALE
compile_icon()
add_blood(victim)
var/matrix/M = matrix()
M.Turn(rand(180))
src.transform = M
if(!clean)
// Throw limb around.
if(src && istype(loc,/turf))
throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
dir = 2
if(DROPLIMB_BURN)
new /obj/effect/decal/cleanable/ash(droploc)
for(var/obj/item/I in src)
if(I.w_class > ITEMSIZE_SMALL && !istype(I,/obj/item/organ))
I.forceMove(droploc)
qdel(src)
if(DROPLIMB_BLUNT)
var/obj/effect/decal/cleanable/blood/gibs/gore
if(robotic >= ORGAN_ROBOT)
gore = new /obj/effect/decal/cleanable/blood/gibs/robot(droploc)
else
gore = new /obj/effect/decal/cleanable/blood/gibs(droploc)
gore.fleshcolor = use_flesh_colour
gore.basecolor = use_blood_colour
gore.update_icon()
gore.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
for(var/obj/item/organ/I in internal_organs)
I.removed()
if(istype(loc,/turf))
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
for(var/obj/item/I in src)
I.forceMove(droploc)
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5)
qdel(src)
if(DROPLIMB_ACID)
appearance_flags &= ~PIXEL_SCALE
compile_icon()
add_blood(victim)
var/matrix/M = matrix()
M.Turn(rand(180))
transform = M
if(victim.l_hand)
if(istype(victim.l_hand,/obj/item/material/twohanded)) //if they're holding a two-handed weapon, drop it now they've lost a hand
victim.l_hand.update_held_icon()
if(victim.r_hand)
if(istype(victim.r_hand,/obj/item/material/twohanded))
victim.r_hand.update_held_icon()
/****************************************************
HELPERS
****************************************************/
/obj/item/organ/external/proc/is_stump()
return 0
/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 in wounds)
if(W.internal) continue
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 in wounds)
if(W.internal) continue
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 in wounds)
if(W.internal) continue
if(!W.disinfected)
return 0
return 1
/obj/item/organ/external/proc/bandage()
var/rval = 0
status &= ~ORGAN_BLEEDING
for(var/datum/wound/W in wounds)
if(W.internal) continue
rval |= !W.bandaged
W.bandaged = 1
return rval
/obj/item/organ/external/proc/salve()
var/rval = 0
for(var/datum/wound/W in wounds)
rval |= !W.salved
W.salved = 1
return rval
/obj/item/organ/external/proc/disinfect()
var/rval = 0
for(var/datum/wound/W in wounds)
if(W.internal) continue
rval |= !W.disinfected
W.disinfected = 1
W.germ_level = 0
return rval
/obj/item/organ/external/proc/organ_clamp()
var/rval = 0
src.status &= ~ORGAN_BLEEDING
for(var/datum/wound/W in wounds)
if(W.internal) continue
rval |= !W.clamped
W.clamped = 1
return rval
/obj/item/organ/external/proc/fracture()
if(robotic >= ORGAN_ROBOT)
return //ORGAN_BROKEN doesn't have the same meaning for robot limbs
if((status & ORGAN_BROKEN) || cannot_break)
return
if(owner)
var/show_message = TRUE
var/scream = TRUE
if(!organ_can_feel_pain() || owner.transforming)
show_message = FALSE
scream = FALSE
if(isbelly(owner.loc) || isliving(owner.loc))
scream = FALSE
if(!owner.digest_pain)
show_message = FALSE
if(show_message)
// CHOMPEdit Start
owner.custom_pain(pick(\
span_danger("You hear a loud cracking sound coming from \the [owner]."),\
span_danger("Something feels like it shattered in your [name]!"),\
span_danger("You hear a sickening crack.")), brokenpain)
// CHOMPEdit End
if(scream)
owner.emote("scream")
jostle_bone()
if(istype(owner.loc, /obj/belly)) //CHOMPedit, bone breaks in bellys should be whisper range to prevent bar wide blender prefbreak. This is a hacky passive hardcode, if a pref gets added, remove this if else
playsound(src, "fracture", 90, 1, -6.5)
else
playsound(src, "fracture", 90, 1, -2) // CHOMPedit: Much more audible bonebreaks.
status |= ORGAN_BROKEN
broken_description = pick("broken","fracture","hairline fracture")
// 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.
// TODO: consider moving this to a suit proc or process() or something during
// hardsuit rewrite.
if(!(splinted) && owner && istype(owner.wear_suit, /obj/item/clothing/suit/space))
var/obj/item/clothing/suit/space/suit = owner.wear_suit
suit.handle_fracture(owner, src)
return 1
/obj/item/organ/external/proc/mend_fracture()
if(robotic >= ORGAN_ROBOT)
return 0 //ORGAN_BROKEN doesn't have the same meaning for robot limbs
if(brute_dam > min_broken_damage * CONFIG_GET(number/organ_health_multiplier))
return 0 //will just immediately fracture again
status &= ~ORGAN_BROKEN
return 1
/obj/item/organ/external/proc/apply_splint(var/atom/movable/splint)
if(!splinted)
splinted = splint
if(!applied_pressure)
applied_pressure = splint
return 1
return 0
/obj/item/organ/external/proc/remove_splint()
if(splinted)
if(splinted.loc == src)
splinted.dropInto(owner? owner.loc : src.loc)
if(applied_pressure == splinted)
applied_pressure = null
splinted = null
return 1
return 0
/obj/item/organ/external/robotize(var/company, var/skip_prosthetics = 0, var/keep_organs = 0)
if(robotic >= ORGAN_ROBOT)
return
..()
if(company)
model = company
var/datum/robolimb/R = GLOB.all_robolimbs[company]
if(!R || (data.get_species_name() in R.species_cannot_use))
R = GLOB.basic_robolimb
if(R)
force_icon = R.icon
brute_mod *= R.robo_brute_mod
burn_mod *= R.robo_burn_mod
skip_robo_icon = R.no_icon //CHOMPStation edit
digi_prosthetic = R.can_be_digitigrade //CHOMPStation edit
if(R.lifelike)
robotic = ORGAN_LIFELIKE
name = "[initial(name)]"
else if(R.modular_bodyparts == MODULAR_BODYPART_PROSTHETIC)
name = "prosthetic [initial(name)]"
else
name = "robotic [initial(name)]"
desc = "[R.desc] It looks like it was produced by [R.company]."
dislocated = -1
cannot_break = 1
min_broken_damage = ROBOLIMB_REPAIR_CAP //VOREStation Addition - Makes robotic limb damage scalable
remove_splint()
get_icon()
unmutate()
drop_sound = 'sound/items/drop/weldingtool.ogg'
pickup_sound = 'sound/items/pickup/weldingtool.ogg'
for(var/obj/item/organ/external/T in children)
T.robotize(company, keep_organs = keep_organs)
if(owner)
if(!keep_organs)
for(var/obj/item/organ/thing in internal_organs)
if(istype(thing))
if(thing.vital)
continue
internal_organs -= thing
owner.internal_organs_by_name[thing.organ_tag] = null
owner.internal_organs_by_name -= thing.organ_tag
owner.internal_organs.Remove(thing)
qdel(thing)
while(null in owner.internal_organs)
owner.internal_organs -= null
owner.refresh_modular_limb_verbs()
return 1
/obj/item/organ/external/proc/mutate()
if(src.robotic >= ORGAN_ROBOT)
return
src.status |= ORGAN_MUTATED
if(owner) owner.update_icons_body()
/obj/item/organ/external/proc/unmutate()
src.status &= ~ORGAN_MUTATED
if(owner) owner.update_icons_body()
/obj/item/organ/external/proc/get_damage() //returns total damage
return (brute_dam+burn_dam) //could use max_damage?
/obj/item/organ/external/proc/has_infected_wound()
for(var/datum/wound/W in wounds)
if(W.germ_level > INFECTION_LEVEL_ONE)
return 1
return 0
/obj/item/organ/external/proc/is_usable()
return !(status & (ORGAN_MUTATED|ORGAN_DEAD))
/obj/item/organ/external/proc/is_malfunctioning()
return ((robotic >= ORGAN_ROBOT) && (brute_dam + burn_dam) >= min_broken_damage*0.83 && prob(brute_dam + burn_dam)) //VOREStation Edit - Makes robotic limb damage scalable
/obj/item/organ/external/proc/embed(var/obj/item/W, var/silent = 0)
if(!owner || loc != owner)
return
if(SEND_SIGNAL(owner, COMSIG_EMBED_OBJECT) & COMSIG_CANCEL_EMBED) //Normally we'd let this proc continue on, but it's much less time consumptive to just do a godmode check here.
return 0 // Cancelled by a component
if(!silent)
owner.visible_message(span_danger("\The [W] sticks in the wound!"))
implants += W
owner.embedded_flag = 1
add_verb(owner, /mob/proc/yank_out_object)
owner.throw_alert("embeddedobject", /atom/movable/screen/alert/embeddedobject)
W.add_blood(owner)
if(ismob(W.loc))
var/mob/living/H = W.loc
H.drop_from_inventory(W)
W.loc = owner
/obj/item/organ/external/removed(var/mob/living/user, var/ignore_children = 0)
if(!owner)
return
var/is_robotic = robotic >= ORGAN_ROBOT
var/mob/living/carbon/human/victim = owner
..()
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 < ITEMSIZE_NORMAL)
implant.loc = get_turf(victim.loc)
else
implant.loc = src
implants.Cut()
// Attached organs also fly off.
if(!ignore_children)
for(var/obj/item/organ/external/O in children)
O.removed()
if(O)
O.loc = src
for(var/obj/item/I in O.contents)
I.loc = src
// Grab all the internal giblets too.
for(var/obj/item/organ/organ in internal_organs)
organ.removed()
organ.loc = src
// Remove parent references
parent?.children -= src
parent = null
release_restraints(victim)
victim.organs -= src
victim.organs_by_name[organ_tag] = null // Remove from owner's vars.
status |= ORGAN_CUT_AWAY //Checked during surgeries to reattach it
//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)
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
spark_system.set_up(5, 0, victim)
spark_system.attach(owner)
spark_system.start()
spawn(10)
qdel(spark_system)
qdel(src)
victim.refresh_modular_limb_verbs()
victim.update_icons_body()
/obj/item/organ/external/proc/disfigure(var/type = "brute")
if (disfigured)
return
if(owner)
if(type == "brute")
owner.visible_message(span_danger("You hear a sickening cracking sound coming from \the [owner]'s [name]."), \
span_danger("Your [name] becomes a mangled mess!"), \
span_danger("You hear a sickening crack."))
else
owner.visible_message(span_danger("\The [owner]'s [name] melts away, turning into mangled mess!"), \
span_danger("Your [name] melts away!"), \
span_danger("You hear a sickening sizzle."))
disfigured = 1
/obj/item/organ/external/proc/jostle_bone(force)
if(!(status & ORGAN_BROKEN)) //intact bones stay still
return
if(brute_dam + force < min_broken_damage/5) //no papercuts moving bones
return
if(internal_organs.len && prob(brute_dam + force) && !owner.transforming)
owner.custom_pain("A piece of bone in your [encased ? encased : name] moves painfully!", 50)
var/obj/item/organ/I = pick(internal_organs)
I.take_damage(rand(3,5))
/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(robotic >= ORGAN_ROBOT)
var/LL //Life-Like, aka only show that it's robotic in heavy damage
if(robotic >= ORGAN_LIFELIKE)
LL = 1
if(brute_dam)
switch(brute_dam)
if(0 to 20)
. += "some [LL ? "cuts" : "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 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
//Normal organic organ damage
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 in wounds)
if(W.internal && !open) continue // can't see internal wounds
var/this_wound_desc = W.desc
if(W.damage_type == BURN && W.salved)
this_wound_desc = "salved [this_wound_desc]"
if(W.bleeding())
this_wound_desc = "bleeding [this_wound_desc]"
else if(W.bandaged)
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(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)
// Returns a list of the clothing (not glasses) that are covering this part
/obj/item/organ/external/proc/get_covering_clothing(var/target_covering) // target_covering checks for mouth/eye coverage
var/list/covering_clothing = list()
if(!target_covering)
target_covering = src.body_part
if(owner)
var/list/protective_gear = list(owner.head, owner.wear_mask, owner.wear_suit, owner.w_uniform, owner.gloves, owner.shoes, owner.glasses)
for(var/obj/item/clothing/gear in protective_gear)
if(gear.body_parts_covered & target_covering)
covering_clothing |= gear
if(LAZYLEN(gear.accessories))
for(var/obj/item/clothing/accessory/bling in gear.accessories)
if(bling.body_parts_covered & src.body_part)
covering_clothing |= bling
return covering_clothing
/mob/living/carbon/human/proc/has_embedded_objects()
. = 0
for(var/obj/item/organ/external/L in organs)
for(var/obj/item/I in L.implants)
if(!istype(I,/obj/item/implant) && !istype(I,/obj/item/nif)) //VOREStation Add - NIFs
return 1
/obj/item/organ/external/proc/is_hidden_by_sprite_accessory(var/clothing_only = FALSE) // Clothing only will mean the check should only be used in places where we want to hide clothing icon, not organ itself.
if(owner && owner.tail_style && owner.tail_style.hide_body_parts && (organ_tag in owner.tail_style.hide_body_parts))
return 1
if(clothing_only && markings.len)
for(var/M in markings)
if(!markings[M]["on"]) //If the marking is off, the organ isn't hidden by it.
continue
var/datum/sprite_accessory/marking/mark = markings[M]["datum"]
if(mark.hide_body_parts && (organ_tag in mark.hide_body_parts))
return 1
#undef DROPLIMB_THRESHOLD_EDGE
#undef DROPLIMB_THRESHOLD_DESTROY