diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index b1f7cc84f8..ddb901f24c 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -308,6 +308,7 @@ #define GRAB_PIXEL_SHIFT_NECK 16 #define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; +#define INTERACTING_WITH(X, Y) (Y in X.do_afters) /// Field of vision defines. #define FOV_90_DEGREES 90 diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm new file mode 100644 index 0000000000..d70a4ebdb2 --- /dev/null +++ b/code/datums/wounds/_wounds.dm @@ -0,0 +1,320 @@ +/* + Wounds are specific medical complications that can arise and be applied to (currently) carbons, with a focus on humans. All of the code for and related to this is heavily WIP, + and the documentation will be slanted towards explaining what each part/piece is leading up to, until such a time as I finish the core implementations. The original design doc + can be found at https://hackmd.io/@Ryll/r1lb4SOwU + + Wounds are datums that operate like a mix of diseases, brain traumas, and components, and are applied to a /obj/item/bodypart (preferably attached to a carbon) when they take large spikes of damage + or under other certain conditions (thrown hard against a wall, sustained exposure to plasma fire, etc). Wounds are categorized by the three following criteria: + 1. Severity: Either MODERATE, SEVERE, or CRITICAL. See the hackmd for more details + 2. Viable zones: What body parts the wound is applicable to. Generic wounds like broken bones and severe burns can apply to every zone, but you may want to add special wounds for certain limbs + like a twisted ankle for legs only, or open air exposure of the organs for particularly gruesome chest wounds. Wounds should be able to function for every zone they are marked viable for. + 3. Damage type: Currently either BRUTE or BURN. Again, see the hackmd for a breakdown of my plans for each type. + + When a body part suffers enough damage to get a wound, the severity (determined by a roll or something, worse damage leading to worse wounds), affected limb, and damage type sustained are factored into + deciding what specific wound will be applied. I'd like to have a few different types of wounds for at least some of the choices, but I'm just doing rough generals for now. Expect polishing +*/ + +/datum/wound + /// What it's named + var/name = "ouchie" + /// The description shown on the scanners + var/desc = "" + /// The basic treatment suggested by health analyzers + var/treat_text = "" + /// What the limb looks like on a cursory examine + var/examine_desc = "is badly hurt" + + /// needed for "your arm has a compound fracture" vs "your arm has some third degree burns" + var/a_or_from = "a" + /// The visible message when this happens + var/occur_text = "" + /// This sound will be played upon the wound being applied + var/sound_effect + + /// Either WOUND_SEVERITY_TRIVIAL (meme wounds like stubbed toe), WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, or WOUND_SEVERITY_CRITICAL (or maybe WOUND_SEVERITY_LOSS) + var/severity = WOUND_SEVERITY_MODERATE + /// The list of wounds it belongs in, WOUND_LIST_BONE, WOUND_LIST_CUT, or WOUND_LIST_BURN + var/wound_type + + /// What body zones can we affect + var/list/viable_zones = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + /// Who owns the body part that we're wounding + var/mob/living/carbon/victim = null + /// If we only work on organics (everything right now) + var/organic_only = TRUE + /// The bodypart we're parented to + var/obj/item/bodypart/limb = null + + /// Specific items such as bandages or sutures that can try directly treating this wound + var/list/treatable_by + /// Specific items such as bandages or sutures that can try directly treating this wound only if the user has the victim in an aggressive grab or higher + var/list/treatable_by_grabbed + /// Tools with the specified tool flag will also be able to try directly treating this wound + var/treatable_tool + /// Set to TRUE if we don't give a shit about the patient's comfort and are allowed to just use any random sharp thing on this wound. Will require an aggressive grab or more to perform + var/treatable_sharp + /// How long it will take to treat this wound with a standard effective tool, assuming it doesn't need surgery + var/base_treat_time = 5 SECONDS + + /// Using this limb in a do_after interaction will multiply the length by this duration (arms) + var/interaction_efficiency_penalty = 1 + /// Incoming damage on this limb will be multiplied by this, to simulate tenderness and vulnerability (mostly burns). + var/damage_mulitplier_penalty = 1 + /// If set and this wound is applied to a leg, we take this many deciseconds extra per step on this leg + var/limp_slowdown + /// How much we're contributing to this limb's bleed_rate + var/blood_flow + + /// The minimum we need to roll on [/obj/item/bodypart/proc/check_wounding()] to begin suffering this wound, see check_wounding_mods() for more + var/threshold_minimum + /// How much having this wound will add to all future check_wounding() rolls on this limb, to allow progression to worse injuries with repeated damage + var/threshold_penalty + /// If we need to process each life tick + var/processes = FALSE + + /// If TRUE and an item that can treat multiple different types of coexisting wounds (gauze can be used to splint broken bones, staunch bleeding, and cover burns), we get first dibs if we come up first for it, then become nonpriority. + /// Otherwise, if no untreated wound claims the item, we cycle through the non priority wounds and pick a random one who can use that item. + var/treat_priority = FALSE + + /// If having this wound makes currently makes the parent bodypart unusable + var/disabling + + /// What status effect we assign on application + var/status_effect_type + /// The status effect we're linked to + var/datum/status_effect/linked_status_effect + /// If we're operating on this wound and it gets healed, we'll nix the surgery too + var/datum/surgery/attached_surgery + /// if you're a lazy git and just throw them in cryo, the wound will go away after accumulating severity * 25 power + var/cryo_progress + + /// What kind of scars this wound will create description wise once healed + var/list/scarring_descriptions = list("general disfigurement") + /// If we've already tried scarring while removing (since remove_wound calls qdel, and qdel calls remove wound, .....) TODO: make this cleaner + var/already_scarred = FALSE + /// If we forced this wound through badmin smite, we won't count it towards the round totals + var/from_smite + +/datum/wound/Destroy() + if(attached_surgery) + QDEL_NULL(attached_surgery) + if(limb?.wounds && (src in limb.wounds)) // destroy can call remove_wound() and remove_wound() calls qdel, so we check to make sure there's anything to remove first + remove_wound() + limb = null + victim = null + return ..() + +/** + * apply_wound() is used once a wound type is instantiated to assign it to a bodypart, and actually come into play. + * + * + * Arguments: + * * L: The bodypart we're wounding, we don't care about the person, we can get them through the limb + * * silent: Not actually necessary I don't think, was originally used for demoting wounds so they wouldn't make new messages, but I believe old_wound took over that, I may remove this shortly + * * old_wound: If our new wound is a replacement for one of the same time (promotion or demotion), we can reference the old one just before it's removed to copy over necessary vars + * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) + */ +/datum/wound/proc/apply_wound(obj/item/bodypart/L, silent = FALSE, datum/wound/old_wound = null, smited = FALSE) + if(!istype(L) || !L.owner || !(L.body_zone in viable_zones) || isalien(L.owner)) + qdel(src) + return + + if(ishuman(L.owner)) + var/mob/living/carbon/human/H = L.owner + if(organic_only && ((NOBLOOD in H.dna.species.species_traits) || !L.is_organic_limb())) + qdel(src) + return + + // we accept promotions and demotions, but no point in redundancy. This should have already been checked wherever the wound was rolled and applied for (see: bodypart damage code), but we do an extra check + // in case we ever directly add wounds + for(var/i in L.wounds) + var/datum/wound/preexisting_wound = i + if((preexisting_wound.type == type) && (preexisting_wound != old_wound)) + qdel(src) + return + + victim = L.owner + limb = L + LAZYADD(victim.all_wounds, src) + LAZYADD(limb.wounds, src) + limb.update_wounds() + if(status_effect_type) + linked_status_effect = victim.apply_status_effect(status_effect_type, src) + SEND_SIGNAL(victim, COMSIG_CARBON_GAIN_WOUND, src, limb) + if(!victim.alerts["wound"]) // only one alert is shared between all of the wounds + victim.throw_alert("wound", /obj/screen/alert/status_effect/wound) + + var/demoted + if(old_wound) + demoted = (severity <= old_wound.severity) + + if(severity == WOUND_SEVERITY_TRIVIAL) + return + + if(!(silent || demoted)) + var/msg = "[victim]'s [limb.name] [occur_text]!" + var/vis_dist = COMBAT_MESSAGE_RANGE + + if(severity != WOUND_SEVERITY_MODERATE) + msg = "[msg]" + vis_dist = DEFAULT_MESSAGE_RANGE + + victim.visible_message(msg, "Your [limb.name] [occur_text]!", vision_distance = vis_dist) + if(sound_effect) + playsound(L.owner, sound_effect, 60 + 20 * severity, TRUE) + + if(!demoted) + wound_injury(old_wound) + second_wind() + +/// Remove the wound from whatever it's afflicting, and cleans up whateverstatus effects it had or modifiers it had on interaction times. ignore_limb is used for detachments where we only want to forget the victim +/datum/wound/proc/remove_wound(ignore_limb, replaced = FALSE) + //TODO: have better way to tell if we're getting removed without replacement (full heal) scar stuff + if(limb && !already_scarred && !replaced) + already_scarred = TRUE + var/datum/scar/new_scar = new + new_scar.generate(limb, src) + if(victim) + LAZYREMOVE(victim.all_wounds, src) + if(!victim.all_wounds) + victim.clear_alert("wound") + SEND_SIGNAL(victim, COMSIG_CARBON_LOSE_WOUND, src, limb) + if(limb && !ignore_limb) + LAZYREMOVE(limb.wounds, src) + limb.update_wounds() + +/** + * replace_wound() is used when you want to replace the current wound with a new wound, presumably of the same category, just of a different severity (either up or down counts) + * + * This proc actually instantiates the new wound based off the specific type path passed, then returns the new instantiated wound datum. + * + * Arguments: + * * new_type- The TYPE PATH of the wound you want to replace this, like /datum/wound/brute/cut/severe + * * smited- If this is a smite, we don't care about this wound for stat tracking purposes (not yet implemented) + */ +/datum/wound/proc/replace_wound(new_type, smited = FALSE) + var/datum/wound/new_wound = new new_type + already_scarred = TRUE + remove_wound(replaced=TRUE) + new_wound.apply_wound(limb, old_wound = src, smited = smited) + qdel(src) + return new_wound + +/// The immediate negative effects faced as a result of the wound +/datum/wound/proc/wound_injury(datum/wound/old_wound = null) + return + +/// Additional beneficial effects when the wound is gained, in case you want to give a temporary boost to allow the victim to try an escape or last stand +/datum/wound/proc/second_wind() + if(HAS_TRAIT(victim, TRAIT_NOMETABOLISM)) + return + + switch(severity) + if(WOUND_SEVERITY_MODERATE) + victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_MODERATE) + if(WOUND_SEVERITY_SEVERE) + victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_SEVERE) + if(WOUND_SEVERITY_CRITICAL) + victim.reagents.add_reagent(/datum/reagent/determination, WOUND_DETERMINATION_CRITICAL) + +/** + * try_treating() is an intercept run from [/mob/living/carbon/attackby()] right after surgeries but before anything else. Return TRUE here if the item is something that is relevant to treatment to take over the interaction. + * + * This proc leads into [/datum/wound/proc/treat()] and probably shouldn't be added onto in children types. You can specify what items or tools you want to be intercepted + * with var/list/treatable_by and var/treatable_tool, then if an item fulfills one of those requirements and our wound claims it first, it goes over to treat() and treat_self(). + * + * Arguments: + * * I: The item we're trying to use + * * user: The mob trying to use it on us + */ +/datum/wound/proc/try_treating(obj/item/I, mob/user) + // first we weed out if we're not dealing with our wound's bodypart, or if it might be an attack + if(!I || limb.body_zone != user.zone_selected || (I.force && user.a_intent != INTENT_HELP)) + return FALSE + + var/allowed = FALSE + + // check if we have a valid treatable tool (or, if cauteries are allowed, if we have something hot) + if((I.tool_behaviour == treatable_tool) || (treatable_tool == TOOL_CAUTERY && I.get_temperature())) + allowed = TRUE + // failing that, see if we're aggro grabbing them and if we have an item that works for aggro grabs only + else if(user.pulling == victim && user.grab_state >= GRAB_AGGRESSIVE && check_grab_treatments(I, user)) + allowed = TRUE + // failing THAT, we check if we have a generally allowed item + else + for(var/allowed_type in treatable_by) + if(istype(I, allowed_type)) + allowed = TRUE + break + + // if none of those apply, we return false to avoid interrupting + if(!allowed) + return FALSE + + // now that we've determined we have a valid attempt at treating, we can stomp on their dreams if we're already interacting with the patient + if(INTERACTING_WITH(user, victim)) + to_chat(user, "You're already interacting with [victim]!") + return TRUE + + // lastly, treat them + treat(I, user) + return TRUE + +/// Return TRUE if we have an item that can only be used while aggro grabbed (unhanded aggro grab treatments go in [/datum/wound/proc/try_handling()]). Treatment is still is handled in [/datum/wound/proc/treat()] +/datum/wound/proc/check_grab_treatments(obj/item/I, mob/user) + return FALSE + +/// Like try_treating() but for unhanded interactions from humans, used by joint dislocations for manual bodypart chiropractice for example. +/datum/wound/proc/try_handling(mob/living/carbon/human/user) + return FALSE + +/// Someone is using something that might be used for treating the wound on this limb +/datum/wound/proc/treat(obj/item/I, mob/user) + return + +/// If var/processing is TRUE, this is run on each life tick +/datum/wound/proc/handle_process() + return + +/// For use in do_after callback checks +/datum/wound/proc/still_exists() + return (!QDELETED(src) && limb) + +/// When our parent bodypart is hurt +/datum/wound/proc/receive_damage(wounding_type, wounding_dmg, wound_bonus) + return + +/// Called from cryoxadone and pyroxadone when they're proc'ing. Wounds will slowly be fixed separately from other methods when these are in effect. crappy name but eh +/datum/wound/proc/on_xadone(power) + cryo_progress += power + if(cryo_progress > 33 * severity) + qdel(src) + +/// Called when we're crushed in an airlock or firedoor, for one of the improvised joint dislocation fixes +/datum/wound/proc/crush() + return + +/** + * get_examine_description() is used in carbon/examine and human/examine to show the status of this wound. Useful if you need to show some status like the wound being splinted or bandaged. + * + * Return the full string line you want to show, note that we're already dealing with the 'warning' span at this point, and that \n is already appended for you in the place this is called from + * + * Arguments: + * * mob/user: The user examining the wound's owner, if that matters + */ +/datum/wound/proc/get_examine_description(mob/user) + return "[victim.p_their(TRUE)] [limb.name] [examine_desc]!" + +/datum/wound/proc/get_scanner_description(mob/user) + return "Type: [name]\nSeverity: [severity_text()]\nDescription: [desc]\nRecommended Treatment: [treat_text]" + +/datum/wound/proc/severity_text() + switch(severity) + if(WOUND_SEVERITY_TRIVIAL) + return "Trivial" + if(WOUND_SEVERITY_MODERATE) + return "Moderate" + if(WOUND_SEVERITY_SEVERE) + return "Severe" + if(WOUND_SEVERITY_CRITICAL) + return "Critical" diff --git a/code/datums/wounds/cuts.dm b/code/datums/wounds/cuts.dm index 61a4b8a1ea..5f9d754238 100644 --- a/code/datums/wounds/cuts.dm +++ b/code/datums/wounds/cuts.dm @@ -179,8 +179,7 @@ /datum/wound/brute/cut/proc/tool_cauterize(obj/item/I, mob/user) var/self_penalty_mult = (user == victim ? 1.5 : 1) user.visible_message("[user] begins cauterizing [victim]'s [limb.name] with [I]...", "You begin cauterizing [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - var/time_mod = user.mind?.get_skill_modifier(/datum/skill/healing, SKILL_SPEED_MODIFIER) || 1 - if(!do_after(user, base_treat_time * time_mod * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) return user.visible_message("[user] cauterizes some of the bleeding on [victim].", "You cauterize some of the bleeding on [victim].") @@ -200,8 +199,7 @@ /datum/wound/brute/cut/proc/suture(obj/item/stack/medical/suture/I, mob/user) var/self_penalty_mult = (user == victim ? 1.4 : 1) user.visible_message("[user] begins stitching [victim]'s [limb.name] with [I]...", "You begin stitching [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - var/time_mod = user.mind?.get_skill_modifier(/datum/skill/healing, SKILL_SPEED_MODIFIER) || 1 - if(!do_after(user, base_treat_time * time_mod * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) return user.visible_message("[user] stitches up some of the bleeding on [victim].", "You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].") var/blood_sutured = I.stop_bleeding / self_penalty_mult @@ -224,8 +222,7 @@ user.visible_message("[user] begins rewrapping the cuts on [victim]'s [limb.name] with [I]...", "You begin rewrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") else user.visible_message("[user] begins wrapping the cuts on [victim]'s [limb.name] with [I]...", "You begin wrapping the cuts on [user == victim ? "your" : "[victim]'s"] [limb.name] with [I]...") - var/time_mod = user.mind?.get_skill_modifier(/datum/skill/healing, SKILL_SPEED_MODIFIER) || 1 - if(!do_after(user, base_treat_time * time_mod, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + if(!do_after(user, base_treat_time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) return user.visible_message("[user] applies [I] to [victim]'s [limb.name].", "You bandage some of the bleeding on [user == victim ? "yourself" : "[victim]"].") diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index ba3eba9bd3..c54c49b7e5 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -10,6 +10,11 @@ var/damtype = BRUTE var/force = 0 + /// How good a given object is at causing wounds on carbons. Higher values equal better shots at creating serious wounds. + var/wound_bonus = 0 + /// If this attacks a human with no wound armor on the affected body part, add this to the wound mod. Some attacks may be significantly worse at wounding if there's even a slight layer of armor to absorb some of it vs bare flesh + var/bare_wound_bonus = 0 + var/datum/armor/armor var/obj_integrity //defaults to max_integrity var/max_integrity = 500 diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index bfa9c40a7c..ea42c4df2f 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -79,7 +79,7 @@ send_item_attack_message(I, user, affecting.name, totitemdamage) I.do_stagger_action(src, user, totitemdamage) if(I.force) - apply_damage(totitemdamage, I.damtype, affecting) //CIT CHANGE - replaces I.force with totitemdamage + apply_damage(totitemdamage, I.damtype, affecting, wound_bonus = I.wound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness()) //CIT CHANGE - replaces I.force with totitemdamage if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC) var/basebloodychance = affecting.brute_dam + totitemdamage if(prob(basebloodychance)) @@ -132,6 +132,9 @@ if(S.next_step(user, user.a_intent)) return TRUE + for(var/datum/wound/W in all_wounds) + if(W.try_handling(user)) + return 1 /mob/living/carbon/attack_paw(mob/living/carbon/monkey/M) @@ -467,3 +470,8 @@ if (BP.status < 2) amount += BP.burn_dam return amount + +/mob/living/carbon/proc/get_interaction_efficiency(zone) + var/obj/item/bodypart/limb = get_bodypart(zone) + if(!limb) + return diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index 15413f76d4..cce3fb6a9b 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -64,3 +64,8 @@ var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects var/tackling = FALSE //Whether or not we are tackling, this will prevent the knock into effects for carbons + + /// All of the wounds a carbon has afflicted throughout their limbs + var/list/all_wounds + /// All of the scars a carbon has afflicted throughout their limbs + var/list/all_scars diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index becff250b9..61b14b2cce 100644 --- a/code/modules/mob/living/carbon/damage_procs.dm +++ b/code/modules/mob/living/carbon/damage_procs.dm @@ -1,6 +1,6 @@ -/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE) +/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) var/hit_percent = (100-blocked)/100 if(!forced && hit_percent <= 0) @@ -21,13 +21,13 @@ switch(damagetype) if(BRUTE) if(BP) - if(damage > 0 ? BP.receive_damage(damage_amount) : BP.heal_damage(abs(damage_amount), 0)) + if(BP.receive_damage(damage_amount, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) update_damage_overlays() else //no bodypart, we deal damage with a more general method. adjustBruteLoss(damage_amount, forced = forced) if(BURN) if(BP) - if(damage > 0 ? BP.receive_damage(0, damage_amount) : BP.heal_damage(0, abs(damage_amount))) + if(BP.receive_damage(0, damage_amount, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) update_damage_overlays() else adjustFireLoss(damage_amount, forced = forced) @@ -202,12 +202,12 @@ //Damages ONE bodypart randomly selected from damagable ones. //It automatically updates damage overlays if necessary //It automatically updates health status -/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0) +/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) var/list/obj/item/bodypart/parts = get_damageable_bodyparts() if(!parts.len) return var/obj/item/bodypart/picked = pick(parts) - if(picked.receive_damage(brute, burn, stamina)) + if(picked.receive_damage(brute, burn, stamina,check_armor ? run_armor_check(picked, (brute ? "melee" : burn ? "fire" : stamina ? "bullet" : null)) : FALSE, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) update_damage_overlays() //Heal MANY bodyparts, in random order @@ -253,7 +253,7 @@ var/stamina_was = picked.stamina_dam - update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE) + update |= picked.receive_damage(brute_per_part, burn_per_part, stamina_per_part, FALSE, required_status, wound_bonus = CANT_WOUND) // disabling wounds from these for now cuz your entire body snapping cause your heart stopped would suck brute = round(brute - (picked.brute_dam - brute_was), DAMAGE_PRECISION) burn = round(burn - (picked.burn_dam - burn_was), DAMAGE_PRECISION) @@ -265,3 +265,12 @@ if(update) update_damage_overlays() update_stamina() + +///Returns a list of bodyparts with wounds (in case someone has a wound on an otherwise fully healed limb) +/mob/living/carbon/proc/get_wounded_bodyparts(brute = FALSE, burn = FALSE, stamina = FALSE, status) + var/list/obj/item/bodypart/parts = list() + for(var/X in bodyparts) + var/obj/item/bodypart/BP = X + if(LAZYLEN(BP.wounds)) + parts += BP + return parts diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index b07aab30d1..be046a3713 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -44,6 +44,8 @@ msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!\n" else msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n" + for(var/datum/wound/W in BP.wounds) + msg += "[W.get_examine_description(user)]\n" for(var/X in disabled) var/obj/item/bodypart/BP = X @@ -99,6 +101,22 @@ if(pulledby && pulledby.grab_state) msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n" + var/scar_severity = 0 + for(var/i in all_scars) + var/datum/scar/S = i + if(S.is_visible(user)) + scar_severity += S.severity + + switch(scar_severity) + if(1 to 2) + msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n" + if(3 to 4) + msg += "[t_He] [t_has] several bad scars, you can look again to take a closer look...\n" + if(5 to 6) + msg += "[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...\n" + if(7 to INFINITY) + msg += "[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...\n" + if(msg.len) . += "[msg.Join("")]" @@ -135,3 +153,25 @@ . += "[t_He] look[p_s()] ecstatic." SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) . += "*---------*" + +/mob/living/carbon/examine_more(mob/user) + if(!all_scars) + return ..() + + var/list/visible_scars + for(var/i in all_scars) + var/datum/scar/S = i + if(S.is_visible(user)) + LAZYADD(visible_scars, S) + + if(!visible_scars) + return ..() + + var/msg = list("You examine [src] closer, and note the following...") + for(var/i in visible_scars) + var/datum/scar/S = i + var/scar_text = S.get_examine_description(user) + if(scar_text) + msg += "[scar_text]" + + return msg diff --git a/code/modules/mob/living/carbon/human/damage_procs.dm b/code/modules/mob/living/carbon/human/damage_procs.dm index 651fec8415..5cd00b7e6a 100644 --- a/code/modules/mob/living/carbon/human/damage_procs.dm +++ b/code/modules/mob/living/carbon/human/damage_procs.dm @@ -1,5 +1,5 @@ +// depending on the species, it will run the corresponding apply_damage code there +/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) + return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage, wound_bonus, bare_wound_bonus, sharpness) -/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage) - // depending on the species, it will run the corresponding apply_damage code there - return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index d32184edb5..77137c7239 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -163,16 +163,18 @@ msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] stuck to [t_his] [BP.name]!\n" else msg += "[t_He] [t_has] \a [icon2html(I, user)] [I] embedded in [t_his] [BP.name]!\n" + for(var/datum/wound/W in BP.wounds) + msg += "[W.get_examine_description(user)]\n" for(var/X in disabled) var/obj/item/bodypart/BP = X var/damage_text - if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //Stamina is disabling the limb - damage_text = "limp and lifeless" - else - damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg - msg += "[capitalize(t_his)] [BP.name] is [damage_text]!\n" - + if(BP.is_disabled() != BODYPART_DISABLED_WOUND) // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!) + if(!(BP.get_damage(include_stamina = FALSE) >= BP.max_damage)) //we don't care if it's stamcritted + damage_text = "limp and lifeless" + else + damage_text = (BP.brute_dam >= BP.burn_dam) ? BP.heavy_brute_msg : BP.heavy_burn_msg + msg += "[capitalize(t_his)] [BP.name] is [damage_text]!\n" //stores missing limbs var/l_limbs_missing = 0 var/r_limbs_missing = 0 @@ -246,16 +248,40 @@ if(DISGUST_LEVEL_DISGUSTED to INFINITY) msg += "[t_He] look[p_s()] extremely disgusted.\n" - if(ShowAsPaleExamine()) - msg += "[t_He] [t_has] pale skin.\n" + var/apparent_blood_volume = blood_volume + if(skin_tone == "albino") + apparent_blood_volume -= 150 // enough to knock you down one tier + switch(apparent_blood_volume) + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) + msg += "[t_He] [t_has] pale skin.\n" + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) + msg += "[t_He] look[p_s()] like pale death.\n" + if(-INFINITY to BLOOD_VOLUME_BAD) + msg += "[t_He] resemble[p_s()] a crushed, empty juice pouch.\n" if(bleedsuppress) - msg += "[t_He] [t_is] bandaged with something.\n" - else if(bleed_rate) - if(bleed_rate >= 8) //8 is the rate at which heparin causes you to bleed - msg += "[t_He] [t_is] bleeding uncontrollably!\n" - else - msg += "[t_He] [t_is] bleeding!\n" + msg += "[t_He] [t_is] embued with a power that defies bleeding.\n" // only statues and highlander sword can cause this so whatever + else if(is_bleeding()) + var/list/obj/item/bodypart/bleeding_limbs = list() + + for(var/i in bodyparts) + var/obj/item/bodypart/BP = i + if(BP.get_bleed_rate()) + bleeding_limbs += BP + + var/num_bleeds = LAZYLEN(bleeding_limbs) + var/bleed_text = "[t_He] [t_is] bleeding from [t_his]" + switch(num_bleeds) + if(1 to 2) + bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]" + if(3 to INFINITY) + for(var/i in 1 to (num_bleeds - 1)) + var/obj/item/bodypart/BP = bleeding_limbs[i] + bleed_text += " [BP.name]," + bleed_text += " and [bleeding_limbs[num_bleeds].name]" + + bleed_text += "!\n" + msg += bleed_text if(reagents.has_reagent(/datum/reagent/teslium)) msg += "[t_He] [t_is] emitting a gentle blue glow!\n" @@ -331,6 +357,21 @@ if(digitalcamo) msg += "[t_He] [t_is] moving [t_his] body in an unnatural and blatantly inhuman manner.\n" + var/scar_severity = 0 + for(var/i in all_scars) + var/datum/scar/S = i + if(S.is_visible(user)) + scar_severity += S.severity + + switch(scar_severity) + if(1 to 2) + msg += "[t_He] [t_has] visible scarring, you can look again to take a closer look...\n" + if(3 to 4) + msg += "[t_He] [t_has] several bad scars, you can look again to take a closer look...\n" + if(5 to 6) + msg += "[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...\n" + if(7 to INFINITY) + msg += "[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...\n" if (length(msg)) . += "[msg.Join("")]" diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index a2d53f6f0a..033b3bf42e 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1055,10 +1055,21 @@ remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying) + /mob/living/carbon/human/do_after_coefficent() . = ..() . *= physiology.do_after_speed +/mob/living/carbon/human/is_bleeding() + if(NOBLOOD in dna.species.species_traits || bleedsuppress) + return FALSE + return ..() + +/mob/living/carbon/human/get_total_bleed_rate() + if(NOBLOOD in dna.species.species_traits) + return FALSE + return ..() + /mob/living/carbon/human/species var/race = null diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 63296021ff..c592301b96 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -34,6 +34,19 @@ protection += physiology.armor.getRating(d_type) return protection +///Get all the clothing on a specific body part +/mob/living/carbon/human/proc/clothingonpart(obj/item/bodypart/def_zone) + var/list/covering_part = list() + var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, back, gloves, shoes, belt, s_store, glasses, ears, wear_id, wear_neck) //Everything but pockets. Pockets are l_store and r_store. (if pockets were allowed, putting something armored, gloves or hats for example, would double up on the armor) + for(var/bp in body_parts) + if(!bp) + continue + if(bp && istype(bp , /obj/item/clothing)) + var/obj/item/clothing/C = bp + if(C.body_parts_covered & def_zone.body_part) + covering_part += C + return covering_part + /mob/living/carbon/human/on_hit(obj/item/projectile/P) if(dna && dna.species) dna.species.on_hit(P, src) @@ -106,7 +119,7 @@ visible_message("[user] [hulk_verb_continous] [src]!", \ "[user] [hulk_verb_continous] you!", null, COMBAT_MESSAGE_RANGE, null, user, "You [hulk_verb_simple] [src]!") - adjustBruteLoss(15) + apply_damage(15, BRUTE, wound_bonus=10) return 1 /mob/living/carbon/human/attack_hand(mob/user) @@ -217,16 +230,17 @@ if(!affecting) affecting = get_bodypart(BODY_ZONE_CHEST) var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration) - apply_damage(damage, M.melee_damage_type, affecting, armor) - + apply_damage(damage, M.melee_damage_type, affecting, armor, wound_bonus = M.wound_bonus, bare_wound_bonus = M.bare_wound_bonus, sharpness = M.sharpness) /mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M) . = ..() if(!.) //unsuccessful slime attack return var/damage = rand(5, 25) + var/wound_mod = -45 // 25^1.4=90, 90-45=45 if(M.is_adult) damage = rand(10, 35) + wound_mod = -90 // 35^1.4=145, 145-90=55 var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) if(!dam_zone) //Dismemberment successful @@ -236,7 +250,7 @@ if(!affecting) affecting = get_bodypart(BODY_ZONE_CHEST) var/armor_block = run_armor_check(affecting, "melee") - apply_damage(damage, BRUTE, affecting, armor_block) + apply_damage(damage, BRUTE, affecting, armor_block, wound_bonus=wound_mod) /mob/living/carbon/human/mech_melee_attack(obj/mecha/M) if(M.occupant.a_intent == INTENT_HARM) @@ -614,6 +628,20 @@ no_damage = TRUE to_send += "\t Your [LB.name] [HAS_TRAIT(src, TRAIT_SELF_AWARE) ? "has" : "is"] [status].\n" + for(var/thing in LB.wounds) + var/datum/wound/W = thing + var/msg + switch(W.severity) + if(WOUND_SEVERITY_TRIVIAL) + msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]." + if(WOUND_SEVERITY_MODERATE) + msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" + if(WOUND_SEVERITY_SEVERE) + msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" + if(WOUND_SEVERITY_CRITICAL) + msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!!" + to_chat(src, msg) + for(var/obj/item/I in LB.embedded_objects) if(I.isEmbedHarmless()) to_chat(src, "\t There is \a [I] stuck to your [LB.name]!") @@ -623,8 +651,25 @@ for(var/t in missing) to_send += "Your [parse_zone(t)] is missing!\n" - if(bleed_rate) - to_send += "You are bleeding!\n" + if(is_bleeding()) + var/list/obj/item/bodypart/bleeding_limbs = list() + for(var/i in bodyparts) + var/obj/item/bodypart/BP = i + if(BP.get_bleed_rate()) + bleeding_limbs += BP + + var/num_bleeds = LAZYLEN(bleeding_limbs) + var/bleed_text = "You are bleeding from your" + switch(num_bleeds) + if(1 to 2) + bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]" + if(3 to INFINITY) + for(var/i in 1 to (num_bleeds - 1)) + var/obj/item/bodypart/BP = bleeding_limbs[i] + bleed_text += " [BP.name]," + bleed_text += " and [bleeding_limbs[num_bleeds].name]" + bleed_text += "!" + to_chat(src, bleed_text) if(getStaminaLoss()) if(getStaminaLoss() > 30) to_send += "You're completely exhausted.\n" diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index e7be540eb9..71005334f8 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -49,7 +49,6 @@ var/special_voice = "" // For changing our voice. Used by a symptom. - var/bleed_rate = 0 //how much are we bleeding var/bleedsuppress = 0 //for stopping bloodloss, eventually this will be limb-based like bleeding var/blood_state = BLOOD_STATE_NOT_BLOODY diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 7c256f5367..8672c0e83d 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -151,3 +151,22 @@ if(blood_dna.len) last_bloodtype = blood_dna[blood_dna[blood_dna.len]]//trust me this works last_blood_DNA = blood_dna[blood_dna.len]*/ + +/// For use formatting all of the scars this human has for saving for persistent scarring +/mob/living/carbon/human/proc/format_scars() + if(!all_scars) + return + var/scars = "" + for(var/i in all_scars) + var/datum/scar/S = i + scars += "[S.format()];" + return scars + +/// Takes a single scar from the persistent scar loader and recreates it from the saved data +/mob/living/carbon/human/proc/load_scar(scar_line) + var/list/scar_data = splittext(scar_line, "|") + if(LAZYLEN(scar_data) != 4) + return // invalid, should delete + var/obj/item/bodypart/BP = get_bodypart("[scar_data[SCAR_SAVE_ZONE]]") + var/datum/scar/S = new + return S.load(BP, scar_data[SCAR_SAVE_DESC], scar_data[SCAR_SAVE_PRECISE_LOCATION], text2num(scar_data[SCAR_SAVE_SEVERITY])) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index efff888c41..50f29a866f 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1728,9 +1728,15 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/armor_block = H.run_armor_check(affecting, "melee", "Your armor has protected your [hit_area].", "Your armor has softened a hit to your [hit_area].",I.armour_penetration) armor_block = min(90,armor_block) //cap damage reduction at 90% var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + var/Iwound_bonus = I.wound_bonus + + // this way, you can't wound with a surgical tool on help intent if they have a surgery active and are laying down, so a misclick with a circular saw on the wrong limb doesn't bleed them dry (they still get hit tho) + if((I.item_flags & SURGICAL_TOOL) && user.a_intent == INTENT_HELP && (H.mobility_flags & ~MOBILITY_STAND) && (LAZYLEN(H.surgeries) > 0)) + Iwound_bonus = CANT_WOUND var/weakness = H.check_weakness(I, user) - apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H) //CIT CHANGE - replaces I.force with totitemdamage + apply_damage(totitemdamage * weakness, I.damtype, def_zone, armor_block, H, wound_bonus = Iwound_bonus, bare_wound_bonus = I.bare_wound_bonus, sharpness = I.get_sharpness()) + H.send_item_attack_message(I, user, hit_area, totitemdamage) @@ -1945,8 +1951,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) append_message += ", causing them to drop [target_held_item]" log_combat(user, target, "shoved", append_message) -/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE) - SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) +/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) + SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal var/hit_percent = (100-(blocked+armor))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 if(!forced && hit_percent <= 0) @@ -1973,7 +1979,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.damageoverlaytemp = 20 var/damage_amount = forced ? damage : damage * hit_percent * brutemod * H.physiology.brute_mod if(BP) - if(damage > 0 ? BP.receive_damage(damage_amount, 0) : BP.heal_damage(abs(damage_amount), 0)) + if(BP.receive_damage(damage_amount, 0, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) H.update_damage_overlays() if(HAS_TRAIT(H, TRAIT_MASO) && prob(damage_amount)) H.mob_climax(forced_climax=TRUE) @@ -1984,7 +1990,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) H.damageoverlaytemp = 20 var/damage_amount = forced ? damage : damage * hit_percent * burnmod * H.physiology.burn_mod if(BP) - if(damage > 0 ? BP.receive_damage(0, damage_amount) : BP.heal_damage(0, abs(damage_amount))) + if(BP.receive_damage(0, damage_amount, wound_bonus = wound_bonus, bare_wound_bonus = bare_wound_bonus, sharpness = sharpness)) H.update_damage_overlays() else H.adjustFireLoss(damage_amount) diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index 606b7a8bfd..1868bd22df 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -3,7 +3,7 @@ id = "human" default_color = "FFFFFF" - species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR) + species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY,WINGCOLOR,CAN_SCAR) mutant_bodyparts = list("mcolor" = "FFF", "mcolor2" = "FFF","mcolor3" = "FFF","tail_human" = "None", "ears" = "None", "taur" = "None", "deco_wings" = "None") use_skintones = USE_SKINTONES_GRAYSCALE_CUSTOM skinned_type = /obj/item/stack/sheet/animalhide/human diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index c42e0bf175..a424969175 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -4,7 +4,7 @@ id = "lizard" say_mod = "hisses" default_color = "00FF00" - species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR) + species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,LIPS,HORNCOLOR,WINGCOLOR,CAN_SCAR) mutant_bodyparts = list("tail_lizard", "snout", "spines", "horns", "frills", "body_markings", "legs", "taur", "deco_wings") inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_REPTILE mutanttongue = /obj/item/organ/tongue/lizard diff --git a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm index dcb6d868fc..8e8c65b15e 100644 --- a/code/modules/mob/living/carbon/human/species_types/mushpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/mushpeople.dm @@ -8,7 +8,7 @@ nojumpsuit = TRUE say_mod = "poofs" //what does a mushroom sound like - species_traits = list(MUTCOLORS, NOEYES, NO_UNDERWEAR,NOGENITALS,NOAROUSAL) + species_traits = list(MUTCOLORS, NOEYES, NO_UNDERWEAR,NOGENITALS,NOAROUSAL,CAN_SCAR) inherent_traits = list(TRAIT_NOBREATH) speedmod = 1.5 //faster than golems but not by much diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm index e79160da06..36a1d52cba 100644 --- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm @@ -3,7 +3,7 @@ name = "Anthromorphic Plant" id = "pod" default_color = "59CE00" - species_traits = list(MUTCOLORS,EYECOLOR) + species_traits = list(MUTCOLORS,EYECOLOR,CAN_SCAR) attack_verb = "slash" attack_sound = 'sound/weapons/slice.ogg' miss_sound = 'sound/weapons/slashmiss.ogg' diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index fede67b47a..da03340ff9 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -10,7 +10,7 @@ ignored_by = list(/mob/living/simple_animal/hostile/faithless) meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/shadow species_traits = list(NOBLOOD,NOEYES) - inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_NOBREATH) + inherent_traits = list(TRAIT_RADIMMUNE,TRAIT_VIRUSIMMUNE,TRAIT_NOBREATH,CAN_SCAR) dangerous_existence = 1 mutanteyes = /obj/item/organ/eyes/night_vision diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 26a99dbc2b..c0369e7711 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -8,7 +8,7 @@ sexes = 0 blacklisted = 1 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/zombie - species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING) + species_traits = list(NOBLOOD,NOZOMBIE,NOTRANSSTING,CAN_SCAR) inherent_traits = list(TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_RADIMMUNE,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NOBREATH,TRAIT_NODEATH,TRAIT_FAKEDEATH) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID mutanttongue = /obj/item/organ/tongue/zombie @@ -45,7 +45,7 @@ /datum/species/zombie/infectious/spec_stun(mob/living/carbon/human/H,amount) . = min(20, amount) -/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE) +/datum/species/zombie/infectious/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) . = ..() if(.) regen_cooldown = world.time + REGENERATION_DELAY @@ -62,6 +62,10 @@ heal_amt *= 2 C.heal_overall_damage(heal_amt,heal_amt) C.adjustToxLoss(-heal_amt) + for(var/i in C.all_wounds) + var/datum/wound/W = i + if(prob(4-W.severity)) + W.remove_wound() if(!C.InCritical() && prob(4)) playsound(C, pick(spooks), 50, TRUE, 10) diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index b40d87a68a..725feb214c 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -404,6 +404,12 @@ if(stat != DEAD || D.process_dead) D.stage_act() +/mob/living/carbon/handle_wounds() + for(var/thing in all_wounds) + var/datum/wound/W = thing + if(W.processes) // meh + W.handle_process() + //todo generalize this and move hud out /mob/living/carbon/proc/handle_changeling() if(mind && hud_used && hud_used.lingchemdisplay) diff --git a/code/modules/mob/living/carbon/monkey/punpun.dm b/code/modules/mob/living/carbon/monkey/punpun.dm index c5cb4dc713..c59218a8a3 100644 --- a/code/modules/mob/living/carbon/monkey/punpun.dm +++ b/code/modules/mob/living/carbon/monkey/punpun.dm @@ -26,6 +26,8 @@ //These have to be after the parent new to ensure that the monkey //bodyparts are actually created before we try to equip things to //those slots + if(ancestor_chain > 1) + generate_fake_scars(rand(ancestor_chain, ancestor_chain * 4)) if(relic_hat) equip_to_slot_or_del(new relic_hat, SLOT_HEAD) if(relic_mask) diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 0c049ab5c1..012808647e 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -14,7 +14,7 @@ * * Returns TRUE if damage applied */ -/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE) +/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) var/hit_percent = (100-blocked)/100 if(!damage || (hit_percent <= 0)) return 0 @@ -245,7 +245,7 @@ update_stamina() // damage ONE external organ, organ gets randomly selected from damaged ones. -/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE) +/mob/living/proc/take_bodypart_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, required_status, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) adjustBruteLoss(brute, FALSE) //zero as argument for no instant health update adjustFireLoss(burn, FALSE) adjustStaminaLoss(stamina, FALSE) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 1993d86b74..e20567121e 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -45,6 +45,8 @@ /mob/living/proc/BiologicalLife(seconds, times_fired) handle_diseases()// DEAD check is in the proc itself; we want it to spread even if the mob is dead, but to handle its disease-y properties only if you're not. + handle_wounds() + // Everything after this shouldn't process while dead (as of the time of writing) if(stat == DEAD) return FALSE @@ -109,6 +111,9 @@ /mob/living/proc/handle_diseases() return +/mob/living/proc/handle_wounds() + return + /mob/living/proc/handle_diginvis() if(!digitaldisguise) src.digitaldisguise = image(loc = src) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index c7ed6e743d..7fe96b0af7 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -636,7 +636,7 @@ TH.transfer_mob_blood_dna(src) /mob/living/carbon/human/makeTrail(turf/T) - if((NOBLOOD in dna.species.species_traits) || !bleed_rate || bleedsuppress) + if((NOBLOOD in dna.species.species_traits) || !is_bleeding() || bleedsuppress) return ..() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 07252b4c45..d3afad23aa 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -85,7 +85,7 @@ totaldamage = block_calculate_resultant_damage(totaldamage, returnlist) var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null) if(!P.nodamage) - apply_damage(totaldamage, P.damage_type, def_zone, armor) + apply_damage(P.damage, P.damage_type, def_zone, armor, wound_bonus=P.wound_bonus, bare_wound_bonus=P.bare_wound_bonus, sharpness=P.sharpness) if(P.dismemberment) check_projectile_dismemberment(P, def_zone) var/missing = 100 - final_percent @@ -138,7 +138,7 @@ visible_message("[src] has been hit by [I].", \ "You have been hit by [I].") var/armor = run_armor_check(impacting_zone, "melee", "Your armor has protected your [parse_zone(impacting_zone)].", "Your armor has softened hit to your [parse_zone(impacting_zone)].",I.armour_penetration) - apply_damage(total_damage, dtype, impacting_zone, armor) + apply_damage(total_damage, dtype, impacting_zone, armor, sharpness=I.sharpness) if(I.thrownby) log_combat(I.thrownby, src, "threw and hit", I) else diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm index 2a03afb61a..b89f249c80 100644 --- a/code/modules/mob/living/silicon/damage_procs.dm +++ b/code/modules/mob/living/silicon/damage_procs.dm @@ -1,5 +1,5 @@ -/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE) +/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) var/hit_percent = (100-blocked)/100 if(!damage || (!forced && hit_percent <= 0)) return 0 diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 8f103c496e..d50808c92a 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -334,9 +334,11 @@ /obj/item/surgicaldrill, /obj/item/scalpel, /obj/item/circular_saw, + /obj/item/bonesetter, /obj/item/roller/robo, /obj/item/borg/cyborghug/medical, /obj/item/stack/medical/gauze/cyborg, + /obj/item/stack/medical/bone_gel/cyborg, /obj/item/organ_storage, /obj/item/borg/lollipop, /obj/item/sensor_device, diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index 5dbf3e8e3b..dc3c90cc64 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -29,8 +29,11 @@ var/armored = FALSE obj_damage = 60 - melee_damage_lower = 20 - melee_damage_upper = 30 + melee_damage_lower = 15 // i know it's like half what it used to be, but bears cause bleeding like crazy now so it works out + melee_damage_upper = 15 + wound_bonus = -5 + bare_wound_bonus = 10 // BEAR wound bonus am i right + sharpness = TRUE attack_verb_continuous = "claws" attack_verb_simple = "claw" attack_sound = 'sound/weapons/bladeslice.ogg' @@ -69,8 +72,9 @@ icon_dead = "combatbear_dead" faction = list("russian") butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab/bear = 5, /obj/item/clothing/head/bearpelt = 1, /obj/item/bear_armor = 1) - melee_damage_lower = 25 - melee_damage_upper = 35 + melee_damage_lower = 18 + melee_damage_upper = 20 + wound_bonus = 0 armour_penetration = 20 health = 120 maxHealth = 120 @@ -99,8 +103,9 @@ A.maxHealth += 60 A.health += 60 A.armour_penetration += 20 - A.melee_damage_lower += 5 + A.melee_damage_lower += 3 A.melee_damage_upper += 5 + A.wound_bonus += 5 A.update_icons() to_chat(user, "You strap the armor plating to [A] and sharpen [A.p_their()] claws with the nail filer. This was a great idea.") qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index a66ee7b1de..c00d7c3130 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -643,7 +643,7 @@ Difficulty: Normal to_chat(L, "You're struck by a [name]!") var/limb_to_hit = L.get_bodypart(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!") - L.apply_damage(damage, BURN, limb_to_hit, armor) + L.apply_damage(damage, BURN, limb_to_hit, armor, wound_bonus=CANT_WOUND) if(ishostile(L)) var/mob/living/simple_animal/hostile/H = L //mobs find and damage you... if(H.stat == CONSCIOUS && !H.target && H.AIStatus != AI_OFF && !H.client) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 4da8a90b23..78c02fb7a7 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -52,6 +52,8 @@ Difficulty: Medium elimination = 1 appearance_flags = 0 mouse_opacity = MOUSE_OPACITY_ICON + wound_bonus = -40 + bare_wound_bonus = 20 /mob/living/simple_animal/hostile/megafauna/legion/Initialize() . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm index ed189f052d..869f29951b 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/wendigo.dm @@ -129,7 +129,7 @@ Difficulty: Hard to_chat(L, "[src]'s ground slam shockwave sends you flying!") var/turf/thrownat = get_ranged_target_turf_direct(src, L, 8, rand(-10, 10)) L.throw_at(thrownat, 8, 2, src, TRUE) //, force = MOVE_FORCE_OVERPOWERING, gentle = TRUE) - L.apply_damage(20, BRUTE) + L.apply_damage(20, BRUTE, wound_bonus=CANT_WOUND) shake_camera(L, 2, 1) all_turfs -= T sleep(delay) diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 9a362b680d..4ea8a3c5dc 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -77,6 +77,9 @@ /mob/living/simple_animal/hostile/syndicate/melee melee_damage_lower = 15 melee_damage_upper = 15 + wound_bonus = -10 + bare_wound_bonus = 20 + sharpness = TRUE icon_state = "syndicate_knife" icon_living = "syndicate_knife" loot = list(/obj/effect/gibspawner/human) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 1a646ea73e..1102559aca 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -140,6 +140,13 @@ ///What kind of footstep this mob should have. Null if it shouldn't have any. var/footstep_type + //How much wounding power it has + var/wound_bonus = CANT_WOUND + //How much bare wounding power it has + var/bare_wound_bonus = 0 + //If the attacks from this are sharp + var/sharpness = FALSE + /mob/living/simple_animal/Initialize() . = ..() GLOB.simple_animals[AIStatus] += src diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm index ea84e23d01..db2914e620 100644 --- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm +++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm @@ -8,6 +8,18 @@ projectile_type = /obj/item/projectile/bullet/shotgun_slug custom_materials = list(/datum/material/iron=4000) +obj/item/ammo_casing/shotgun/executioner + name = "executioner slug" + desc = "A 12 gauge lead slug purpose built to annihilate flesh on impact." + icon_state = "stunshell" + projectile_type = /obj/projectile/bullet/shotgun_slug/executioner + +/obj/item/ammo_casing/shotgun/pulverizer + name = "pulverizer slug" + desc = "A 12 gauge lead slug purpose built to annihilate bones on impact." + icon_state = "stunshell" + projectile_type = /obj/projectile/bullet/shotgun_slug/pulverizer + /obj/item/ammo_casing/shotgun/beanbag name = "beanbag slug" desc = "A weak beanbag slug for riot control." diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm index 174645dd11..5c19edc012 100644 --- a/code/modules/projectiles/ammunition/energy/laser.dm +++ b/code/modules/projectiles/ammunition/energy/laser.dm @@ -37,6 +37,11 @@ select_name = "anti-vehicle" fire_sound = 'sound/weapons/lasercannonfire.ogg' +/obj/item/ammo_casing/energy/laser/hellfire + projectile_type = /obj/projectile/beam/laser/hellfire + e_cost = 130 + select_name = "maim" + /obj/item/ammo_casing/energy/laser/pulse projectile_type = /obj/item/projectile/beam/pulse e_cost = 200 diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index b90f0aee0d..f34343debd 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -184,6 +184,11 @@ return if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected) return + if(iscarbon(target)) + var/mob/living/carbon/C = target + for(var/datum/wound/W in C.all_wounds) + if(W.try_treating(src, user)) + return // another coward cured! if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. var/mob/living/L = user diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 72ac9620b5..8e61221cc8 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -44,6 +44,12 @@ ammo_type = list(/obj/item/ammo_casing/energy/lasergun/old) ammo_x_offset = 3 +/obj/item/gun/energy/laser/hellgun + name ="hellfire laser gun" + desc = "A relic of a weapon, built before NT began installing regulators on its laser weaponry. This pattern of laser gun became infamous for the gruesome burn wounds it caused, and was quietly discontinued once it began to affect NT's reputation." + icon_state = "hellgun" + ammo_type = list(/obj/item/ammo_casing/energy/laser/hellfire) + /obj/item/gun/energy/laser/captain name = "antique laser gun" icon_state = "caplaser" diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 7c988ca730..69e3991e39 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -154,6 +154,10 @@ ///If TRUE, hit mobs even if they're on the floor and not our target var/hit_stunned_targets = FALSE + wound_bonus = CANT_WOUND + /// For telling whether we want to roll for bone breaking or lacerations if we're bothering with wounds + var/sharpness = FALSE + /obj/item/projectile/Initialize() . = ..() permutated = list() diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 2f12f0f69b..7147754211 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -14,11 +14,26 @@ ricochets_max = 50 //Honk! ricochet_chance = 80 is_reflectable = TRUE + wound_bonus = -20 + bare_wound_bonus = 10 /obj/item/projectile/beam/laser tracer_type = /obj/effect/projectile/tracer/laser muzzle_type = /obj/effect/projectile/muzzle/laser impact_type = /obj/effect/projectile/impact/laser + wound_bonus = -30 + bare_wound_bonus = 40 + +//overclocked laser, does a bit more damage but has much higher wound power (-0 vs -20) +/obj/projectile/beam/laser/hellfire + name = "hellfire laser" + wound_bonus = 0 + damage = 25 + speed = 0.6 // higher power = faster, that's how light works right + +/obj/projectile/beam/laser/hellfire/Initialize() + . = ..() + transform *= 2 /obj/item/projectile/beam/laser/heavylaser name = "heavy laser" @@ -90,6 +105,7 @@ tracer_type = /obj/effect/projectile/tracer/pulse muzzle_type = /obj/effect/projectile/muzzle/pulse impact_type = /obj/effect/projectile/impact/pulse + wound_bonus = 10 /obj/item/projectile/beam/pulse/on_hit(atom/target, blocked = FALSE) . = ..() @@ -116,6 +132,8 @@ damage = 30 impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser light_color = LIGHT_COLOR_GREEN + wound_bonus = -40 + bare_wound_bonus = 70 /obj/item/projectile/beam/emitter/singularity_pull() return diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 6d03012315..6a081f9b03 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -8,3 +8,4 @@ flag = "bullet" hitsound_wall = "ricochet" impact_effect_type = /obj/effect/temp_visual/impact_effect + sharpness = TRUE \ No newline at end of file diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm index 5643804ac1..c793e9f95e 100644 --- a/code/modules/projectiles/projectile/bullets/revolver.dm +++ b/code/modules/projectiles/projectile/bullets/revolver.dm @@ -19,6 +19,8 @@ ricochet_chance = 50 ricochet_auto_aim_angle = 10 ricochet_auto_aim_range = 3 + wound_bonus = -35 + sharpness = TRUE /obj/item/projectile/bullet/c38/match name = ".38 Match bullet" @@ -29,6 +31,7 @@ ricochet_incidence_leeway = 50 ricochet_decay_chance = 1 ricochet_decay_damage = 1 + wound_bonus = 0 /obj/item/projectile/bullet/c38/match/bouncy name = ".38 Rubber bullet" diff --git a/code/modules/projectiles/projectile/bullets/shotgun.dm b/code/modules/projectiles/projectile/bullets/shotgun.dm index 264df22c76..b268877451 100644 --- a/code/modules/projectiles/projectile/bullets/shotgun.dm +++ b/code/modules/projectiles/projectile/bullets/shotgun.dm @@ -2,10 +2,22 @@ name = "12g shotgun slug" damage = 60 +/obj/projectile/bullet/shotgun_slug/executioner + name = "executioner slug" // admin only, can dismember limbs + sharpness = TRUE + wound_bonus = 0 + +/obj/projectile/bullet/shotgun_slug/pulverizer + name = "pulverizer slug" // admin only, can crush bones + sharpness = FALSE + wound_bonus = 0 + /obj/item/projectile/bullet/shotgun_beanbag name = "beanbag slug" - damage = 5 + damage = 10 stamina = 70 + wound_bonus = 20 + sharpness = FALSE /obj/item/projectile/bullet/incendiary/shotgun name = "incendiary slug" @@ -77,6 +89,7 @@ /obj/item/projectile/bullet/pellet/shotgun_buckshot name = "buckshot pellet" damage = 12.5 + wound_bonus = -10 /obj/item/projectile/bullet/pellet/shotgun_rubbershot name = "rubbershot pellet" diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index b9da8271ab..050b1f29b5 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -144,6 +144,9 @@ M.adjustFireLoss(-power, 0) M.adjustToxLoss(-power, 0, TRUE) //heals TOXINLOVERs M.adjustCloneLoss(-power, 0) + for(var/i in M.all_wounds) + var/datum/wound/W = i + W.on_xadone(power) REMOVE_TRAIT(M, TRAIT_DISFIGURED, TRAIT_GENERIC) //fixes common causes for disfiguration . = 1 metabolization_rate = REAGENTS_METABOLISM * (0.00001 * (M.bodytemperature ** 2) + 0.5) @@ -192,6 +195,9 @@ M.adjustFireLoss(-1.5 * power, 0) M.adjustToxLoss(-power, 0, TRUE) M.adjustCloneLoss(-power, 0) + for(var/i in M.all_wounds) + var/datum/wound/W = i + W.on_xadone(power) REMOVE_TRAIT(M, TRAIT_DISFIGURED, TRAIT_GENERIC) . = 1 ..() @@ -231,7 +237,7 @@ /datum/reagent/medicine/spaceacillin name = "Spaceacillin" - description = "Spaceacillin will prevent a patient from conventionally spreading any diseases they are currently infected with." + description = "Spaceacillin will prevent a patient from conventionally spreading any diseases they are currently infected with. Also reduces infection in serious burns." color = "#f2f2f2" metabolization_rate = 0.1 * REAGENTS_METABOLISM pH = 8.1 @@ -403,7 +409,7 @@ /datum/reagent/medicine/mine_salve name = "Miner's Salve" - description = "A powerful painkiller. Restores bruising and burns in addition to making the patient believe they are fully healed." + description = "A powerful painkiller. Restores bruising and burns in addition to making the patient believe they are fully healed. Also great for treating severe burn wounds in a pinch." reagent_state = LIQUID color = "#6D6374" metabolization_rate = 0.4 * REAGENTS_METABOLISM @@ -432,7 +438,7 @@ // +10% success propability on each step, useful while operating in less-than-perfect conditions if(show_message) - to_chat(M, "You feel your wounds fade away to nothing!" ) + to_chat(M, "You feel your injuries fade away to nothing!" ) ..() /datum/reagent/medicine/mine_salve/on_mob_end_metabolize(mob/living/M) @@ -453,10 +459,10 @@ /datum/reagent/medicine/synthflesh/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1) if(iscarbon(M)) - if (M.stat == DEAD) + var/mob/living/carbon/C = M + if(M.stat == DEAD) show_message = 0 if(method in list(INGEST, VAPOR)) - var/mob/living/carbon/C = M C.losebreath++ C.emote("cough") to_chat(M, "You feel your throat closing up!") @@ -465,6 +471,8 @@ else if(method in list(PATCH, TOUCH)) M.adjustBruteLoss(-1 * reac_volume) M.adjustFireLoss(-1 * reac_volume) + for(var/datum/wound/burn/burn_wound in C.all_wounds) + burn_wound.regenerate_flesh(reac_volume) if(show_message) to_chat(M, "You feel your burns and bruises healing! It stings like hell!") SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "painful_medicine", /datum/mood_event/painful_medicine) @@ -1552,10 +1560,6 @@ /datum/reagent/medicine/polypyr/on_mob_life(mob/living/carbon/M) //I wanted a collection of small positive effects, this is as hard to obtain as coniine after all. M.adjustOrganLoss(ORGAN_SLOT_LUNGS, -0.25) M.adjustBruteLoss(-0.35, 0) - if(prob(50)) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - H.bleed_rate = max(H.bleed_rate - 1, 0) ..() . = 1 @@ -1588,3 +1592,10 @@ to_chat(C, "[pick(GLOB.wisdoms)]") //give them a random wisdom ..() +// handled in cut wounds process +/datum/reagent/medicine/coagulant + name = "Sanguirite" + description = "A coagulant used to help open cuts clot faster." + reagent_state = LIQUID + color = "#bb2424" + metabolization_rate = 0.25 * REAGENTS_METABOLISM diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 988441fa2b..9dc091b2eb 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2296,3 +2296,36 @@ if(data["blood_DNA"]) S.add_blood_DNA(list(data["blood_DNA"] = data["blood_type"])) +/datum/reagent/determination + name = "Determination" + description = "For when you need to push on a little more. Do NOT allow near plants." + reagent_state = LIQUID + color = "#D2FFFA" + metabolization_rate = 0.75 * REAGENTS_METABOLISM // 5u (WOUND_DETERMINATION_CRITICAL) will last for ~17 ticks + /// Whether we've had at least WOUND_DETERMINATION_SEVERE (2.5u) of determination at any given time. No damage slowdown immunity or indication we're having a second wind if it's just a single moderate wound + var/significant = FALSE + +/datum/reagent/determination/on_mob_end_metabolize(mob/living/carbon/M) + if(significant) + var/stam_crash = 0 + for(var/thing in M.all_wounds) + var/datum/wound/W = thing + stam_crash += (W.severity + 1) * 3 // spike of 3 stam damage per wound severity (moderate = 6, severe = 9, critical = 12) when the determination wears off if it was a combat rush + M.adjustStaminaLoss(stam_crash) + M.remove_status_effect(STATUS_EFFECT_DETERMINED) + ..() + +/datum/reagent/determination/on_mob_life(mob/living/carbon/M) + if(!significant && volume >= WOUND_DETERMINATION_SEVERE) + significant = TRUE + M.apply_status_effect(STATUS_EFFECT_DETERMINED) // in addition to the slight healing, limping cooldowns are divided by 4 during the combat high + + volume = min(volume, WOUND_DETERMINATION_MAX) + + for(var/thing in M.all_wounds) + var/datum/wound/W = thing + var/obj/item/bodypart/wounded_part = W.limb + if(wounded_part) + wounded_part.heal_damage(0.25, 0.25) + M.adjustStaminaLoss(-0.25*REM) // the more wounds, the more stamina regen + ..() diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 02e4a89e1d..a763376caa 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -154,6 +154,11 @@ pH = 12 value = REAGENT_VALUE_RARE +/datum/reagent/toxin/carpotoxin/on_mob_life(mob/living/carbon/M) + . = ..() + for(var/i in M.all_scars) + qdel(i) + /datum/reagent/toxin/zombiepowder name = "Zombie Powder" description = "A strong neurotoxin that puts the subject into a death-like state." @@ -736,22 +741,13 @@ /datum/reagent/toxin/heparin //Based on a real-life anticoagulant. I'm not a doctor, so this won't be realistic. name = "Heparin" - description = "A powerful anticoagulant. Victims will bleed uncontrollably and suffer scaling bruising." + description = "A powerful anticoagulant. All open cut wounds on the victim will open up and bleed much faster" reagent_state = LIQUID color = "#C8C8C8" //RGB: 200, 200, 200 metabolization_rate = 0.2 * REAGENTS_METABOLISM toxpwr = 0 value = REAGENT_VALUE_VERY_RARE -/datum/reagent/toxin/heparin/on_mob_life(mob/living/carbon/M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - H.bleed_rate = min(H.bleed_rate + 2, 8) - H.adjustBruteLoss(1, 0) //Brute damage increases with the amount they're bleeding - . = 1 - return ..() || . - - /datum/reagent/toxin/rotatium //Rotatium. Fucks up your rotation and is hilarious name = "Rotatium" description = "A constantly swirling, oddly colourful fluid. Causes the consumer's sense of direction and hand-eye coordination to become wild." diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 799a6db9f5..35f5ebd46e 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -90,12 +90,12 @@ item_state = "medipen" lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' - amount_per_transfer_from_this = 13 - volume = 13 + amount_per_transfer_from_this = 15 + volume = 15 ignore_flags = 1 //so you can medipen through hardsuits reagent_flags = DRAWABLE flags_1 = null - list_reagents = list(/datum/reagent/medicine/epinephrine = 10, /datum/reagent/preservahyde = 3) + list_reagents = list(/datum/reagent/medicine/epinephrine = 10, /datum/reagent/preservahyde = 3, /datum/reagent/medicine/coagulant = 2) custom_premium_price = PRICE_ALMOST_EXPENSIVE /obj/item/reagent_containers/hypospray/medipen/suicide_act(mob/living/carbon/user) @@ -133,6 +133,13 @@ else . += "It is spent." +/obj/item/reagent_containers/hypospray/medipen/ekit + name = "emergency first-aid autoinjector" + desc = "An epinephrine medipen with trace amounts of coagulants and antibiotics to help stabilize bad cuts and burns." + volume = 15 + amount_per_transfer_from_this = 15 + list_reagents = list(/datum/reagent/medicine/epinephrine = 12, /datum/reagent/medicine/coagulant = 2.5, /datum/reagent/medicine/spaceacillin = 0.5) + /obj/item/reagent_containers/hypospray/medipen/stimulants name = "illegal stimpack medipen" desc = "A highly illegal medipen due to its load and small injections, allow for five uses before being drained" diff --git a/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm b/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm index 76ec6224b8..574c7c9282 100644 --- a/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm +++ b/code/modules/research/designs/autolathe_desings/autolathe_designs_medical_and_dinnerware.dm @@ -119,6 +119,24 @@ category = list("initial", "Medical","Tool Designs") departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE +/datum/design/bonesetter + name = "Bonesetter" + id = "bonesetter" + build_type = AUTOLATHE | PROTOLATHE + materials = list(/datum/material/iron = 1000) + build_path = /obj/item/bonesetter + category = list("initial", "Medical", "Tool Designs") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL | DEPARTMENTAL_FLAG_SCIENCE + +/datum/design/sticky_tape/surgical + name = "Surgical Tape" + id = "surgical_tape" + build_type = PROTOLATHE + materials = list(/datum/material/plastic = 500) + build_path = /obj/item/stack/sticky_tape/surgical + category = list("initial", "Medical") + departmental_flags = DEPARTMENTAL_FLAG_MEDICAL + /datum/design/beaker name = "Beaker" id = "beaker" diff --git a/code/modules/research/techweb/nodes/medical_nodes.dm b/code/modules/research/techweb/nodes/medical_nodes.dm index 71dd7c943c..3a9e654b81 100644 --- a/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/code/modules/research/techweb/nodes/medical_nodes.dm @@ -104,7 +104,7 @@ display_name = "Advanced Surgery Tools" description = "Refined and improved redesigns for the run-of-the-mill medical utensils." prereq_ids = list("adv_biotech", "adv_surgery") - design_ids = list("drapes", "retractor_adv", "surgicaldrill_adv", "scalpel_adv") + design_ids = list("drapes", "retractor_adv", "surgicaldrill_adv", "scalpel_adv", "bonesetter", "surgical_tape") research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) /datum/techweb_node/adv_surgery diff --git a/code/modules/spells/spell_types/bloodcrawl.dm b/code/modules/spells/spell_types/bloodcrawl.dm index e59fc2049d..39b0f1709e 100644 --- a/code/modules/spells/spell_types/bloodcrawl.dm +++ b/code/modules/spells/spell_types/bloodcrawl.dm @@ -25,6 +25,11 @@ /obj/effect/proc_holder/spell/bloodcrawl/perform(obj/effect/decal/cleanable/target, recharge = 1, mob/living/user = usr) if(istype(user)) + if(istype(user, /mob/living/simple_animal/slaughter)) + var/mob/living/simple_animal/slaughter/slaught = user + slaught.current_hitstreak = 0 + slaught.wound_bonus = initial(slaught.wound_bonus) + slaught.bare_wound_bonus = initial(slaught.bare_wound_bonus) if(phased) if(user.phasein(target)) phased = 0 diff --git a/code/modules/spells/spell_types/shapeshift.dm b/code/modules/spells/spell_types/shapeshift.dm index a8f9c8bce5..e513865246 100644 --- a/code/modules/spells/spell_types/shapeshift.dm +++ b/code/modules/spells/spell_types/shapeshift.dm @@ -105,7 +105,7 @@ var/damage_percent = (stored.maxHealth - stored.health)/stored.maxHealth; var/damapply = damage_percent * shape.maxHealth; - shape.apply_damage(damapply, source.convert_damage_type, forced = TRUE); + shape.apply_damage(damapply, source.convert_damage_type, forced = TRUE, wound_bonus=CANT_WOUND); slink = soullink(/datum/soullink/shapeshift, stored , shape) slink.source = src @@ -158,7 +158,7 @@ var/damage_percent = (shape.maxHealth - shape.health)/shape.maxHealth; var/damapply = stored.maxHealth * damage_percent - stored.apply_damage(damapply, source.convert_damage_type, forced = TRUE) + stored.apply_damage(damapply, source.convert_damage_type, forced = TRUE, wound_bonus=CANT_WOUND) qdel(shape) qdel(src) diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm similarity index 72% rename from code/modules/surgery/bodyparts/bodyparts.dm rename to code/modules/surgery/bodyparts/_bodyparts.dm index fcdb07ca17..711f90a629 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -71,6 +71,28 @@ var/medium_burn_msg = "blistered" var/heavy_burn_msg = "peeling away" + /// The wounds currently afflicting this body part + var/list/wounds + + /// The scars currently afflicting this body part + var/list/scars + /// Our current stored wound damage multiplier + var/wound_damage_multiplier = 1 + + /// This number is subtracted from all wound rolls on this bodypart, higher numbers mean more defense, negative means easier to wound + var/wound_resistance = 0 + /// When this bodypart hits max damage, this number is added to all wound rolls. Obviously only relevant for bodyparts that have damage caps. + var/disabled_wound_penalty = 15 + + /// A hat won't cover your face, but a shirt covering your chest will cover your... you know, chest + var/scars_covered_by_clothes = TRUE + /// Descriptions for the locations on the limb for scars to be assigned, just cosmetic + var/list/specific_locations = list("general area") + /// So we know if we need to scream if this limb hits max damage + var/last_maxed + /// How much generic bleedstacks we have on this bodypart + var/generic_bleedstacks + /obj/item/bodypart/examine(mob/user) . = ..() if(brute_dam > DAMAGE_PRECISION) @@ -149,7 +171,7 @@ //Applies brute and burn damage to the organ. Returns 1 if the damage-icon states changed at all. //Damage will not exceed max_damage using this proc //Cannot apply negative damage -/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE) +/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE) // maybe separate BRUTE_SHARP and BRUTE_OTHER eventually somehow hmm if(owner && (owner.status_flags & GODMODE)) return FALSE //godmode var/dmg_mlt = CONFIG_GET(number/damage_multiplier) @@ -163,20 +185,38 @@ if(!brute && !burn && !stamina) return FALSE + brute *= wound_damage_multiplier + burn *= wound_damage_multiplier + switch(animal_origin) if(ALIEN_BODYPART,LARVA_BODYPART) //aliens take some additional burn //nothing can burn with so much snowflake code around burn *= 1.2 + var/wounding_type = (brute > burn ? WOUND_BRUTE : WOUND_BURN) + var/wounding_dmg = max(brute, burn) + if(wounding_type == WOUND_BRUTE && sharpness) + wounding_type = WOUND_SHARP + // i know this is effectively the same check as above but i don't know if those can null the damage by rounding and want to be safe + if(owner && wounding_dmg > 4 && wound_bonus != CANT_WOUND) + // if you want to make tox wounds or some other type, this will need to be expanded and made more modular + // handle all our wounding stuff + check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus) + var/can_inflict = max_damage - get_damage() - if(can_inflict <= 0) - return FALSE var/total_damage = brute + burn - if(total_damage > can_inflict) + if(total_damage > can_inflict && total_damage > 0) // TODO: the second part of this check should be removed once disabling is all done brute = round(brute * (max_damage / total_damage),DAMAGE_PRECISION) burn = round(burn * (max_damage / total_damage),DAMAGE_PRECISION) + if(can_inflict <= 0) + return FALSE + + for(var/i in wounds) + var/datum/wound/W = i + W.receive_damage(wounding_type, wounding_dmg, wound_bonus) + brute_dam += brute burn_dam += burn @@ -196,6 +236,107 @@ update_disabled() return update_bodypart_damage_state() +/** + * check_wounding() is where we handle rolling for, selecting, and applying a wound if we meet the criteria + * + * We generate a "score" for how woundable the attack was based on the damage and other factors discussed in [check_wounding_mods()], then go down the list from most severe to least severe wounds in that category. + * We can promote a wound from a lesser to a higher severity this way, but we give up if we have a wound of the given type and fail to roll a higher severity, so no sidegrades/downgrades + * + * Arguments: + * * woundtype- Either WOUND_SHARP, WOUND_BRUTE, or WOUND_BURN based on the attack type. + * * damage- How much damage is tied to this attack, since wounding potential scales with damage in an attack (see: WOUND_DAMAGE_EXPONENT) + * * wound_bonus- The wound_bonus of an attack + * * bare_wound_bonus- The bare_wound_bonus of an attack + */ +/obj/item/bodypart/proc/check_wounding(woundtype, damage, wound_bonus, bare_wound_bonus) + // actually roll wounds if applicable + if(HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE)) + damage *= 1.5 + + var/base_roll = rand(1, round(damage ** WOUND_DAMAGE_EXPONENT)) + var/injury_roll = base_roll + injury_roll += check_woundings_mods(woundtype, damage, wound_bonus, bare_wound_bonus) + var/list/wounds_checking + + switch(woundtype) + if(WOUND_SHARP) + wounds_checking = WOUND_LIST_CUT + if(WOUND_BRUTE) + wounds_checking = WOUND_LIST_BONE + if(WOUND_BURN) + wounds_checking = WOUND_LIST_BURN + + //cycle through the wounds of the relevant category from the most severe down + for(var/PW in wounds_checking) + var/datum/wound/possible_wound = PW + var/datum/wound/replaced_wound + for(var/i in wounds) + var/datum/wound/existing_wound = i + if(existing_wound.type in wounds_checking) + if(existing_wound.severity >= initial(possible_wound.severity)) + return + else + replaced_wound = existing_wound + + if(initial(possible_wound.threshold_minimum) < injury_roll) + if(replaced_wound) + var/datum/wound/new_wound = replaced_wound.replace_wound(possible_wound) + log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) + else + var/datum/wound/new_wound = new possible_wound + new_wound.apply_wound(src) + log_wound(owner, new_wound, damage, wound_bonus, bare_wound_bonus, base_roll) + return + +// try forcing a specific wound, but only if there isn't already a wound of that severity or greater for that type on this bodypart +/obj/item/bodypart/proc/force_wound_upwards(specific_woundtype, smited = FALSE) + var/datum/wound/potential_wound = specific_woundtype + for(var/i in wounds) + var/datum/wound/existing_wound = i + if(existing_wound.type in (initial(potential_wound.wound_type))) + if(existing_wound.severity < initial(potential_wound.severity)) // we only try if the existing one is inferior to the one we're trying to force + existing_wound.replace_wound(potential_wound, smited) + return + + var/datum/wound/new_wound = new potential_wound + new_wound.apply_wound(src, smited = smited) + +/obj/item/bodypart/proc/check_woundings_mods(wounding_type, damage, wound_bonus, bare_wound_bonus) + var/armor_ablation = 0 + var/injury_mod = 0 + + //var/bwb = 0 + + if(owner && ishuman(owner)) + var/mob/living/carbon/human/H = owner + var/list/clothing = H.clothingonpart(src) + for(var/c in clothing) + var/obj/item/clothing/C = c + // unlike normal armor checks, we tabluate these piece-by-piece manually so we can also pass on appropriate damage the clothing's limbs if necessary + armor_ablation += C.armor.getRating("wound") + if(wounding_type == WOUND_SHARP) + C.take_damage_zone(body_zone, damage, BRUTE, armour_penetration) + else if(wounding_type == WOUND_BURN && damage >= 10) // lazy way to block freezing from shredding clothes without adding another var onto apply_damage() + C.take_damage_zone(body_zone, damage, BURN, armour_penetration) + + if(!armor_ablation) + injury_mod += bare_wound_bonus + + injury_mod -= armor_ablation + injury_mod += wound_bonus + + for(var/thing in wounds) + var/datum/wound/W = thing + injury_mod += W.threshold_penalty + + var/part_mod = -wound_resistance + if(is_disabled()) + part_mod += disabled_wound_penalty + + injury_mod += part_mod + + return injury_mod + //Heals brute and burn damage for the organ. Returns 1 if the damage-icon states changed at all. //Damage cannot go below zero. //Cannot remove negative damage (i.e. apply damage) @@ -227,16 +368,29 @@ //Checks disabled status thresholds /obj/item/bodypart/proc/update_disabled() + if(!owner) + return set_disabled(is_disabled()) /obj/item/bodypart/proc/is_disabled() + if(!owner) + return if(HAS_TRAIT(owner, TRAIT_PARALYSIS)) return BODYPART_DISABLED_PARALYSIS + for(var/i in wounds) + var/datum/wound/W = i + if(W.disabling) + return BODYPART_DISABLED_WOUND if(can_dismember() && !HAS_TRAIT(owner, TRAIT_NODISMEMBER)) . = disabled //inertia, to avoid limbs healing 0.1 damage and being re-enabled if((get_damage(TRUE) >= max_damage) || (HAS_TRAIT(owner, TRAIT_EASYLIMBDISABLE) && (get_damage(TRUE) >= (max_damage * 0.6)))) //Easy limb disable disables the limb at 40% health instead of 0% - return BODYPART_DISABLED_DAMAGE - if(disabled && (get_damage(TRUE) <= (max_damage * 0.5))) + if(!last_maxed) + owner.emote("scream") + last_maxed = TRUE + if(!is_organic_limb()) + return BODYPART_DISABLED_DAMAGE + else if(disabled && (get_damage(TRUE) <= (max_damage * 0.8))) // reenabled at 80% now instead of 50% as of wounds update + last_maxed = FALSE return BODYPART_NOT_DISABLED else return BODYPART_NOT_DISABLED @@ -251,9 +405,11 @@ /obj/item/bodypart/proc/set_disabled(new_disabled) - if(disabled == new_disabled) + if(disabled == new_disabled || !owner) return FALSE disabled = new_disabled + if(disabled && owner.get_item_for_held_index(held_index)) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) owner.update_health_hud() //update the healthdoll owner.update_body() owner.update_mobility() @@ -581,293 +737,41 @@ drop_organs() qdel(src) -/obj/item/bodypart/chest - name = BODY_ZONE_CHEST - desc = "It's impolite to stare at a person's chest." - icon_state = "default_human_chest" - max_damage = 200 - body_zone = BODY_ZONE_CHEST - body_part = CHEST - px_x = 0 - px_y = 0 - stam_damage_coeff = 1 - max_stamina_damage = 200 - var/obj/item/cavity_item - -/obj/item/bodypart/chest/can_dismember(obj/item/I) - if(!((owner.stat == DEAD) || owner.InFullCritical())) - return FALSE - return ..() - -/obj/item/bodypart/chest/Destroy() - if(cavity_item) - qdel(cavity_item) - return ..() - -/obj/item/bodypart/chest/drop_organs(mob/user) - if(cavity_item) - cavity_item.forceMove(user.loc) - cavity_item = null - ..() - -/obj/item/bodypart/chest/monkey - icon = 'icons/mob/animal_parts.dmi' - icon_state = "default_monkey_chest" - animal_origin = MONKEY_BODYPART - -/obj/item/bodypart/chest/alien - icon = 'icons/mob/animal_parts.dmi' - icon_state = "alien_chest" - dismemberable = 0 - max_damage = 500 - animal_origin = ALIEN_BODYPART - -/obj/item/bodypart/chest/devil - dismemberable = 0 - max_damage = 5000 - animal_origin = DEVIL_BODYPART - -/obj/item/bodypart/chest/larva - icon = 'icons/mob/animal_parts.dmi' - icon_state = "larva_chest" - dismemberable = 0 - max_damage = 50 - animal_origin = LARVA_BODYPART - -/obj/item/bodypart/l_arm - name = "left arm" - desc = "Did you know that the word 'sinister' stems originally from the \ - Latin 'sinestra' (left hand), because the left hand was supposed to \ - be possessed by the devil? This arm appears to be possessed by no \ - one though." - icon_state = "default_human_l_arm" - attack_verb = list("slapped", "punched") - max_damage = 50 - max_stamina_damage = 50 - body_zone = BODY_ZONE_L_ARM - body_part = ARM_LEFT - aux_icons = list(BODY_ZONE_PRECISE_L_HAND = HANDS_PART_LAYER, "l_hand_behind" = BODY_BEHIND_LAYER) - body_damage_coeff = 0.75 - held_index = 1 - px_x = -6 - px_y = 0 - stam_heal_tick = STAM_RECOVERY_LIMB - -/obj/item/bodypart/l_arm/is_disabled() - if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM)) - return BODYPART_DISABLED_PARALYSIS - return ..() - -/obj/item/bodypart/l_arm/set_disabled(new_disabled) - . = ..() - if(!.) +/// Get whatever wound of the given type is currently attached to this limb, if any +/obj/item/bodypart/proc/get_wound_type(checking_type) + if(isnull(wounds)) return - if(owner.stat < UNCONSCIOUS) - switch(disabled) - if(BODYPART_DISABLED_DAMAGE) - owner.emote("scream") - to_chat(owner, "Your [name] is too damaged to function!") - if(BODYPART_DISABLED_PARALYSIS) - to_chat(owner, "You can't feel your [name]!") - if(held_index) - owner.dropItemToGround(owner.get_item_for_held_index(held_index)) - if(owner.hud_used) - var/obj/screen/inventory/hand/L = owner.hud_used.hand_slots["[held_index]"] - if(L) - L.update_icon() + for(var/thing in wounds) + var/datum/wound/W = thing + if(istype(W, checking_type)) + return W -/obj/item/bodypart/l_arm/monkey - icon = 'icons/mob/animal_parts.dmi' - icon_state = "default_monkey_l_arm" - animal_origin = MONKEY_BODYPART - px_x = -5 - px_y = -3 +/// very rough start for updating efficiency and other stats on a body part whenever a wound is gained/lost +/obj/item/bodypart/proc/update_wounds() + var/dam_mul = 1 //initial(wound_damage_multiplier) + // we can only have one wound per type, but remember there's multiple types + for(var/datum/wound/W in wounds) + dam_mul *= W.damage_mulitplier_penalty + wound_damage_multiplier = dam_mul + update_disabled() -/obj/item/bodypart/l_arm/alien - icon = 'icons/mob/animal_parts.dmi' - icon_state = "alien_l_arm" - px_x = 0 - px_y = 0 - dismemberable = 0 - max_damage = 100 - animal_origin = ALIEN_BODYPART - -/obj/item/bodypart/l_arm/devil - dismemberable = 0 - max_damage = 5000 - animal_origin = DEVIL_BODYPART - -/obj/item/bodypart/r_arm - name = "right arm" - desc = "Over 87% of humans are right handed. That figure is much lower \ - among humans missing their right arm." - icon_state = "default_human_r_arm" - attack_verb = list("slapped", "punched") - max_damage = 50 - body_zone = BODY_ZONE_R_ARM - body_part = ARM_RIGHT - aux_icons = list(BODY_ZONE_PRECISE_R_HAND = HANDS_PART_LAYER, "r_hand_behind" = BODY_BEHIND_LAYER) - body_damage_coeff = 0.75 - held_index = 2 - px_x = 6 - px_y = 0 - stam_heal_tick = STAM_RECOVERY_LIMB - max_stamina_damage = 50 - -/obj/item/bodypart/r_arm/is_disabled() - if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM)) - return BODYPART_DISABLED_PARALYSIS - return ..() - -/obj/item/bodypart/r_arm/set_disabled(new_disabled) - . = ..() - if(!.) +/obj/item/bodypart/proc/get_bleed_rate() + if(status != BODYPART_ORGANIC) // maybe in the future we can bleed oil from aug parts, but not now return - if(owner.stat < UNCONSCIOUS) - switch(disabled) - if(BODYPART_DISABLED_DAMAGE) - owner.emote("scream") - to_chat(owner, "Your [name] is too damaged to function!") - if(BODYPART_DISABLED_PARALYSIS) - to_chat(owner, "You can't feel your [name]!") - if(held_index) - owner.dropItemToGround(owner.get_item_for_held_index(held_index)) - if(owner.hud_used) - var/obj/screen/inventory/hand/R = owner.hud_used.hand_slots["[held_index]"] - if(R) - R.update_icon() + var/bleed_rate = 0 + if(generic_bleedstacks > 0) + bleed_rate++ + if(brute_dam >= 40) + bleed_rate += (brute_dam * 0.008) + //We want an accurate reading of .len + listclearnulls(embedded_objects) + for(var/obj/item/embeddies in embedded_objects) + if(!embeddies.isEmbedHarmless()) + bleed_rate += 0.5 -/obj/item/bodypart/r_arm/monkey - icon = 'icons/mob/animal_parts.dmi' - icon_state = "default_monkey_r_arm" - animal_origin = MONKEY_BODYPART - px_x = 5 - px_y = -3 + for(var/thing in wounds) + var/datum/wound/W = thing + bleed_rate += W.blood_flow -/obj/item/bodypart/r_arm/alien - icon = 'icons/mob/animal_parts.dmi' - icon_state = "alien_r_arm" - px_x = 0 - px_y = 0 - dismemberable = 0 - max_damage = 100 - animal_origin = ALIEN_BODYPART - -/obj/item/bodypart/r_arm/devil - dismemberable = 0 - max_damage = 5000 - animal_origin = DEVIL_BODYPART - -/obj/item/bodypart/l_leg - name = "left leg" - desc = "Some athletes prefer to tie their left shoelaces first for good \ - luck. In this instance, it probably would not have helped." - icon_state = "default_human_l_leg" - attack_verb = list("kicked", "stomped") - max_damage = 50 - body_zone = BODY_ZONE_L_LEG - body_part = LEG_LEFT - body_damage_coeff = 0.75 - px_x = -2 - px_y = 12 - stam_heal_tick = STAM_RECOVERY_LIMB - max_stamina_damage = 50 - -/obj/item/bodypart/l_leg/is_disabled() - if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG)) - return BODYPART_DISABLED_PARALYSIS - return ..() - -/obj/item/bodypart/l_leg/set_disabled(new_disabled) - . = ..() - if(!. || owner.stat >= UNCONSCIOUS) - return - switch(disabled) - if(BODYPART_DISABLED_DAMAGE) - owner.emote("scream") - to_chat(owner, "Your [name] is too damaged to function!") - if(BODYPART_DISABLED_PARALYSIS) - to_chat(owner, "You can't feel your [name]!") - - -/obj/item/bodypart/l_leg/digitigrade - name = "left digitigrade leg" - use_digitigrade = FULL_DIGITIGRADE - -/obj/item/bodypart/l_leg/monkey - icon = 'icons/mob/animal_parts.dmi' - icon_state = "default_monkey_l_leg" - animal_origin = MONKEY_BODYPART - px_y = 4 - -/obj/item/bodypart/l_leg/alien - icon = 'icons/mob/animal_parts.dmi' - icon_state = "alien_l_leg" - px_x = 0 - px_y = 0 - dismemberable = 0 - max_damage = 100 - animal_origin = ALIEN_BODYPART - -/obj/item/bodypart/l_leg/devil - dismemberable = 0 - max_damage = 5000 - animal_origin = DEVIL_BODYPART - -/obj/item/bodypart/r_leg - name = "right leg" - desc = "You put your right leg in, your right leg out. In, out, in, out, \ - shake it all about. And apparently then it detaches.\n\ - The hokey pokey has certainly changed a lot since space colonisation." - // alternative spellings of 'pokey' are availible - icon_state = "default_human_r_leg" - attack_verb = list("kicked", "stomped") - max_damage = 50 - body_zone = BODY_ZONE_R_LEG - body_part = LEG_RIGHT - body_damage_coeff = 0.75 - px_x = 2 - px_y = 12 - max_stamina_damage = 50 - stam_heal_tick = STAM_RECOVERY_LIMB - -/obj/item/bodypart/r_leg/is_disabled() - if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG)) - return BODYPART_DISABLED_PARALYSIS - return ..() - -/obj/item/bodypart/r_leg/set_disabled(new_disabled) - . = ..() - if(!. || owner.stat >= UNCONSCIOUS) - return - switch(disabled) - if(BODYPART_DISABLED_DAMAGE) - owner.emote("scream") - to_chat(owner, "Your [name] is too damaged to function!") - if(BODYPART_DISABLED_PARALYSIS) - to_chat(owner, "You can't feel your [name]!") - -/obj/item/bodypart/r_leg/digitigrade - name = "right digitigrade leg" - use_digitigrade = FULL_DIGITIGRADE - -/obj/item/bodypart/r_leg/monkey - icon = 'icons/mob/animal_parts.dmi' - icon_state = "default_monkey_r_leg" - animal_origin = MONKEY_BODYPART - px_y = 4 - -/obj/item/bodypart/r_leg/alien - icon = 'icons/mob/animal_parts.dmi' - icon_state = "alien_r_leg" - px_x = 0 - px_y = 0 - dismemberable = 0 - max_damage = 100 - animal_origin = ALIEN_BODYPART - -/obj/item/bodypart/r_leg/devil - dismemberable = 0 - max_damage = 5000 - animal_origin = DEVIL_BODYPART + return bleed_rate diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index f654270df7..2ce6ca8862 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -15,7 +15,7 @@ if(HAS_TRAIT(C, TRAIT_NODISMEMBER)) return FALSE var/obj/item/bodypart/affecting = C.get_bodypart(BODY_ZONE_CHEST) - affecting.receive_damage(clamp(brute_dam/2 * affecting.body_damage_coeff, 15, 50), clamp(burn_dam/2 * affecting.body_damage_coeff, 0, 50)) //Damage the chest based on limb's existing damage + affecting.receive_damage(clamp(brute_dam/2 * affecting.body_damage_coeff, 15, 50), clamp(burn_dam/2 * affecting.body_damage_coeff, 0, 50), wound_bonus=CANT_WOUND) //Damage the chest based on limb's existing damage C.visible_message("[C]'s [src.name] has been violently dismembered!") C.emote("scream") SEND_SIGNAL(C, COMSIG_ADD_MOOD_EVENT, "dismembered", /datum/mood_event/dismembered) @@ -83,11 +83,12 @@ //limb removal. The "special" argument is used for swapping a limb with a new one without the effects of losing a limb kicking in. -/obj/item/bodypart/proc/drop_limb(special) +/obj/item/bodypart/proc/drop_limb(special, dismembered) if(!owner) return var/atom/Tsec = owner.drop_location() var/mob/living/carbon/C = owner + SEND_SIGNAL(C, COMSIG_CARBON_REMOVE_LIMB, src, dismembered) update_limb(1) C.bodyparts -= src @@ -95,6 +96,15 @@ C.dropItemToGround(owner.get_item_for_held_index(held_index), 1) C.hand_bodyparts[held_index] = null + for(var/thing in scars) + var/datum/scar/S = thing + S.victim = null + LAZYREMOVE(owner.all_scars, S) + + for(var/thing in wounds) + var/datum/wound/W = thing + W.remove_wound(TRUE) + owner = null for(var/X in C.surgeries) //if we had an ongoing surgery on that limb, we stop it. @@ -298,6 +308,15 @@ for(var/obj/item/organ/O in contents) O.Insert(C) + for(var/thing in scars) + var/datum/scar/S = thing + S.victim = C + LAZYADD(C.all_scars, thing) + + for(var/i in wounds) + var/datum/wound/W = i + W.apply_wound(src, TRUE) + update_bodypart_damage_state() update_disabled() diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index a74b1dad28..a67f16b934 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -35,6 +35,10 @@ //If the head is a special sprite var/custom_head + wound_resistance = 10 + specific_locations = list("left eyebrow", "cheekbone", "neck", "throat", "jawline", "entire face") + scars_covered_by_clothes = FALSE + /obj/item/bodypart/head/can_dismember(obj/item/I) if(!((owner.stat == DEAD) || owner.InFullCritical())) return FALSE diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 29aca7166f..3161419449 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -10,6 +10,16 @@ if(L.body_zone == zone) return L +///Get the bodypart for whatever hand we have active, Only relevant for carbons +/mob/proc/get_active_hand() + return FALSE + +/mob/living/carbon/get_active_hand() + var/which_hand = BODY_ZONE_PRECISE_L_HAND + if(!(active_hand_index % 2)) + which_hand = BODY_ZONE_PRECISE_R_HAND + return get_bodypart(check_zone(which_hand)) + /mob/living/carbon/has_hand_for_held_index(i) if(i) var/obj/item/bodypart/L = hand_bodyparts[i] diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm new file mode 100644 index 0000000000..bd4abaa765 --- /dev/null +++ b/code/modules/surgery/bodyparts/parts.dm @@ -0,0 +1,307 @@ + +/obj/item/bodypart/chest + name = BODY_ZONE_CHEST + desc = "It's impolite to stare at a person's chest." + icon_state = "default_human_chest" + max_damage = 200 + body_zone = BODY_ZONE_CHEST + body_part = CHEST + px_x = 0 + px_y = 0 + stam_damage_coeff = 1 + max_stamina_damage = 120 + var/obj/item/cavity_item + specific_locations = list("upper chest", "lower abdomen", "midsection", "collarbone", "lower back") + wound_resistance = 10 + +/obj/item/bodypart/chest/can_dismember(obj/item/I) + if(!((owner.stat == DEAD) || owner.InFullCritical())) + return FALSE + return ..() + +/obj/item/bodypart/chest/Destroy() + QDEL_NULL(cavity_item) + return ..() + +/obj/item/bodypart/chest/drop_organs(mob/user, violent_removal) + if(cavity_item) + cavity_item.forceMove(drop_location()) + cavity_item = null + ..() + +/obj/item/bodypart/chest/monkey + icon = 'icons/mob/animal_parts.dmi' + icon_state = "default_monkey_chest" + animal_origin = MONKEY_BODYPART + wound_resistance = -10 + +/obj/item/bodypart/chest/alien + icon = 'icons/mob/animal_parts.dmi' + icon_state = "alien_chest" + dismemberable = 0 + max_damage = 500 + animal_origin = ALIEN_BODYPART + +/obj/item/bodypart/chest/devil + dismemberable = 0 + max_damage = 5000 + animal_origin = DEVIL_BODYPART + +/obj/item/bodypart/chest/larva + icon = 'icons/mob/animal_parts.dmi' + icon_state = "larva_chest" + dismemberable = 0 + max_damage = 50 + animal_origin = LARVA_BODYPART + +/obj/item/bodypart/l_arm + name = "left arm" + desc = "Did you know that the word 'sinister' stems originally from the \ + Latin 'sinestra' (left hand), because the left hand was supposed to \ + be possessed by the devil? This arm appears to be possessed by no \ + one though." + icon_state = "default_human_l_arm" + attack_verb = list("slapped", "punched") + max_damage = 50 + max_stamina_damage = 50 + body_zone = BODY_ZONE_L_ARM + body_part = ARM_LEFT + aux_zone = BODY_ZONE_PRECISE_L_HAND + aux_layer = HANDS_PART_LAYER + body_damage_coeff = 0.75 + held_index = 1 + px_x = -6 + px_y = 0 + specific_locations = list("outer left forearm", "inner left wrist", "left elbow", "left bicep", "left shoulder") + +/obj/item/bodypart/l_arm/is_disabled() + if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_ARM)) + return BODYPART_DISABLED_PARALYSIS + return ..() + +/obj/item/bodypart/l_arm/set_disabled(new_disabled) + . = ..() + if(!.) + return + if(disabled == BODYPART_DISABLED_DAMAGE) + if(owner.stat < UNCONSCIOUS) + owner.emote("scream") + if(owner.stat < DEAD) + to_chat(owner, "Your [name] is too damaged to function!") + if(held_index) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) + else if(disabled == BODYPART_DISABLED_PARALYSIS) + if(owner.stat < DEAD) + to_chat(owner, "You can't feel your [name]!") + if(held_index) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) + if(owner.hud_used) + var/obj/screen/inventory/hand/L = owner.hud_used.hand_slots["[held_index]"] + if(L) + L.update_icon() + +/obj/item/bodypart/l_arm/monkey + icon = 'icons/mob/animal_parts.dmi' + icon_state = "default_monkey_l_arm" + animal_origin = MONKEY_BODYPART + wound_resistance = -10 + px_x = -5 + px_y = -3 + +/obj/item/bodypart/l_arm/alien + icon = 'icons/mob/animal_parts.dmi' + icon_state = "alien_l_arm" + px_x = 0 + px_y = 0 + dismemberable = 0 + max_damage = 100 + animal_origin = ALIEN_BODYPART + +/obj/item/bodypart/l_arm/devil + dismemberable = 0 + max_damage = 5000 + animal_origin = DEVIL_BODYPART + +/obj/item/bodypart/r_arm + name = "right arm" + desc = "Over 87% of humans are right handed. That figure is much lower \ + among humans missing their right arm." + icon_state = "default_human_r_arm" + attack_verb = list("slapped", "punched") + max_damage = 50 + body_zone = BODY_ZONE_R_ARM + body_part = ARM_RIGHT + aux_zone = BODY_ZONE_PRECISE_R_HAND + aux_layer = HANDS_PART_LAYER + body_damage_coeff = 0.75 + held_index = 2 + px_x = 6 + px_y = 0 + max_stamina_damage = 50 + specific_locations = list("outer right forearm", "inner right wrist", "right elbow", "right bicep", "right shoulder") + +/obj/item/bodypart/r_arm/is_disabled() + if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_ARM)) + return BODYPART_DISABLED_PARALYSIS + return ..() + +/obj/item/bodypart/r_arm/set_disabled(new_disabled) + . = ..() + if(!.) + return + if(disabled == BODYPART_DISABLED_DAMAGE) + if(owner.stat < UNCONSCIOUS) + owner.emote("scream") + if(owner.stat < DEAD) + to_chat(owner, "Your [name] is too damaged to function!") + if(held_index) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) + else if(disabled == BODYPART_DISABLED_PARALYSIS) + if(owner.stat < DEAD) + to_chat(owner, "You can't feel your [name]!") + if(held_index) + owner.dropItemToGround(owner.get_item_for_held_index(held_index)) + if(owner.hud_used) + var/obj/screen/inventory/hand/R = owner.hud_used.hand_slots["[held_index]"] + if(R) + R.update_icon() + +/obj/item/bodypart/r_arm/monkey + icon = 'icons/mob/animal_parts.dmi' + icon_state = "default_monkey_r_arm" + animal_origin = MONKEY_BODYPART + wound_resistance = -10 + px_x = 5 + px_y = -3 + +/obj/item/bodypart/r_arm/alien + icon = 'icons/mob/animal_parts.dmi' + icon_state = "alien_r_arm" + px_x = 0 + px_y = 0 + dismemberable = 0 + max_damage = 100 + animal_origin = ALIEN_BODYPART + +/obj/item/bodypart/r_arm/devil + dismemberable = 0 + max_damage = 5000 + animal_origin = DEVIL_BODYPART + +/obj/item/bodypart/l_leg + name = "left leg" + desc = "Some athletes prefer to tie their left shoelaces first for good \ + luck. In this instance, it probably would not have helped." + icon_state = "default_human_l_leg" + attack_verb = list("kicked", "stomped") + max_damage = 50 + body_zone = BODY_ZONE_L_LEG + body_part = LEG_LEFT + body_damage_coeff = 0.75 + px_x = -2 + px_y = 12 + max_stamina_damage = 50 + specific_locations = list("inner left thigh", "outer left calf", "outer left hip", " left kneecap", "lower left shin") + +/obj/item/bodypart/l_leg/is_disabled() + if(HAS_TRAIT(owner, TRAIT_PARALYSIS_L_LEG)) + return BODYPART_DISABLED_PARALYSIS + return ..() + +/obj/item/bodypart/l_leg/set_disabled(new_disabled) + . = ..() + if(!.) + return + if(disabled == BODYPART_DISABLED_DAMAGE) + if(owner.stat < UNCONSCIOUS) + owner.emote("scream") + if(owner.stat < DEAD) + to_chat(owner, "Your [name] is too damaged to function!") + else if(disabled == BODYPART_DISABLED_PARALYSIS) + if(owner.stat < DEAD) + to_chat(owner, "You can't feel your [name]!") + +/obj/item/bodypart/l_leg/digitigrade + name = "left digitigrade leg" + use_digitigrade = FULL_DIGITIGRADE + +/obj/item/bodypart/l_leg/monkey + icon = 'icons/mob/animal_parts.dmi' + icon_state = "default_monkey_l_leg" + animal_origin = MONKEY_BODYPART + wound_resistance = -10 + px_y = 4 + +/obj/item/bodypart/l_leg/alien + icon = 'icons/mob/animal_parts.dmi' + icon_state = "alien_l_leg" + px_x = 0 + px_y = 0 + dismemberable = 0 + max_damage = 100 + animal_origin = ALIEN_BODYPART + +/obj/item/bodypart/l_leg/devil + dismemberable = 0 + max_damage = 5000 + animal_origin = DEVIL_BODYPART + +/obj/item/bodypart/r_leg + name = "right leg" + desc = "You put your right leg in, your right leg out. In, out, in, out, \ + shake it all about. And apparently then it detaches.\n\ + The hokey pokey has certainly changed a lot since space colonisation." + // alternative spellings of 'pokey' are availible + icon_state = "default_human_r_leg" + attack_verb = list("kicked", "stomped") + max_damage = 50 + body_zone = BODY_ZONE_R_LEG + body_part = LEG_RIGHT + body_damage_coeff = 0.75 + px_x = 2 + px_y = 12 + max_stamina_damage = 50 + specific_locations = list("inner right thigh", "outer right calf", "outer right hip", "right kneecap", "lower right shin") + +/obj/item/bodypart/r_leg/is_disabled() + if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG)) + return BODYPART_DISABLED_PARALYSIS + return ..() + +/obj/item/bodypart/r_leg/set_disabled(new_disabled) + . = ..() + if(!.) + return + if(disabled == BODYPART_DISABLED_DAMAGE) + if(owner.stat < UNCONSCIOUS) + owner.emote("scream") + if(owner.stat < DEAD) + to_chat(owner, "Your [name] is too damaged to function!") + else if(disabled == BODYPART_DISABLED_PARALYSIS) + if(owner.stat < DEAD) + to_chat(owner, "You can't feel your [name]!") + +/obj/item/bodypart/r_leg/digitigrade + name = "right digitigrade leg" + use_digitigrade = FULL_DIGITIGRADE + +/obj/item/bodypart/r_leg/monkey + icon = 'icons/mob/animal_parts.dmi' + icon_state = "default_monkey_r_leg" + animal_origin = MONKEY_BODYPART + wound_resistance = -10 + px_y = 4 + +/obj/item/bodypart/r_leg/alien + icon = 'icons/mob/animal_parts.dmi' + icon_state = "alien_r_leg" + px_x = 0 + px_y = 0 + dismemberable = 0 + max_damage = 100 + animal_origin = ALIEN_BODYPART + +/obj/item/bodypart/r_leg/devil + dismemberable = 0 + max_damage = 5000 + animal_origin = DEVIL_BODYPART diff --git a/code/modules/surgery/bone_mending.dm b/code/modules/surgery/bone_mending.dm new file mode 100644 index 0000000000..6afe494105 --- /dev/null +++ b/code/modules/surgery/bone_mending.dm @@ -0,0 +1,142 @@ + +/////BONE FIXING SURGERIES////// + +///// Repair Hairline Fracture (Severe) +/datum/surgery/repair_bone_hairline + name = "Repair bone fracture (hairline)" + steps = list(/datum/surgery_step/incise, /datum/surgery_step/repair_bone_hairline, /datum/surgery_step/close) + target_mobtypes = list(/mob/living/carbon/human) + possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) + requires_real_bodypart = TRUE + targetable_wound = /datum/wound/brute/bone/severe + +/datum/surgery/repair_bone_hairline/can_start(mob/living/user, mob/living/carbon/target) + if(..()) + var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) + return(targeted_bodypart.get_wound_type(targetable_wound)) + + +///// Repair Compound Fracture (Critical) +/datum/surgery/repair_bone_compound + name = "Repair Compound Fracture" + steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, /datum/surgery_step/reset_compound_fracture, /datum/surgery_step/repair_bone_compound, /datum/surgery_step/close) + target_mobtypes = list(/mob/living/carbon/human) + possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) + requires_real_bodypart = TRUE + targetable_wound = /datum/wound/brute/bone/critical + +/datum/surgery/repair_bone_compound/can_start(mob/living/user, mob/living/carbon/target) + if(..()) + var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) + return(targeted_bodypart.get_wound_type(targetable_wound)) + + + +//SURGERY STEPS + +///// Repair Hairline Fracture (Severe) +/datum/surgery_step/repair_bone_hairline + name = "repair hairline fracture (bonesetter/bone gel/tape)" + implements = list(/obj/item/bonesetter = 100, /obj/item/stack/medical/bone_gel = 100, /obj/item/stack/sticky_tape/surgical = 100, /obj/item/stack/sticky_tape/super = 50, /obj/item/stack/sticky_tape = 30) + time = 40 + experience_given = MEDICAL_SKILL_MEDIUM + +/datum/surgery_step/repair_bone_hairline/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(surgery.operated_wound) + display_results(user, target, "You begin to repair the fracture in [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)].") + else + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + +/datum/surgery_step/repair_bone_hairline/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + if(surgery.operated_wound) + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) + display_results(user, target, "You successfully repair the fracture in [target]'s [parse_zone(target_zone)].", + "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "repaired a hairline fracture in", addition="INTENT: [uppertext(user.a_intent)]") + qdel(surgery.operated_wound) + else + to_chat(user, "[target] has no hairline fracture there!") + return ..() + +/datum/surgery_step/repair_bone_hairline/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0) + ..() + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) + + + +///// Reset Compound Fracture (Crticial) +/datum/surgery_step/reset_compound_fracture + name = "reset bone" + implements = list(/obj/item/bonesetter = 100, /obj/item/stack/sticky_tape/surgical = 60, /obj/item/stack/sticky_tape/super = 40, /obj/item/stack/sticky_tape = 20) + time = 40 + experience_given = MEDICAL_SKILL_MEDIUM + +/datum/surgery_step/reset_compound_fracture/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(surgery.operated_wound) + display_results(user, target, "You begin to reset the bone in [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to reset the bone in [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to reset the bone in [target]'s [parse_zone(user.zone_selected)].") + else + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + +/datum/surgery_step/reset_compound_fracture/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + if(surgery.operated_wound) + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) + display_results(user, target, "You successfully reset the bone in [target]'s [parse_zone(target_zone)].", + "[user] successfully resets the bone in [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully resets the bone in [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "reset a compound fracture in", addition="INTENT: [uppertext(user.a_intent)]") + else + to_chat(user, "[target] has no compound fracture there!") + return ..() + +/datum/surgery_step/reset_compound_fracture/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0) + ..() + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) + + +///// Repair Compound Fracture (Crticial) +/datum/surgery_step/repair_bone_compound + name = "repair compound fracture (bone gel/tape)" + implements = list(/obj/item/stack/medical/bone_gel = 100, /obj/item/stack/sticky_tape/surgical = 100, /obj/item/stack/sticky_tape/super = 50, /obj/item/stack/sticky_tape = 30) + time = 40 + experience_given = MEDICAL_SKILL_MEDIUM + +/datum/surgery_step/repair_bone_compound/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(surgery.operated_wound) + display_results(user, target, "You begin to repair the fracture in [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to repair the fracture in [target]'s [parse_zone(user.zone_selected)].") + else + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + +/datum/surgery_step/repair_bone_compound/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + if(surgery.operated_wound) + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) + display_results(user, target, "You successfully repair the fracture in [target]'s [parse_zone(target_zone)].", + "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully repairs the fracture in [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "repaired a compound fracture in", addition="INTENT: [uppertext(user.a_intent)]") + qdel(surgery.operated_wound) + else + to_chat(user, "[target] has no compound fracture there!") + return ..() + +/datum/surgery_step/repair_bone_compound/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0) + ..() + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) diff --git a/code/modules/surgery/burn_dressing.dm b/code/modules/surgery/burn_dressing.dm new file mode 100644 index 0000000000..1d64d29ff6 --- /dev/null +++ b/code/modules/surgery/burn_dressing.dm @@ -0,0 +1,108 @@ + +/////BURN FIXING SURGERIES////// + +///// Debride burnt flesh +/datum/surgery/debride + name = "Debride infected flesh" + steps = list(/datum/surgery_step/debride, /datum/surgery_step/dress) + target_mobtypes = list(/mob/living/carbon/human) + possible_locs = list(BODY_ZONE_R_ARM,BODY_ZONE_L_ARM,BODY_ZONE_R_LEG,BODY_ZONE_L_LEG,BODY_ZONE_CHEST,BODY_ZONE_HEAD) + requires_real_bodypart = TRUE + targetable_wound = /datum/wound/burn + +/datum/surgery/debride/can_start(mob/living/user, mob/living/carbon/target) + if(..()) + var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) + var/datum/wound/burn/burn_wound = targeted_bodypart.get_wound_type(targetable_wound) + return(burn_wound && burn_wound.infestation > 0) + +//SURGERY STEPS + +///// Debride +/datum/surgery_step/debride + name = "excise infection" + implements = list(TOOL_HEMOSTAT = 100, TOOL_SCALPEL = 85, TOOL_SAW = 60, TOOL_WIRECUTTER = 40) + time = 30 + repeatable = TRUE + experience_given = MEDICAL_SKILL_MEDIUM + +/datum/surgery_step/debride/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + if(surgery.operated_wound) + var/datum/wound/burn/burn_wound = surgery.operated_wound + if(burn_wound.infestation <= 0) + to_chat(user, "[target]'s [parse_zone(user.zone_selected)] has no infected flesh to remove!") + surgery.status++ + repeatable = FALSE + return + display_results(user, target, "You begin to excise infected flesh from [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to excise infected flesh from [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to excise infected flesh from [target]'s [parse_zone(user.zone_selected)].") + else + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + +/datum/surgery_step/debride/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + var/datum/wound/burn/burn_wound = surgery.operated_wound + if(burn_wound) + display_results(user, target, "You successfully excise some of the infected flesh from [target]'s [parse_zone(target_zone)].", + "[user] successfully excises some of the infected flesh from [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully excises some of the infected flesh from [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "excised infected flesh in", addition="INTENT: [uppertext(user.a_intent)]") + surgery.operated_bodypart.receive_damage(brute=3, wound_bonus=CANT_WOUND) + burn_wound.infestation -= 0.5 + burn_wound.sanitization += 0.5 + if(burn_wound.infestation <= 0) + repeatable = FALSE + else + to_chat(user, "[target] has no infected flesh there!") + return ..() + +/datum/surgery_step/debride/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0) + ..() + display_results(user, target, "You carve away some of the healthy flesh from [target]'s [parse_zone(target_zone)].", + "[user] carves away some of the healthy flesh from [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] carves away some of the healthy flesh from [target]'s [parse_zone(target_zone)]!") + surgery.operated_bodypart.receive_damage(brute=rand(4,8), sharpness=TRUE) + +/datum/surgery_step/debride/initiate(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, try_to_fail = FALSE) + if(!..()) + return + var/datum/wound/burn/burn_wound = surgery.operated_wound + while(burn_wound && burn_wound.infestation > 0.25) + if(!..()) + break + +///// Dressing burns +/datum/surgery_step/dress + name = "bandage burns" + implements = list(/obj/item/stack/medical/gauze = 100, /obj/item/stack/sticky_tape/surgical = 100) + time = 40 + experience_given = MEDICAL_SKILL_MEDIUM + +/datum/surgery_step/dress/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/datum/wound/burn/burn_wound = surgery.operated_wound + if(burn_wound) + display_results(user, target, "You begin to dress the burns on [target]'s [parse_zone(user.zone_selected)]...", + "[user] begins to dress the burns on [target]'s [parse_zone(user.zone_selected)] with [tool].", + "[user] begins to dress the burns on [target]'s [parse_zone(user.zone_selected)].") + else + user.visible_message("[user] looks for [target]'s [parse_zone(user.zone_selected)].", "You look for [target]'s [parse_zone(user.zone_selected)]...") + +/datum/surgery_step/dress/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results = FALSE) + var/datum/wound/burn/burn_wound = surgery.operated_wound + if(burn_wound) + display_results(user, target, "You successfully wrap [target]'s [parse_zone(target_zone)] with [tool].", + "[user] successfully wraps [target]'s [parse_zone(target_zone)] with [tool]!", + "[user] successfully wraps [target]'s [parse_zone(target_zone)]!") + log_combat(user, target, "dressed burns in", addition="INTENT: [uppertext(user.a_intent)]") + burn_wound.sanitization += 3 + burn_wound.flesh_healing += 5 + burn_wound.force_bandage(tool) + else + to_chat(user, "[target] has no burns there!") + return ..() + +/datum/surgery_step/dress/failure(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, var/fail_prob = 0) + ..() + if(istype(tool, /obj/item/stack)) + var/obj/item/stack/used_stack = tool + used_stack.use(1) diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm index af736e393c..d1d478ddfe 100644 --- a/code/modules/surgery/coronary_bypass.dm +++ b/code/modules/surgery/coronary_bypass.dm @@ -31,7 +31,8 @@ display_results(user, target, "Blood pools around the incision in [H]'s heart.", "Blood pools around the incision in [H]'s heart.", "") - H.bleed_rate += 10 + var/obj/item/bodypart/BP = H.get_bodypart(target_zone) + BP.generic_bleedstacks += 10 H.adjustBruteLoss(10) return TRUE @@ -41,7 +42,8 @@ display_results(user, target, "You screw up, cutting too deeply into the heart!", "[user] screws up, causing blood to spurt out of [H]'s chest!", "[user] screws up, causing blood to spurt out of [H]'s chest!") - H.bleed_rate += 20 + var/obj/item/bodypart/BP = H.get_bodypart(target_zone) + BP.generic_bleedstacks += 10 H.adjustOrganLoss(ORGAN_SLOT_HEART, 10) H.adjustBruteLoss(10) @@ -73,5 +75,6 @@ "[user] screws up, causing blood to spurt out of [H]'s chest profusely!", "[user] screws up, causing blood to spurt out of [H]'s chest profusely!") H.adjustOrganLoss(ORGAN_SLOT_HEART, 20) - H.bleed_rate += 30 + var/obj/item/bodypart/BP = H.get_bodypart(target_zone) + BP.generic_bleedstacks += 30 return FALSE diff --git a/code/modules/surgery/experimental_dissection.dm b/code/modules/surgery/experimental_dissection.dm index 6110bb6202..b9d877d0c1 100644 --- a/code/modules/surgery/experimental_dissection.dm +++ b/code/modules/surgery/experimental_dissection.dm @@ -78,7 +78,7 @@ "[user] dissects [target]!") SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points_earned)) var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST) - target.apply_damage(80, BRUTE, L) + target.apply_damage(80, BRUTE, L, wound_bonus=CANT_WOUND) ADD_TRAIT(target, TRAIT_DISSECTED, "[surgery.name]") repeatable = FALSE return TRUE @@ -89,7 +89,7 @@ "[user] dissects [target], but looks a little dissapointed.") SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = (round(check_value(target, surgery) * 0.01)))) var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST) - target.apply_damage(80, BRUTE, L) + target.apply_damage(80, BRUTE, L, wound_bonus=CANT_WOUND) return TRUE /datum/surgery/advanced/experimental_dissection/adv diff --git a/code/modules/surgery/healing.dm b/code/modules/surgery/healing.dm index 4069199864..0680dda404 100644 --- a/code/modules/surgery/healing.dm +++ b/code/modules/surgery/healing.dm @@ -87,7 +87,7 @@ urdamageamt_brute += round((target.getBruteLoss()/ (missinghpbonus*2)),0.1) urdamageamt_burn += round((target.getFireLoss()/ (missinghpbonus*2)),0.1) - target.take_bodypart_damage(urdamageamt_brute, urdamageamt_burn) + target.take_bodypart_damage(urdamageamt_brute, urdamageamt_burn, wound_bonus=CANT_WOUND) return FALSE /***************************BRUTE***************************/ diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm index 0b38ecc2fe..8e893875a9 100644 --- a/code/modules/surgery/organic_steps.dm +++ b/code/modules/surgery/organic_steps.dm @@ -21,7 +21,9 @@ display_results(user, target, "Blood pools around the incision in [H]'s [parse_zone(target_zone)].", "Blood pools around the incision in [H]'s [parse_zone(target_zone)].", "") - H.bleed_rate += 3 + var/obj/item/bodypart/BP = target.get_bodypart(target_zone) + if(BP) + BP.generic_bleedstacks += 10 return TRUE /datum/surgery_step/incise/nobleed //silly friendly! @@ -50,7 +52,9 @@ target.heal_bodypart_damage(20,0) if (ishuman(target)) var/mob/living/carbon/human/H = target - H.bleed_rate = max( (H.bleed_rate - 3), 0) + var/obj/item/bodypart/BP = H.get_bodypart(target_zone) + if(BP) + BP.generic_bleedstacks -= 3 return ..() //retract skin /datum/surgery_step/retract_skin @@ -86,7 +90,9 @@ target.heal_bodypart_damage(45,0) if (ishuman(target)) var/mob/living/carbon/human/H = target - H.bleed_rate = max( (H.bleed_rate - 3), 0) + var/obj/item/bodypart/BP = H.get_bodypart(target_zone) + if(BP) + BP.generic_bleedstacks -= 3 return ..() //saw bone /datum/surgery_step/saw @@ -100,7 +106,7 @@ "[user] begins to saw through the bone in [target]'s [parse_zone(target_zone)].") /datum/surgery_step/saw/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) - target.apply_damage(50, BRUTE, "[target_zone]") + target.apply_damage(50, BRUTE, "[target_zone]", wound_bonus=CANT_WOUND) display_results(user, target, "You saw [target]'s [parse_zone(target_zone)] open.", "[user] saws [target]'s [parse_zone(target_zone)] open!", "[user] saws [target]'s [parse_zone(target_zone)] open!") diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index fa49e3eff5..b6b74efe32 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -318,13 +318,14 @@ cooldown = COOLDOWN_DAMAGE for(var/V in listeners) var/mob/living/L = V - L.apply_damage(15 * power_multiplier, def_zone = BODY_ZONE_CHEST) + L.apply_damage(15 * power_multiplier, def_zone = BODY_ZONE_CHEST, wound_bonus=CANT_WOUND) //BLEED else if((findtext(message, bleed_words))) cooldown = COOLDOWN_DAMAGE for(var/mob/living/carbon/human/H in listeners) - H.bleed_rate += (5 * power_multiplier) + var/obj/item/bodypart/BP = pick(H.bodyparts) + BP.generic_bleedstacks += 5 //FIRE else if((findtext(message, burn_words))) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 51ce2726b0..4d42cb6c23 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -18,6 +18,8 @@ var/lying_required = TRUE //Does the vicitm needs to be lying down. var/requires_tech = FALSE var/replaced_by + var/datum/wound/operated_wound //The actual wound datum instance we're targeting + var/datum/wound/targetable_wound //The wound type this surgery targets /datum/surgery/New(surgery_target, surgery_location, surgery_bodypart) ..() @@ -28,8 +30,13 @@ location = surgery_location if(surgery_bodypart) operated_bodypart = surgery_bodypart + if(targetable_wound) + operated_wound = operated_bodypart.get_wound_type(targetable_wound) + operated_wound.attached_surgery = src /datum/surgery/Destroy() + if(operated_wound) + operated_wound.attached_surgery = null if(target) target.surgeries -= src target = null diff --git a/tgstation.dme b/tgstation.dme index 9c1f8a2378..dd57ad97a2 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -654,6 +654,7 @@ #include "code\datums\wires\tesla_coil.dm" #include "code\datums\wires\vending.dm" #include "code\datums\wounds\_scars.dm" +#include "code\datums\wounds\_wounds.dm" #include "code\datums\wounds\cuts.dm" #include "code\game\alternate_appearance.dm" #include "code\game\atoms.dm" @@ -3256,7 +3257,9 @@ #include "code\modules\station_goals\shield.dm" #include "code\modules\station_goals\station_goal.dm" #include "code\modules\surgery\amputation.dm" +#include "code\modules\surgery\bone_mending.dm" #include "code\modules\surgery\brain_surgery.dm" +#include "code\modules\surgery\burn_dressing.dm" #include "code\modules\surgery\cavity_implant.dm" #include "code\modules\surgery\core_removal.dm" #include "code\modules\surgery\coronary_bypass.dm" @@ -3297,10 +3300,11 @@ #include "code\modules\surgery\advanced\bioware\nerve_grounding.dm" #include "code\modules\surgery\advanced\bioware\nerve_splicing.dm" #include "code\modules\surgery\advanced\bioware\vein_threading.dm" -#include "code\modules\surgery\bodyparts\bodyparts.dm" +#include "code\modules\surgery\bodyparts\_bodyparts.dm" #include "code\modules\surgery\bodyparts\dismemberment.dm" #include "code\modules\surgery\bodyparts\head.dm" #include "code\modules\surgery\bodyparts\helpers.dm" +#include "code\modules\surgery\bodyparts\parts.dm" #include "code\modules\surgery\bodyparts\robot_bodyparts.dm" #include "code\modules\surgery\organs\appendix.dm" #include "code\modules\surgery\organs\augments_arms.dm"