/**************************************************** EXTERNAL ORGANS ****************************************************/ /datum/organ/external name = "external" var/datum/species/species var/icon_name = null var/body_part = null var/icon_position = 0 var/obj/item/organ_item = null //The actual item used to make the organ var/list/slots_to_drop var/damage_state = "00" var/brute_dam = 0 var/burn_dam = 0 var/max_damage = 0 var/max_size = 0 var/last_dam = -1 var/display_name var/list/wounds = list() var/number_wounds = 0 //Cache the number of wounds, which is NOT wounds.len! var/tmp/perma_injury = 0 var/tmp/destspawn = 0 //Has it spawned the broken limb? var/tmp/amputated = 0 //Whether this has been cleanly amputated, thus causing no pain var/min_broken_damage = 30 var/datum/organ/external/parent var/list/datum/organ/external/children //Internal organs of this body part var/list/datum/organ/internal/internal_organs var/damage_msg = "You feel an intense pain." var/broken_description var/open = 0 var/stage = 0 var/cavity = 0 var/sabotaged = 0 //If a prosthetic limb is emagged, it will detonate when it fails. var/encased //Needs to be opened with a saw to access the organs. var/obj/item/hidden = null var/list/implants = list() //How often wounds should be updated, a higher number means less often var/wound_update_accuracy = 1 var/has_fat = 0 //Has a _fat variant var/grasp_id = 0 //Does this organ affect other grasping organs? var/can_grasp = 0 //Can this organ actually grasp something? /datum/organ/external/New(var/datum/organ/external/P) if(P) parent = P if(!parent.children) parent.children = list() parent.children.Add(src) return ..() /datum/organ/external/Destroy() if(parent?.children) parent.children -= src parent = null if(children) for(var/datum/organ/external/O in children) qdel(O) children = null if(internal_organs) for(var/datum/organ/internal/O in internal_organs) qdel(O) internal_organs = null if(implants) for(var/obj/O in implants) qdel(O) implants = null if(wounds) for(var/datum/wound/W in wounds) qdel(W) wounds = null if(owner) owner.organs -= src if(grasp_id) owner.grasp_organs -= src for(var/organ_name in owner.organs_by_name) //I hate that this is the only way if(owner.organs_by_name[organ_name] == src) owner.organs_by_name -= src break //Assume there's only one because if not we have much bigger problems than a hard del owner = null organ_item = null //I honestly cannot tell if anything else should be done with this ..() /**************************************************** DAMAGE PROCS ****************************************************/ /datum/organ/external/proc/emp_act(severity) if(!is_robotic()) //Meatbags do not care about EMP return var/probability = 30 var/damage = 15 if(severity == 2) probability = 1 damage = 3 if(prob(probability)) if(is_malfunctioning()) explode() else status |= ORGAN_MALFUNCTIONING to_chat(owner, "Your [display_name] malfunctions!") take_damage(damage, 0, 1, used_weapon = "EMP") /datum/organ/external/proc/get_health() return (burn_dam + brute_dam) /datum/organ/external/proc/explode() if(is_peg()) return droplimb(1) var/gib_type = /obj/effect/decal/cleanable/blood/gibs/core if(is_robotic()) gib_type = /obj/effect/decal/cleanable/blood/gibs/robot var/turf/T = get_turf(owner) if(T) var/obj/effect/decal/gib //Create two gibs for(var/i = 1 to 2) gib = new gib_type(get_turf(owner)) gib.name = "mangled [src.display_name]" gib.desc = "Completely useless now." spawn(3) step(gib, pick(alldirs)) //move the last spawned gib in a random direction if(is_robotic()) T.hotspot_expose(1000,1000,surfaces=1) else playsound(T, 'sound/effects/gib3.ogg', 50, 1) var/msg = pick( "\The [owner]'s [display_name] is mangled to bits!",\ "\The [owner]'s [display_name] [is_robotic() ? "is completely wrecked" : "explodes into gory red paste"]!",\ "\The [owner]'s [display_name] [is_robotic() ? "is ripped into loose shreds" : "collapses into a lump of gore"]!") owner.visible_message("[msg]") droplimb(1, spawn_limb = 0, display_message = FALSE) /datum/organ/external/proc/dust() if(is_peg()) return droplimb(1) var/obj/O = generate_dropped_organ() var/obj/I = O.ashtype() qdel(O) new I(owner.loc) droplimb(1, spawn_limb = 0, display_message = FALSE) /datum/organ/external/proc/take_damage(brute, burn, sharp, edge, used_weapon = null, list/forbidden_limbs = list()) if((brute <= 0) && (burn <= 0)) return 0 if(!is_existing()) //No limb there return 0 if(!is_organic()) brute *= 0.66 //~2/3 damage for ROBOLIMBS burn *= (status & (ORGAN_PEG) ? 2 : 0.66) //~2/3 damage for ROBOLIMBS 2x for peg else var/datum/species/species = src.species || owner.species if(species) if(species.brute_mod) brute *= species.brute_mod if(species.burn_mod) burn *= species.burn_mod //If limb took enough damage, try to cut or tear it off if(body_part != UPPER_TORSO && body_part != LOWER_TORSO) //As hilarious as it is, getting hit on the chest too much shouldn't effectively gib you. var/threshold_multiplier = 1 if(isslimeperson(owner)) threshold_multiplier = 0 if(config.limbs_can_break && get_health() >= max_damage * config.organ_health_multiplier * threshold_multiplier) if(isslimeperson(owner)) var/chance_multiplier = 1 if(istype(src, /datum/organ/external/head)) chance_multiplier = 0 if(prob(brute * sharp * chance_multiplier)) droplimb(1) return else if(sharp || is_peg()) if(prob((5 * brute) * sharp)) //sharp things have a greater chance to sever based on how sharp they are droplimb(1) return else if(!sharp && brute > 15) //Massive blunt damage can result in limb explosion if(prob((brute/7.5)**3)) //15 dmg - 8% chance, 22 dmg - 27%, 30 dmg - 64%, anything higher than ~35 is a guaranteed limbgib explode() return else if(brute > 20 && prob(2 * brute)) //non-sharp hits with force greater than 20 can cause limbs to sever, too (smaller chance) droplimb(1) return else if((config.limbs_can_break && sharp == 100) || ((sharp >= 2) && (config.limbs_can_break && brute_dam + burn_dam >= (max_damage * config.organ_health_multiplier)/sharp))) //items of exceptional sharpness are capable of severing the limb below its damage threshold, the necessary threshold scaling inversely with sharpness if(prob((5 * (brute * sharp)) * (sharp - 1))) //the same chance multiplier based on sharpness applies here as well droplimb(1) return //High brute damage or sharp objects may damage internal organs if(internal_organs != null) if((sharp && brute >= 5) || brute >= 10) if(prob(5)) //Damage an internal organ var/datum/organ/internal/I = pick(internal_organs) I.take_damage(brute / 2) brute -= brute / 2 if(is_broken() && prob(40) && brute) owner.audible_scream() //Getting hit on broken and unsplinted limbs hurts if(used_weapon) add_autopsy_data("[used_weapon]", brute + burn) var/can_cut = (prob(brute * 2) || sharp) && is_organic() //If the limbs can break, make sure we don't exceed the maximum damage a limb can take before breaking if((brute_dam + burn_dam + brute + burn) < max_damage || !config.limbs_can_break) if(brute) if(can_cut) 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.organ_health_multiplier - (brute_dam + burn_dam) if(can_inflict) if(brute > 0) //Inflict all burte damage we can if(can_cut) createwound(CUT, min(brute,can_inflict)) else createwound(BRUISE, min(brute,can_inflict)) var/temp = can_inflict //How much mroe damage can we inflict can_inflict = max(0, can_inflict - brute) //How much brute damage is left to inflict brute = max(0, brute - temp) 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 = max(0, burn - can_inflict) //If there are still hurties to dispense if(burn || brute) if(!is_organic()) droplimb(1) //Non-organic limbs just drop off with no further complications else //List organs we can pass it to var/list/datum/organ/external/possible_points = list() if(parent) possible_points += parent if(children) possible_points += children if(forbidden_limbs.len) possible_points -= forbidden_limbs if(possible_points.len) //And pass the pain around var/datum/organ/external/target = pick(possible_points) target.take_damage(brute, burn, sharp, edge, used_weapon, forbidden_limbs + src) //Sync the organ's damage with its wounds src.update_damages() owner.updatehealth() var/result = update_icon() return result /datum/organ/external/proc/heal_damage(brute, burn, internal = 0, robo_repair = 0) if(is_robotic() && !robo_repair) //This item can't fix robotic limbs return if(is_peg()) //We can't fix peg legs at all 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 == CUT || W.damage_type == BRUISE) brute = W.heal_damage(brute) else if(W.damage_type == BURN) burn = W.heal_damage(burn) if(internal) status &= ~ORGAN_BROKEN perma_injury = 0 //Sync the organ's damage with its wounds src.update_damages() owner.updatehealth() var/result = update_icon() return result //This function completely restores a damaged organ to perfect condition /datum/organ/external/proc/rejuvenate() damage_state = "00" //Robotic organs stay robotic. Fix because right click rejuvinate makes IPC's organs organic. //N3X: Use bitmask to exclude shit we don't want. status = status & (ORGAN_ROBOT|ORGAN_PEG) perma_injury = 0 brute_dam = 0 burn_dam = 0 cancer_stage = 0 germ_level = 0 //Handle internal organs for(var/datum/organ/internal/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/weapon/implant)) //We don't want to remove REAL implants. Just shrapnel etc. implanted_object.forceMove(owner.loc) implants -= implanted_object owner.updatehealth() /datum/organ/external/proc/createwound(var/type = CUT, var/damage) if(!damage || damage < 0) //We weren't passed a damage value, or it's negative for some reason return var/datum/species/species = src.species || owner.species //First check whether we can widen an existing wound if(wounds.len > 0 && prob(50 + owner.number_wounds * 10)) if((type == CUT || type == BRUISE) && damage >= 5) var/datum/wound/W = pick(wounds) if(W.amount == 1 && W.started_healing()) W.open_wound(damage) if(prob(25)) owner.visible_message("The wound on [owner.name]'s [display_name] widens with a nasty ripping sound.", \ "The wound on your [display_name] widens with a nasty ripping sound.", \ "You hear a nasty ripping noise, as if flesh is being torn apart.") return //Creating wound var/datum/wound/W var/size = min(max(1, damage/10), 6) //Possible types of wound var/list/size_names = list() switch(type) if(CUT) size_names = typesof(/datum/wound/cut/) - /datum/wound/cut/ if(BRUISE) size_names = typesof(/datum/wound/bruise/) - /datum/wound/bruise/ if(BURN) size_names = typesof(/datum/wound/burn/) - /datum/wound/burn/ size = min(size,size_names.len) var/wound_type = size_names[size] W = new wound_type(damage) //Possibly trigger an internal wound, too. var/local_damage = brute_dam + burn_dam + damage if(damage > 10 && type != BURN && local_damage > 20 && prob(damage) && is_organic() && !(species && species.anatomy_flags & NO_BLOOD)) var/internal_bleeding = 0 for(var/datum/wound/Wound in wounds) if(Wound.internal) internal_bleeding = 1 break if(!internal_bleeding) var/datum/wound/internal_bleeding/I = new (15) wounds += I owner.custom_pain("You feel something rip in your [display_name]!", 1) //Check whether we can add the wound to an existing wound for(var/datum/wound/other in wounds) if(other.desc == W.desc) //Okay, add it! other.damage += W.damage other.amount += 1 W = null //To signify that the wound was added break if(W) wounds += W update_damages() owner.updatehealth() /**************************************************** PROCESSING & UPDATING ****************************************************/ //Determines if we even need to process this organ. /datum/organ/external/proc/need_process() if(status && !is_organic()) //If it's non-organic, that's fine it will have a status. return 1 if(brute_dam || burn_dam) return 1 if(last_dam != brute_dam + burn_dam) //Process when we are fully healed up. last_dam = brute_dam + burn_dam return 1 last_dam = brute_dam + burn_dam return 0 /datum/organ/external/process() //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) //Dismemberment if(status & ORGAN_DESTROYED) if(!destspawn && config.limbs_can_break) droplimb() return if(parent) if(parent.status & ORGAN_DESTROYED) status |= ORGAN_DESTROYED owner.update_body(1) return //Bone fracurtes var/datum/species/species = src.species || owner.species if(config.bones_can_break && brute_dam > min_broken_damage * config.organ_health_multiplier && is_organic() && !(species.anatomy_flags & NO_BONES)) src.fracture() if(!is_broken()) perma_injury = 0 update_germs() if(grasp_id) process_grasp(owner.held_items[grasp_id], owner.get_index_limb_name(grasp_id)) //Cancer growth for external organs is simple, it grows, hurts, damages, and suddenly grows out of control //Limb cancer is relatively benign until it grows large, then it cripples you and metastases /datum/organ/external/handle_cancer() if(..()) return 1 if(!is_existing()) //Limb has been destroyed or amputated, cancer's over as far as we are concerned since there is no limb to grow on anymore cancer_stage = 0 return 1 switch(cancer_stage) if(CANCER_STAGE_SMALL_TUMOR to CANCER_STAGE_LARGE_TUMOR) //Small tumors will not damage your limb, but might flash pain if(prob(1)) owner.custom_pain("You feel a stabbing pain in your [display_name]!", 1) if(CANCER_STAGE_LARGE_TUMOR to CANCER_STAGE_METASTASIS) //Large tumors will start damaging your limb and give the owner DNA damage (bodywide, can't go per limb) if(prob(20)) take_damage(0.5, 0, 0, used_weapon = "tumor growth") owner.custom_pain("You feel a stabbing pain in your [display_name]!", 1) if(prob(1)) owner.apply_damage(0.5, CLONE, src) if(CANCER_STAGE_METASTASIS to INFINITY) //Metastasis achieved, limb will start breaking down very rapidly, and cancer will spread to all other limbs in short order through bloodstream if(prob(50)) take_damage(0.5, 0, 0, used_weapon = "tumor metastasis") if(prob(20)) owner.apply_damage(0.5, CLONE, src) if(prob(1)) owner.add_cancer() //Add a new cancerous growth //Cancer has a single universal sign. Coughing. Has a chance to happen every tick //Most likely not medically accurate, but whocares.ru if(prob(1)) owner.audible_cough() //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. */ /datum/organ/external/proc/update_germs() if(!is_existing() || !is_organic()) //Needs to be organic and existing 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() /datum/organ/external/proc/handle_germ_sync() var/antibiotics = owner.reagents.get_reagent_amount(SPACEACILLIN) 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 < 5) 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 /datum/organ/external/proc/handle_germ_effects() var/antibiotics = owner.reagents.get_reagent_amount(SPACEACILLIN) if(germ_level > 0 && germ_level < INFECTION_LEVEL_ONE && prob(60)) //This could be an else clause, but it looks cleaner this way germ_level-- //Since germ_level increases at a rate of 1 per second with dirty wounds, prob(60) should give us about 5 minutes before level one. if(germ_level >= INFECTION_LEVEL_ONE) //Having an infection raises your body temperature var/fever_temperature = (owner.species.heat_level_1 - owner.species.body_temperature - 5)* min(germ_level/INFECTION_LEVEL_TWO, 1) + owner.species.body_temperature //Need to make sure we raise temperature fast enough to get around environmental cooling preventing us from reaching fever_temperature owner.bodytemperature += clamp((fever_temperature - T20C) / BODYTEMP_COLD_DIVISOR + 1, 0, fever_temperature - owner.bodytemperature) if(prob(round(germ_level/10))) if(antibiotics < 5) germ_level++ if(prob(10)) //Adjust this to tweak how fast people take toxin damage from infections owner.adjustToxLoss(1) if(germ_level >= INFECTION_LEVEL_TWO && antibiotics < 5) //Spread the infection to internal organs var/datum/organ/internal/target_organ = null //Make internal organs become infected one at a time instead of all at once for(var/datum/organ/internal/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/datum/organ/internal/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/datum/organ/external/child in children) if(child.germ_level < germ_level && child.is_organic()) if(child.germ_level < INFECTION_LEVEL_ONE * 2 || prob(30)) child.germ_level++ if(parent) if(parent.germ_level < germ_level && parent.is_organic()) if(parent.germ_level < INFECTION_LEVEL_ONE * 2 || prob(30)) parent.germ_level++ if(germ_level >= INFECTION_LEVEL_THREE && antibiotics < 30) //Overdosing is necessary to stop severe infections if(!(status & ORGAN_DEAD)) status |= ORGAN_DEAD to_chat(owner, "You can't feel your [display_name] anymore.") owner.update_body(1) germ_level++ owner.adjustToxLoss(1) //Updating wounds. Handles wound natural healing, internal bleedings and infections /datum/organ/external/proc/update_wounds() if(!is_organic()) //Non-organic limbs don't heal or get worse return var/datum/species/species = src.species || owner.species for(var/datum/wound/W in wounds) //Internal wounds get worse over time. Low temperatures (cryo) stop them. if(W.internal && !W.is_treated() && owner.bodytemperature >= 170 && !(species && species.anatomy_flags & NO_BLOOD)) var/blood_factor = 1 if(owner.reagents.has_reagent(INAPROVALINE) || owner.reagents.has_reagent(BICARIDINE)) //Inaprovaline and bicaridine slow bleeding by 30% blood_factor -= 0.3 if(owner.reagents.has_reagent(HYPERZINE)) //On the other hand, hyperzine speeds up bleeding by 30% blood_factor += 0.3 if(owner.reagents.has_reagent(CLOTTING_AGENT) || owner.reagents.has_reagent(BIOFOAM)) //Clotting agent and biofoam stop bleeding entirely. blood_factor = 0 owner.vessel.remove_reagent(BLOOD, W.damage * wound_update_accuracy * 0.07 * max(0, blood_factor)) if(prob(1 * wound_update_accuracy)) owner.custom_pain("You feel a stabbing pain in your [display_name]!", 1) // slow healing var/heal_amt = 0 if(W.damage < 15 || (M_REGEN in owner.mutations && W.damage <= 50)) //This thing's edges are not in day's travel of each other, what healing? heal_amt += 0.2 if(W.is_treated() && W.damage < 50) //Whoa, not even magical band aid can hold it together heal_amt += 0.3 //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.organ_regeneration_multiplier if(M_REGEN in owner.mutations) heal_amt = heal_amt * 2 // The heal rate is twice as much if you have regeneration //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.germ_level = 0 W.disinfected = 1 if(W.damage <= 0) //Since we're a HIGH ARRPEE codebase and we want LOTS OF FLAVORR we like to keep all the completely healed wounds sticking around for 10 minutes minimum, so they show up as bruises and scars in examine. if(W.internal || W.created + 10 MINUTES <= world.time) //The exception to this is internal wounds, which are more of a special status and not really a wound at all in practice. wounds -= W continue //Let the GC handle the deletion of the wound //Sync the organ's damage with its wounds src.update_damages() if(update_icon()) owner.UpdateDamageIcon() //Updates brute_damn and burn_damn from wound damages. Updates BLEEDING status. /datum/organ/external/proc/update_damages() number_wounds = 0 brute_dam = 0 burn_dam = 0 status &= ~ORGAN_BLEEDING var/clamped = 0 var/datum/species/species = src.species || owner.species for(var/datum/wound/W in wounds) if(W.damage_type == CUT || W.damage_type == BRUISE) brute_dam += W.damage else if(W.damage_type == BURN) burn_dam += W.damage if(is_organic() && W.bleeding() && !(species.anatomy_flags & NO_BLOOD)) W.bleed_timer-- if(!owner.reagents.has_reagent(CLOTTING_AGENT)) status |= ORGAN_BLEEDING clamped |= W.clamped number_wounds += W.amount if(open && !clamped && is_organic() && !(species.anatomy_flags & NO_BLOOD)) //Things tend to bleed if they are CUT OPEN if(!owner.reagents.has_reagent(CLOTTING_AGENT)) status |= ORGAN_BLEEDING // new damage icon system // adjusted to set damage_state to brute/burn code only (without r_name0 as before) /datum/organ/external/proc/update_icon() var/n_is = damage_state_text() if(n_is != damage_state) damage_state = n_is //owner.update_body(1) return 1 return 0 //New damage icon system //Returns just the brute/burn damage code /datum/organ/external/proc/damage_state_text() if(!is_existing()) return "--" 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]" /datum/organ/external/proc/rejuvenate_limb() for(var/obj/item/weapon/shard/shrapnel/s in implants) if(istype(s)) implants -= s owner.contents -= s qdel(s) s = null amputated = 0 brute_dam = 0 burn_dam = 0 damage_state = "00" germ_level = 0 hidden = null number_wounds = 0 open = 0 perma_injury = 0 stage = 0 status = 0 trace_chemicals = list() wounds = list() wound_update_accuracy = 1 /**************************************************** DISMEMBERMENT ****************************************************/ //Recursive setting of all child organs to amputated /datum/organ/external/proc/setAmputatedTree() for(var/datum/organ/external/O in children) O.amputated = 1 O.setAmputatedTree() //Handles dismemberment //Returns the organ item /datum/organ/external/proc/droplimb(var/override = 0, var/no_explode = 0, var/spawn_limb = 1, var/display_message = TRUE) if(destspawn) return if(body_part == (UPPER_TORSO || LOWER_TORSO)) //We can't lose either, those cannot be amputated and will cause extremely serious problems return var/datum/species/species = src.species || owner.species var/obj/item/organ/external/organ //Dropped limb object if(override) status |= ORGAN_DESTROYED if(status & ORGAN_DESTROYED) destspawn = 1 //If your whole leg is missing, then yes, your foot is considered as "cleanly amputated". setAmputatedTree() if(spawn_limb) organ = get_organ_item() //If any organs are attached to this, attach them to the dropped organ item for(var/datum/organ/external/O in children) var/obj/item/organ/external/child_organ = O.droplimb(1, display_message = FALSE) if(istype(child_organ) && istype(organ)) organ.add_child(child_organ) else //If any organs are attached to this, destroy them for(var/datum/organ/external/O in children) O.droplimb(1, no_explode, spawn_limb, display_message) for(var/implant in implants) qdel(implant) src.status &= ~ORGAN_BROKEN src.status &= ~ORGAN_BLEEDING src.status &= ~ORGAN_SPLINTED src.status &= ~ORGAN_DEAD //No limb, no damage brute_dam = 0 burn_dam = 0 perma_injury = 0 for(var/datum/wound/W in wounds) wounds -= W number_wounds -= W.amount //Don't delete the wounds because they're transferred to the organ item. If they aren't, garbage collecting will take care of it src.species = null if(body_part == LOWER_TORSO) to_chat(owner, "You are now sterile.") if(body_part & (HAND_LEFT | HAND_RIGHT | ARM_LEFT | ARM_RIGHT)) owner.update_hands_icons() if(slots_to_drop && slots_to_drop.len) for(var/slot_id in slots_to_drop) owner.u_equip(owner.get_item_by_slot(slot_id), 1) if(grasp_id && can_grasp) if(owner.held_items[grasp_id]) owner.u_equip(owner.held_items[grasp_id], 1) //Robotic limbs explode if sabotaged. if(status & ORGAN_ROBOT && !no_explode && sabotaged) owner.visible_message("\The [owner]'s [display_name] explodes violently!", \ "Your [display_name] explodes violently!", \ "You hear an explosion followed by a scream!") explosion(get_turf(owner), -1, -1, 2, 3, whodunnit = owner) spark(src, 5, FALSE) if(organ) if(display_message) owner.visible_message("[owner.name]'s [display_name] flies off in an arc.", \ "Your [display_name] goes flying off!", \ "You hear a terrible sound of ripping tendons and flesh.") //Throw organs around var/randomdir = pick(cardinal) step(organ, randomdir) owner.update_body(1) owner.handle_organs(1) //OK so maybe your limb just flew off, but if it was attached to a pair of cuffs then hooray! Freedom! release_restraints() if(vital) owner.death() if(species.anatomy_flags & NO_STRUCTURE) status |= ORGAN_ATTACHABLE amputated = 1 setAmputatedTree() open = 0 if(isslimeperson(owner) && organ) spawn(1) if(organ.borer) organ.borer.detach() if(name != LIMB_HEAD) qdel(organ) else var/headloc = organ.loc rejuvenate_limb() var/datum/organ/internal/I = organ.organ_data var/obj/item/organ/internal/O = I.remove(owner) if(O && istype(O)) O.organ_data.rejecting = null owner.internal_organs_by_name["brain"] = null owner.internal_organs_by_name -= "brain" owner.internal_organs -= O.organ_data internal_organs -= O.organ_data O.removed(owner,owner) O.loc = headloc qdel(organ) organ = null return organ /datum/organ/external/proc/get_organ_item() var/organ_item = generate_dropped_organ(src.organ_item) if(istype(organ_item, /obj/item/robot_parts)) var/obj/item/robot_parts/O = organ_item O.burn_dam = src.burn_dam O.brute_dam = src.brute_dam return organ_item //Don't use this proc, use get_organ_item /datum/organ/external/proc/generate_dropped_organ(var/obj/item/current_organ) return current_organ /**************************************************** HELPERS ****************************************************/ //Returns true if this organ's species has a specific mutation /datum/organ/external/proc/has_mutation(mutation) if(!species) return FALSE return (species.default_mutations.Find(mutation)) /datum/organ/external/proc/release_restraints(var/uncuff = UNCUFF_BOTH) if(uncuff >= UNCUFF_BOTH) if(owner.handcuffed && (body_part in list(ARM_LEFT, ARM_RIGHT, HAND_LEFT, HAND_RIGHT))) owner.visible_message(\ "\The [owner.handcuffed.name] falls off of [owner.name].",\ "\The [owner.handcuffed.name] falls off you.") owner.drop_from_inventory(owner.handcuffed) if(uncuff <= UNCUFF_BOTH) if(owner.legcuffed && (body_part in list(FOOT_LEFT, FOOT_RIGHT, LEG_LEFT, LEG_RIGHT))) owner.visible_message("\The [owner.legcuffed.name] falls off of [owner].", \ "\The [owner.legcuffed.name] falls off you.") owner.drop_from_inventory(owner.legcuffed) /datum/organ/external/proc/bandage() var/rval = 0 src.status &= ~ORGAN_BLEEDING for(var/datum/wound/W in wounds) if(W.internal) continue rval |= !W.bandaged W.bandaged = 1 return rval /datum/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 /datum/organ/external/proc/clamp_wounds() //Inconsistent with the other names but clamp is a reserved word now 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 /datum/organ/external/proc/salve() var/rval = 0 for(var/datum/wound/W in wounds) rval |= !W.salved W.salved = 1 return rval /datum/organ/external/proc/fracture() var/datum/species/species = src.species || owner.species if(species.anatomy_flags & NO_BONES) return if(status & ORGAN_BROKEN) return owner.visible_message("You hear a loud cracking sound coming from \the [owner].", \ "Something feels like it shattered in your [display_name]!", \ "You hear a sickening crack.") if(owner.feels_pain()) owner.audible_scream() playsound(owner.loc, "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. All spacemen are all trained to be Houdini. if(owner.handcuffed && (body_part in list(HAND_LEFT, HAND_RIGHT))) if(prob(25)) release_restraints(UNCUFF_HANDS)//Handcuffs only. if(owner.legcuffed && (body_part in list(FOOT_LEFT, FOOT_RIGHT))) if(prob(25)) release_restraints(UNCUFF_LEGS)//Legcuffs only. if(isgolem(owner)) droplimb(1) return /datum/organ/external/proc/robotize() src.status &= ~ORGAN_BROKEN src.status &= ~ORGAN_BLEEDING src.status &= ~ORGAN_SPLINTED src.status &= ~ORGAN_CUT_AWAY src.status &= ~ORGAN_ATTACHABLE src.status &= ~ORGAN_DESTROYED src.status &= ~ORGAN_PEG src.status |= ORGAN_ROBOT src.species = null src.destspawn = 0 for (var/datum/organ/external/T in children) if(T) T.robotize() /datum/organ/external/proc/peggify() src.status &= ~ORGAN_BROKEN src.status &= ~ORGAN_BLEEDING src.status &= ~ORGAN_CUT_AWAY src.status &= ~ORGAN_SPLINTED src.status &= ~ORGAN_ATTACHABLE src.status &= ~ORGAN_DESTROYED src.status &= ~ORGAN_ROBOT src.status |= ORGAN_PEG src.species = null src.wounds.len = 0 for (var/datum/organ/external/T in children) if(T) if(body_part == ARM_LEFT || body_part == ARM_RIGHT || body_part == LEG_RIGHT || body_part == LEG_LEFT) T.peggify() src.destspawn = 0 else T.droplimb(1, 1) T.status &= ~ORGAN_BROKEN T.status &= ~ORGAN_BLEEDING T.status &= ~ORGAN_CUT_AWAY T.status &= ~ORGAN_SPLINTED T.status &= ~ORGAN_ATTACHABLE T.status &= ~ORGAN_DESTROYED T.status &= ~ORGAN_ROBOT T.wounds.len = 0 /datum/organ/external/proc/fleshify() src.status &= ~ORGAN_BROKEN src.status &= ~ORGAN_BLEEDING src.status &= ~ORGAN_SPLINTED src.status &= ~ORGAN_CUT_AWAY src.status &= ~ORGAN_ATTACHABLE src.status &= ~ORGAN_DESTROYED src.status &= ~ORGAN_PEG src.status &= ~ORGAN_ROBOT src.destspawn = 0 /datum/organ/external/proc/attach(obj/item/I) if(istype(I, /obj/item/organ/external)) //Attaching an organic limb var/obj/item/organ/external/organ = I src.fleshify() //Transfer properties (species, damage, ...) src.species = organ.species src.cancer_stage = organ.cancer_stage src.wounds = organ.wounds src.status = organ.status src.brute_dam = organ.brute_dam src.burn_dam = organ.burn_dam owner.butchering_drops += organ.butchering_drops //Transfer any internal_organs from the organ item to the body for(var/datum/organ/internal/transfer in organ.internal_organs) owner.internal_organs += transfer owner.internal_organs_by_name[transfer.organ_type] = transfer owner.internal_organs_by_name[transfer.organ_type].owner = owner //Process attached parts (i.e. if attaching an arm with a hand, this will process the hand) for(var/obj/item/organ/external/attached in organ.children) organ.remove_child(attached) //Get the organ datum of the attached organ var/datum/organ/external/OE = owner.get_organ(attached.part) OE.attach(attached) //If the limb we're attaching has organ_data, convert and transfer it to internal_organs (this is for the brain only) if(organ.organ_data && !owner.internal_organs_by_name[organ.organ_data.organ_type]) //If the limb has organ_data, and the patient doesn't have that internal organ yet: owner.internal_organs_by_name[organ.organ_data.organ_type] = organ.organ_data.Copy() //Patient's interal organ (of the same name) is assigned the organ_data's properties from the limb owner.internal_organs += owner.internal_organs_by_name[organ.organ_data.organ_type] //Patient's internal organ list has organ_data added to it internal_organs += owner.internal_organs_by_name[organ.organ_data.organ_type] //the limb's internal organ list has organ_data added to it owner.internal_organs_by_name[organ.organ_data.organ_type].owner = owner //the patient's new organ has its owner set to the patient else if(istype(I, /obj/item/weapon/peglimb)) //Attaching a peg limb src.peggify() else if(istype(I, /obj/item/robot_parts)) //Robotic limb var/obj/item/robot_parts/R = I src.robotize() src.sabotaged = R.sabotaged src.brute_dam = R.brute_dam src.burn_dam = R.burn_dam else throw EXCEPTION("Bad item passed to attach(): '[I]'") qdel(I) owner.handle_organs(1) owner.update_body() owner.updatehealth() owner.UpdateDamageIcon() /datum/organ/external/proc/mutate() src.status |= ORGAN_MUTATED owner.update_body() owner.handle_organs(1) /datum/organ/external/proc/unmutate() src.status &= ~ORGAN_MUTATED owner.update_body() owner.handle_organs(1) /datum/organ/external/proc/get_damage() //returns total damage return max(brute_dam + burn_dam - perma_injury, perma_injury) //could use health? /datum/organ/external/proc/has_infected_wound() for(var/datum/wound/W in wounds) if(W.germ_level > INFECTION_LEVEL_ONE) return 1 return 0 /datum/organ/external/get_icon(gender = "", isFat = 0) //stand_icon = new /icon(icobase, "torso_[g][fat?"_fat":""]") if(gender) gender="_[gender]" var/fat = "" if(isFat && has_fat) fat = "_fat" var/icon_state = "[icon_name][gender][fat]" var/baseicon = (species ? species.icobase : owner.race_icon) if(status & ORGAN_MUTATED) baseicon = (species ? species.deform : owner.deform_icon) else if(is_peg()) baseicon = 'icons/mob/human_races/o_peg.dmi' else if(is_robotic()) baseicon = 'icons/mob/human_races/o_robot.dmi' return new /icon(baseicon, icon_state) //Our external limb is a peg /datum/organ/external/proc/is_peg() return (status & ORGAN_PEG) //Our external limb is robotic /datum/organ/external/proc/is_robotic() return (status & ORGAN_ROBOT) //Our external limb is organic, 100 % bio /datum/organ/external/proc/is_organic() return !(status & (ORGAN_ROBOT|ORGAN_PEG)) //Is the limb physically present ? /datum/organ/external/proc/is_existing() return !(status & (ORGAN_DESTROYED|ORGAN_CUT_AWAY)) //Can we use the limb at all in any manner /datum/organ/external/proc/is_usable() return !(status & (ORGAN_DESTROYED|ORGAN_MUTATED|ORGAN_DEAD|ORGAN_CUT_AWAY|ORGAN_MALFUNCTIONING)) //Is the limb broken and not splinted /datum/organ/external/proc/is_broken() return ((status & ORGAN_BROKEN) && !(status & ORGAN_SPLINTED)) /datum/organ/external/proc/is_healthy() return (is_usable() && !is_broken()) //Is the limb robotic and malfunctioning /datum/organ/external/proc/is_malfunctioning() return (is_robotic() && (status & (ORGAN_MALFUNCTIONING) || (brute_dam + burn_dam > min_broken_damage/2 && prob(brute_dam + burn_dam)))) //Can we use advanced tools (no pegs or hook-hands) /datum/organ/external/proc/can_use_advanced_tools() return !(status & (ORGAN_DESTROYED|ORGAN_MUTATED|ORGAN_DEAD|ORGAN_PEG|ORGAN_CUT_AWAY)) /datum/organ/external/proc/can_stand() return 0 /datum/organ/external/proc/can_grasp() if(parent) // if the person doesn't have an arm then the hand can't grasp return (parent.is_existing() && can_grasp && grasp_id) return (can_grasp && grasp_id) /datum/organ/external/proc/process_grasp(var/obj/item/c_hand, var/hand_name) if(!c_hand) return if(c_hand.cant_drop) return if(is_organic() && is_broken() && !istype(c_hand,/obj/item/tk_grab)) owner.drop_item(c_hand) var/emote_scream = pick("screams in pain and", "lets out a sharp cry and", "cries out and") owner.emote("me", 1, "[owner.feels_pain() ? emote_scream : ""] drops what they were holding in their [hand_name]!") if(is_malfunctioning()) // owner.u_equip(c_hand, 1) owner.emote("me", 1, "drops what they were holding, their [hand_name] malfunctioning!") spark(src, 5, FALSE) owner.drop_item(c_hand) /datum/organ/external/proc/embed(var/obj/item/weapon/W, var/silent = 0) if(!silent) owner.visible_message("\The [W] sticks in the wound!") implants += W owner.embedded_flag = 1 owner.verbs += /mob/proc/yank_out_object W.add_blood(owner) if(ismob(W.loc)) var/mob/living/H = W.loc H.drop_item(W, force_drop = 1) W.forceMove(owner) /datum/organ/external/proc/skeletify() if(istype(species, /datum/species/skellington)) return owner.visible_message("The flesh falls off of \the [owner]'s [display_name]!","The flesh is falling off of your [display_name]!") var/new_species_name if(isvox(owner)) new_species_name = "Skeletal Vox" else new_species_name = "Skellington" var/datum/species/S = all_species[new_species_name] species = new S.type owner.update_body() /**************************************************** ORGAN DEFINES ****************************************************/ /datum/organ/external/chest name = LIMB_CHEST icon_name = "torso" display_name = "chest" max_damage = 150 min_broken_damage = 75 body_part = UPPER_TORSO has_fat = 1 vital = 1 encased = "ribcage" /datum/organ/external/groin name = LIMB_GROIN icon_name = "groin" display_name = "groin" max_damage = 115 min_broken_damage = 70 body_part = LOWER_TORSO vital = 1 //=====Legs====== /datum/organ/external/l_leg name = LIMB_LEFT_LEG display_name = "left leg" icon_name = "l_leg" max_damage = 75 min_broken_damage = 30 body_part = LEG_LEFT icon_position = LEFT /datum/organ/external/l_leg/can_stand() //Peg legs don't require an attached foot if(is_peg()) return 1 //For normal legs, check if the feet are broken or missing. var/feet_usable = FALSE for(var/datum/organ/external/child in children) if(child.is_usable()) feet_usable = TRUE if(!feet_usable) return 0 //If the feet are OK, return 1 if this leg is usable return is_usable() /datum/organ/external/l_leg/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(is_robotic()) current_organ = new /obj/item/robot_parts/l_leg(owner.loc) else current_organ = new /obj/item/organ/external/l_leg(owner.loc, owner, src) return current_organ /datum/organ/external/r_leg name = LIMB_RIGHT_LEG display_name = "right leg" icon_name = "r_leg" max_damage = 75 min_broken_damage = 30 body_part = LEG_RIGHT icon_position = RIGHT //This proc is same as l_leg/can_stand() /datum/organ/external/r_leg/can_stand() //Peg legs don't require an attached foot if(is_peg()) return 1 //For normal legs, check if the feet are broken or missing. var/feet_usable = FALSE for(var/datum/organ/external/child in children) if(child.is_usable()) feet_usable = TRUE if(!feet_usable) return 0 //If the feet are OK, return 1 if this leg is usable return is_usable() /datum/organ/external/r_leg/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(is_robotic()) current_organ = new /obj/item/robot_parts/r_leg(owner.loc) else current_organ = new /obj/item/organ/external/r_leg(owner.loc, owner, src) return current_organ //======Arms======= /datum/organ/external/l_arm name = LIMB_LEFT_ARM display_name = "left arm" icon_name = "l_arm" max_damage = 75 min_broken_damage = 30 body_part = ARM_LEFT grasp_id = GRASP_LEFT_HAND /datum/organ/external/l_arm/generate_dropped_organ(current_organ) if(status & ORGAN_PEG) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(status & ORGAN_ROBOT) current_organ= new /obj/item/robot_parts/l_arm(owner.loc) else current_organ= new /obj/item/organ/external/l_arm(owner.loc, owner, src) return current_organ /datum/organ/external/r_arm name = LIMB_RIGHT_ARM display_name = "right arm" icon_name = "r_arm" max_damage = 75 min_broken_damage = 30 body_part = ARM_RIGHT grasp_id = GRASP_RIGHT_HAND /datum/organ/external/r_arm/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(is_robotic()) current_organ = new /obj/item/robot_parts/r_arm(owner.loc) else current_organ = new /obj/item/organ/external/r_arm(owner.loc, owner, src) return current_organ /datum/organ/external/l_foot name = LIMB_LEFT_FOOT display_name = "left foot" icon_name = "l_foot" max_damage = 40 min_broken_damage = 15 body_part = FOOT_LEFT icon_position = LEFT slots_to_drop = list(slot_shoes, slot_legcuffed) /datum/organ/external/l_foot/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(!is_robotic()) current_organ = new /obj/item/organ/external/l_foot(owner.loc, owner, src) return current_organ /datum/organ/external/r_foot name = LIMB_RIGHT_FOOT display_name = "right foot" icon_name = "r_foot" max_damage = 40 min_broken_damage = 15 body_part = FOOT_RIGHT icon_position = RIGHT slots_to_drop = list(slot_shoes, slot_legcuffed) /datum/organ/external/r_foot/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(!is_robotic()) current_organ = new /obj/item/organ/external/r_foot(owner.loc, owner, src) return current_organ /datum/organ/external/r_hand name = LIMB_RIGHT_HAND display_name = "right hand" icon_name = "r_hand" max_damage = 40 min_broken_damage = 15 body_part = HAND_RIGHT grasp_id = GRASP_RIGHT_HAND can_grasp = 1 slots_to_drop = list(slot_gloves, slot_handcuffed) /datum/organ/external/r_hand/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(!is_robotic()) current_organ = new /obj/item/organ/external/r_hand(owner.loc, owner, src) return current_organ /datum/organ/external/l_hand name = LIMB_LEFT_HAND display_name = "left hand" icon_name = "l_hand" max_damage = 40 min_broken_damage = 15 body_part = HAND_LEFT grasp_id = GRASP_LEFT_HAND can_grasp = 1 slots_to_drop = list(slot_gloves, slot_handcuffed) /datum/organ/external/l_hand/generate_dropped_organ(current_organ) if(is_peg()) current_organ = new /obj/item/weapon/peglimb(owner.loc) if(!current_organ) if(!is_robotic()) current_organ = new /obj/item/organ/external/l_hand(owner.loc, owner, src) return current_organ /datum/organ/external/head icon_name = "head" display_name = "head" name = LIMB_HEAD max_damage = 130 min_broken_damage = 40 body_part = HEAD var/disfigured = FALSE vital = 1 encased = "skull" slots_to_drop = list(slot_glasses, slot_wear_mask, slot_head, slot_ears) /datum/organ/external/head/attach(obj/item/I) ..() var/obj/item/organ/external/head/head_item = I if(!istype(head_item)) return var/datum/human_appearance/app = head_item.owner_appearance if(app) owner.my_appearance.name = app.name owner.real_name = app.name owner.my_appearance.h_style = app.h_style owner.my_appearance.r_hair = app.r_hair owner.my_appearance.g_hair = app.g_hair owner.my_appearance.b_hair = app.b_hair owner.my_appearance.f_style = app.f_style owner.my_appearance.r_facial = app.r_facial owner.my_appearance.g_facial = app.g_facial owner.my_appearance.b_facial = app.b_facial owner.my_appearance.r_eyes = app.r_eyes owner.my_appearance.g_eyes = app.g_eyes owner.my_appearance.b_eyes = app.b_eyes owner.regenerate_icons() owner.update_name() /datum/organ/external/head/generate_dropped_organ(current_organ) if(!current_organ) current_organ = new /obj/item/organ/external/head(owner.loc, owner, src) owner.decapitated = makeweakref(current_organ) var/datum/organ/internal/brain/B = eject_brain() eject_eyes() var/obj/item/organ/external/head/H = current_organ if(B) H.organ_data = B B.owner_dna = H.owner_dna return current_organ /datum/organ/external/head/proc/eject_brain() var/datum/organ/internal/brain/B = owner.internal_organs_by_name["brain"] if(B) owner.internal_organs_by_name.Remove("brain") owner.internal_organs.Remove(B) src.internal_organs.Remove(B) return B /datum/organ/external/head/proc/eject_eyes() var/datum/organ/internal/eyes/E = owner.internal_organs_by_name["eyes"] if(E) owner.internal_organs_by_name.Remove("eyes") owner.internal_organs.Remove(E) return /datum/organ/external/head/explode() owner.remove_internal_organ(owner, owner.internal_organs_by_name["brain"], src) eject_eyes() .=..() owner.update_hair() /datum/organ/external/head/get_icon(gender = "", isFat = 0) if(!owner) return ..() var/baseicon = (species ? species.icobase : owner.race_icon) if(status & ORGAN_MUTATED) baseicon = (species ? species.deform : owner.deform_icon) if(is_peg()) baseicon = 'icons/mob/human_races/o_peg.dmi' if(is_robotic()) baseicon = 'icons/mob/human_races/o_robot.dmi' return new /icon(baseicon, "[icon_name]_[gender]") /datum/organ/external/head/take_damage(brute, burn, sharp, edge, used_weapon = null, list/forbidden_limbs = list()) ..(brute, burn, sharp, edge, used_weapon, forbidden_limbs) if(!disfigured) /* if(brute_dam > 40) if(prob(50)) disfigure("brute") */ if(burn_dam > 40) disfigure((used_weapon != WPN_LOW_BODY_TEMP) ? "burn" : "frostbite") /datum/organ/external/head/proc/disfigure(var/type = "brute") if(disfigured) return if(type == "brute") owner.visible_message("You hear a sickening cracking sound coming from \the [owner]'s face.", "Your face becomes an unrecognizable, mangled mess!", "You hear a sickening crack.") else if (type == "burn")// Also caused by Sulph Acid and Poly Acid. owner.visible_message("[owner]'s face melts away, turning into a mangled mess!", "Your face melts away into an unrecognizable, mangled mess!", "You hear a sickening sizzle.") else if (type == "frostbite") owner.visible_message("[owner]'s frozen face blisters and cracks.", "Your face blisters and numbs away!", "You hear a sickening crackling.") else // Generic message, shouldn't happen owner.visible_message("[owner]'s face disfigures.", "Your face becomes an unrecognizable, mangled mess!") disfigured = TRUE owner.update_hair() /**************************************************** EXTERNAL ORGAN ITEMS ****************************************************/ /obj/item/organ/external icon = 'icons/mob/human_races/r_human.dmi' var/datum/organ/internal/organ_data //Harvestable organs var/list/datum/organ/internal/internal_organs //Actual organs (for surgery) var/datum/dna/owner_dna var/part = "organ" //This variable stores "butchering products" - objects of type "/datum/butchering_product" (see code/datums/helper_datums/butchering.dm) //They are transferred from the mob from which the organ was removed. //Currently the only "butchering drops" which are going to be stored here are teeth var/list/butchering_drops = list() var/mob/living/simple_animal/borer/borer var/datum/species/species //Store health facts. Right now limited exclusively to cancer, but should likely include all limb stats eventually var/cancer_stage = 0 var/list/wounds = list() var/brute_dam var/burn_dam var/status //List of attached organs //It doesn't contain the whole tree, only the organs attached to this one var/list/obj/item/organ/external/children = list() /obj/item/organ/external/New(loc, mob/living/carbon/human/H, datum/organ/external/source) ..(loc) if(!istype(H)) return if(H.dna) owner_dna = H.dna.Clone() if(!blood_DNA) blood_DNA = list() blood_DNA[H.dna.unique_enzymes] = H.dna.b_type src.species = source.species || H.species cancer_stage = source.cancer_stage wounds = source.wounds.Copy() burn_dam = source.burn_dam brute_dam = source.brute_dam internal_organs = source.internal_organs //Copy status flags except for ORGAN_CUT_AWAY and ORGAN_DESTROYED status = source.status & ~(ORGAN_CUT_AWAY | ORGAN_DESTROYED) //Forming icon for the limb //Setting base icon for this mob's race update_icon(H) for(var/datum/butchering_product/B in H.butchering_drops) //Go through all butchering products (like teeth) in the parent if(B.stored_in_organ == src.part) //If they're stored in our organ, var/datum/butchering_product/new_bp = new B.type() //Create a new butchering_product datum to go into the head! new_bp.amount = B.amount B.amount = 0 //Transfer the found product's amount to the new datum src.butchering_drops += new_bp H.butchering_drops -= B //The reason why B isn't just transferred from H.butchering_drops to src.butchering_drops is: //on examine(), each butchering drop's "desc_modifier()" is added to the description. This adds stuff like "he HAS NO TEETH AT ALL!!!" to the resulting description. /obj/item/organ/external/examine(mob/user) ..() //TODO: wound datums should be handling this (they aren't because they're awful) //Brute damage can be either cuts or bruises if(brute_dam) if(locate(/datum/wound/cut) in wounds) to_chat(user, "It has some cuts on it.") if(locate(/datum/wound/bruise) in wounds) to_chat(user, "It has some bruises on it.") //Burn damage is straightforward switch(burn_dam) if(1 to 10) to_chat(user, "It has some minor burns on it.") if(10 to 50) to_chat(user, "It is covered in major burns.") if(50 to INFINITY) to_chat(user, "It is covered in large carbonised areas.") for(var/obj/item/organ/external/E in children) to_chat(user, "There's \a [E] attached to it.") //Add information about teeth and the such var/butchery if(butchering_drops.len) for(var/datum/butchering_product/B in butchering_drops) butchery = "[butchery][B.desc_modifier(src, user)]" if(butchery) to_chat(user, "[butchery]") /obj/item/organ/external/update_icon(mob/living/carbon/human/H) ..() if(!H && !species) return var/icon/base if(H) base = H.get_organ(part).get_icon(H.gender == FEMALE? "f":"m", (M_FAT in H.mutations) && (H.species && H.species.anatomy_flags & CAN_BE_FAT)) else if(species) base = icon(species.icobase) //Display organs attached to this one if(children.len) for(var/obj/item/organ/external/attached in children) attached.update_icon(H) //This works recursively, ensuring all children are drawn attached.transform = matrix() //Remove any rotation base.Blend(attached.icon, ICON_OVERLAY) if(base) //Changing limb's skin tone to match owner if(H) if(!H.species || H.species.anatomy_flags & HAS_SKIN_TONE) if(H.my_appearance.s_tone >= 0) base.Blend(rgb(H.my_appearance.s_tone, H.my_appearance.s_tone, H.my_appearance.s_tone), ICON_ADD) else base.Blend(rgb(-H.my_appearance.s_tone, -H.my_appearance.s_tone, -H.my_appearance.s_tone), ICON_SUBTRACT) icon = base dir = SOUTH src.transform = turn(src.transform, rand(70, 130)) /obj/item/organ/external/proc/add_child(obj/item/organ/external/O, upd_icon = TRUE) children.Add(O) O.forceMove(src) if(upd_icon) update_icon() /obj/item/organ/external/proc/remove_child(obj/item/organ/external/O) children.Remove(O) /obj/item/organ/external/attackby(obj/item/W, mob/user) if(W.is_sharp()) //Allow cutting attached parts off for(var/obj/item/organ/external/O in children) children.Remove(O) O.forceMove(get_turf(src)) update_icon() ..() /**************************************************** EXTERNAL ORGAN ITEMS DEFINES ****************************************************/ /obj/item/organ/external/l_arm name = "left arm" icon_state = LIMB_LEFT_ARM part = LIMB_LEFT_ARM w_class = W_CLASS_SMALL /obj/item/organ/external/l_arm/New(loc, mob/living/carbon/human/H) ..() if(H && istype(H)) var/mob/living/simple_animal/borer/B = H.has_brain_worms(LIMB_LEFT_ARM) if(B) B.infest_limb(src) /obj/item/organ/external/l_foot name = "left foot" icon_state = LIMB_LEFT_FOOT part = LIMB_LEFT_FOOT w_class = W_CLASS_TINY /obj/item/organ/external/l_hand name = "left hand" icon_state = LIMB_LEFT_HAND part = LIMB_LEFT_HAND w_class = W_CLASS_TINY /obj/item/organ/external/l_leg name = "left leg" icon_state = LIMB_LEFT_LEG part = LIMB_LEFT_LEG w_class = W_CLASS_SMALL /obj/item/organ/external/l_leg/New(loc, mob/living/carbon/human/H) ..() if(H && istype(H)) var/mob/living/simple_animal/borer/B = H.has_brain_worms(LIMB_LEFT_LEG) if(B) B.infest_limb(src) /obj/item/organ/external/r_arm name = "right arm" icon_state = LIMB_RIGHT_ARM part = LIMB_RIGHT_ARM w_class = W_CLASS_SMALL /obj/item/organ/external/r_arm/New(loc, mob/living/carbon/human/H) ..() if(H && istype(H)) var/mob/living/simple_animal/borer/B = H.has_brain_worms(LIMB_RIGHT_ARM) if(B) B.infest_limb(src) /obj/item/organ/external/r_foot name = "right foot" icon_state = LIMB_RIGHT_FOOT part = LIMB_RIGHT_FOOT w_class = W_CLASS_TINY /obj/item/organ/external/r_hand name = "right hand" icon_state = LIMB_RIGHT_HAND part = LIMB_RIGHT_HAND w_class = W_CLASS_TINY /obj/item/organ/external/r_leg name = "right leg" icon_state = LIMB_RIGHT_LEG part = LIMB_RIGHT_LEG w_class = W_CLASS_SMALL /obj/item/organ/external/r_leg/New(loc, mob/living/carbon/human/H) ..() if(H && istype(H)) var/mob/living/simple_animal/borer/B = H.has_brain_worms(LIMB_RIGHT_LEG) if(B) B.infest_limb(src) /obj/item/organ/external/head dir = NORTH name = LIMB_HEAD icon_state = "head_m" part = LIMB_HEAD w_class = W_CLASS_SMALL var/mob/living/carbon/brain/brainmob var/brain_op_stage = 0 /// (/mob/living/carbon/human) var/datum/weakref/origin_body /// Copied from the human passed to New(). Used when the head is reattached. var/datum/human_appearance/owner_appearance /obj/item/organ/external/head/ashtype() return /obj/item/weapon/skull /obj/item/organ/external/head/Destroy() if(brainmob) qdel(brainmob) brainmob = null ..() //obj/item/organ/external/head/with_teeth starts with 32 human teeth! /obj/item/organ/external/head/with_teeth/New() .=..() butchering_drops += new /datum/butchering_product/teeth/human() /obj/item/organ/external/head/posi name = "robotic head" /obj/item/organ/external/head/New(loc, mob/living/carbon/human/H, var/datum/organ/external/head/O) origin_body = makeweakref(H) if(istype(H)) src.icon_state = H.gender == MALE? "head_m" : "head_f" ..() if(isgolem(H)) //Golems don't inhabit their severed heads, they turn to dust when they die. var/mob/living/simple_animal/borer/B = H.has_brain_worms() if(B) B.detach() qdel(src) return if (!O || !O.disfigured) owner_appearance = H.my_appearance.Copy() owner_appearance.name = H.real_name //Add (facial) hair. if(H && H.my_appearance.f_style && !H.check_hidden_head_flags(HIDEBEARDHAIR)) var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[H.my_appearance.f_style] if(facial_hair_style && (species.name in facial_hair_style.species_allowed)) var/icon/facial = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") if(facial_hair_style.do_colouration) facial.Blend(rgb(H.my_appearance.r_facial, H.my_appearance.g_facial, H.my_appearance.b_facial), ICON_ADD) overlays.Add(facial) // icon.Blend(facial, ICON_OVERLAY) if(H && H.my_appearance.h_style && !H.check_hidden_head_flags(HIDEHEADHAIR)) var/datum/sprite_accessory/hair_style = hair_styles_list[H.my_appearance.h_style] if(hair_style && (species.name in hair_style.species_allowed)) var/icon/hair = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") if(hair_style.do_colouration) hair.Blend(rgb(H.my_appearance.r_hair, H.my_appearance.g_hair, H.my_appearance.b_hair), ICON_ADD) if(hair_style.additional_accessories) hair.Blend(icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_acc"), ICON_OVERLAY) overlays.Add(hair) //icon.Blend(hair, ICON_OVERLAY) if(H && istype(H)) var/mob/living/simple_animal/borer/B = H.has_brain_worms() if(B) B.infest_limb(src) if(H) transfer_identity(H) if (!O || !O.disfigured) name = "[H.real_name]'s head" else name = "disfigured head " H.regenerate_icons() if(brainmob) brainmob.stat = 2 brainmob.death() if(brainmob.mind && brainmob.mind.special_role == HIGHLANDER) if(H.lastattacker && istype(H.lastattacker, /mob/living/carbon/human)) var/mob/living/carbon/human/L = H.lastattacker if(L.mind && L.mind.special_role == HIGHLANDER) L.revive(0) to_chat(L, "You absorb \the [brainmob]'s power!") var/turf/T1 = get_turf(H) make_tracker_effects(T1, L) /obj/item/organ/external/head/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->head brainmob = new(src) brainmob.name = H.real_name brainmob.real_name = H.real_name brainmob.dna = H.dna.Clone() if (isbrain(H)) var/mob/living/carbon/brain/otherbrain = H brainmob.timeofhostdeath = otherbrain.timeofhostdeath else if (H.timeofdeath == 0)//happens when the human gets decapitated while still alive brainmob.timeofhostdeath = world.time else brainmob.timeofhostdeath = H.timeofdeath if(H.mind) H.mind.transfer_to(brainmob) brainmob.languages = H.languages brainmob.default_language = H.default_language brainmob.init_language = brainmob.default_language brainmob.container = src /obj/item/organ/external/head/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W,/obj/item/tool/scalpel) || istype(W,/obj/item/weapon/shard) || (istype(W,/obj/item/weapon/kitchen/utensil/knife/large) && !istype(W,/obj/item/weapon/kitchen/utensil/knife/large/butch))) if(organ_data) switch(brain_op_stage) if(0) user.visible_message("[user] cuts [brainmob]'s head open with \the [W].", \ "You cut [brainmob]'s open with \the [W]!") brain_op_stage = 1 if(2) user.visible_message("[user] severs [brainmob]'s brain connections delicately with \the [W].", \ "You sever [brainmob]'s brain connections delicately with \the [W]!") brain_op_stage = 3.0 else ..() else to_chat(user, "That head has no brain to remove!") else if(istype(W,/obj/item/tool/circular_saw) || istype(W,/obj/item/weapon/kitchen/utensil/knife/large/butch) || istype(W,/obj/item/weapon/hatchet)) if(organ_data) switch(brain_op_stage) if(1) user.visible_message("[user] saws [brainmob]'s head open with \the [W].", \ "You saw [brainmob]'s head open with \the [W].") brain_op_stage = 2 if(3) user.visible_message("[user] severs [brainmob]'s spine connections delicately with \the [W].", \ "You sever [brainmob]'s spine connections delicately with \the [W]!") user.attack_log += "\[[time_stamp()]\] Debrained [brainmob.name] ([brainmob.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)])" brainmob.attack_log += "\[[time_stamp()]\] Debrained by [user.name] ([user.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)])" msg_admin_attack("[user] ([user.ckey]) debrained [brainmob] ([brainmob.ckey]) (INTENT: [uppertext(user.a_intent)]) (JMP)") //TODO: ORGAN REMOVAL UPDATE. var/turf/T = get_turf(src) if(isatom(organ_data.removed_type)) var/obj/I = organ_data.removed_type if(istype(organ_data.removed_type, /obj/item/device/mmi/posibrain)) var/obj/item/device/mmi/posibrain/B = organ_data.removed_type B.transfer_identity(brainmob) else if(istype(organ_data.removed_type, /obj/item/organ/internal/brain)) var/obj/item/organ/internal/brain/B = organ_data.removed_type B.transfer_identity(brainmob) I.forceMove(T) else var/obj/item/organ/internal/brain/B = new organ_data.removed_type(T) B.transfer_identity(brainmob) if(borer) borer.detach() brain_op_stage = 4.0 organ_data = null else ..() else to_chat(user, "That head has no brain to remove!") else if(istype(W,/obj/item/soulstone) || istype(W,/obj/item/weapon/melee/soulblade)) var/datum/soul_capture/capture_datum = new() capture_datum.init_datum(user, src, W) qdel(capture_datum) return else if(istype(W,/obj/item/device/healthanalyzer)) to_chat(user, "You use \the [W] to induce a small electric shock into \the [src].") playsound(src,'sound/weapons/electriczap.ogg',50,1) animate(src, pixel_x = pixel_x + rand(-2,2), pixel_y = pixel_y + rand(-2,2) , time = 0.5 SECONDS, easing = BOUNCE_EASING) //Give it a little shake animate(src, pixel_x = initial(pixel_x), pixel_y = initial(pixel_y), time = 0.5 SECONDS, easing = LINEAR_EASING) //Then calm it down if(!organ_data) to_chat(user, "\The [src] has no brain!") return if(brainmob) var/mind_found = 0 if(brainmob.client) mind_found = 1 to_chat(user, "[pick("The eyes","The jaw","The ears")] of \the [src] twitch ever so slightly.") else var/mob/dead/observer/ghost = mind_can_reenter(brainmob.mind) if(ghost) var/mob/ghostmob = ghost.get_top_transmogrification() if(ghostmob)//Lights are on but no-one's home mind_found = 1 to_chat(user, "\The [src] stares blankly forward. The pupils dilate but otherwise it does not react to stimuli.") ghostmob << 'sound/effects/adminhelp.ogg' to_chat(ghostmob, "Someone has found your head. Return to it if you want to be resurrected! \ (Verbs -> Ghost -> Re-enter corpse, or click here!)") if(!mind_found) to_chat(user, "\The [src] seems unresponsive to shock stimuli.") else to_chat(user, "\The [src] seems unresponsive to shock stimuli.") return else ..() /obj/item/organ/external/head/Destroy() if(brainmob) brainmob.ghostize() qdel(brainmob) brainmob = null ..() /mob/living/carbon/human/find_organ_by_grasp_index(index) for(var/datum/organ/external/OE in grasp_organs) if(OE.grasp_id == index && OE.can_grasp) return OE return null /mob/living/carbon/human/has_hand_check() for(var/datum/organ/external/OE in grasp_organs) if(OE.can_grasp()) return TRUE return FALSE /datum/organ/external/send_to_past(var/duration) ..() var/static/list/resettable_vars = list( "damage_state", "brute_dam", "burn_dam", "last_dam", "wounds", "number_wounds", "perma_injury", "parent", "children", "internal_organs", "open", "stage", "cavity", "sabotaged", "encased", "implants") reset_vars_after_duration(resettable_vars, duration) for(var/datum/wound/W in wounds) W.send_to_past(duration)