From c9bf77d162215147f4214a37d4a8c3d420d4a6f0 Mon Sep 17 00:00:00 2001 From: Theos Date: Sat, 8 Jun 2024 05:48:32 -0400 Subject: [PATCH] Separates bleeding from damage, gauze now STOPS bleeding rather than postponing it (#3009) ![image](https://github.com/shiptest-ss13/Shiptest/assets/24857008/d5162c66-c24c-4115-8f17-b71fe2a7c97e) ![image](https://github.com/shiptest-ss13/Shiptest/assets/24857008/0035fc3d-ae4f-4bc8-b8a2-fab9e1652365) Bleeding rate is currently the same as it used to be (0.013 bleeding per damage) but can be increased beyond a limb's maximum damage from further attacks. Bleeding from damage caps at 2 per limb so exsanguinating someone isn't as easy as dumping lead into them until their blood falls out, and is hard-capped at 30 per limb for more direct sources such as throat cutting and heparin Blunt weapons require a force of 10 or higher, and for the limb to have 25 damage to cause bleeding Sharp weapons (including projectiles) need a force of 5 or higher, and for the limb to have 10 damage to cause bleeding Heparin now causes existing bleeding to get worse, rather than magically creating internal bleeding Bleeding is addressed through gauze, tape, cauterization or chemicals, with the former three halting current bleeding while it is being treated. Better equipment (primarily real medical gauze) heals faster. Numbers are non-final and speculative ## Why It's Good For The Game Causes bleeding to actually come from somewhere and be similarly addressed directly, gauze now exists to actually stop bleeding instead of being re-applied until the brute damage is fixed ## Changelog :cl: tweak: bleeding is now stored in the limbs, functioning similarly to bone breaking. Taking damage over a certain threshold and amount (lower for sharp weapons) will cause part of it to be turned into bleeding. tweak: gauze, tape, and bleeding suppression are similarly no longer abstracted into a "bleed suppression" value rscadd: you can now cauterize bleeding with a lighter. Which is cool. rscadd: you can now also cauterize bleeding with suit storage decontamination. Which is hot. rscadd: examine and examine closely will show whether or not someone is visibly bleeding or bandaged. This means people who are both will show both. Examine closely additionally shows which limbs are currently bleeding. tweak: heparin now causes existing bleeding to worsen, instead of causing bleeding on its own tweak: you can no longer cut the throat of someone who's head has been lopped off /:cl: --------- Signed-off-by: Theos --- code/__DEFINES/dcs/signals.dm | 5 ++ code/__DEFINES/mobs.dm | 7 ++ code/_onclick/item_attack.dm | 2 +- code/datums/components/bandage.dm | 60 ++++++++++++++++ code/datums/components/butchering.dm | 8 ++- .../diseases/advance/symptoms/flesh_eating.dm | 2 +- code/datums/status_effects/debuffs.dm | 3 +- code/game/machinery/medical_kiosk.dm | 2 +- code/game/machinery/suit_storage_unit.dm | 6 ++ code/game/objects/items/devices/scanners.dm | 2 +- code/game/objects/items/stacks/medical.dm | 24 ++++--- code/game/objects/items/stacks/tape.dm | 24 +++---- .../objects/structures/petrified_statue.dm | 5 +- .../kitchen_machinery/microwave.dm | 6 ++ code/modules/mob/living/blood.dm | 35 +++------ .../mob/living/carbon/carbon_defense.dm | 22 ++++++ .../modules/mob/living/carbon/damage_procs.dm | 71 ++++++++++++++++++- .../mob/living/carbon/human/damage_procs.dm | 4 +- .../mob/living/carbon/human/examine.dm | 25 ++++++- .../mob/living/carbon/human/human_defense.dm | 6 +- .../mob/living/carbon/human/human_defines.dm | 4 +- .../mob/living/carbon/human/species.dm | 8 +-- .../carbon/human/species_types/zombies.dm | 2 +- code/modules/mob/living/damage_procs.dm | 4 +- code/modules/mob/living/life.dm | 2 + code/modules/mob/living/living.dm | 2 +- code/modules/mob/living/living_defense.dm | 2 +- .../mob/living/silicon/damage_procs.dm | 2 +- .../hostile/human/cat_butcher.dm | 4 +- .../chemistry/reagents/medicine_reagents.dm | 4 +- .../chemistry/reagents/toxin_reagents.dm | 3 +- .../chemistry/reagents/trickwine_reagents.dm | 2 +- code/modules/surgery/bodyparts/bodyparts.dm | 43 ++++++++++- .../surgery/bodyparts/dismemberment.dm | 1 + code/modules/surgery/coronary_bypass.dm | 9 ++- code/modules/surgery/organic_steps.dm | 12 +++- code/modules/surgery/organs/vocal_cords.dm | 2 +- code/modules/surgery/surgery_helpers.dm | 4 +- shiptest.dme | 1 + 39 files changed, 335 insertions(+), 95 deletions(-) create mode 100644 code/datums/components/bandage.dm diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index f7267a7af8d3..97049254cc10 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -387,6 +387,9 @@ /* #define SPEECH_IGNORE_SPAM 6 #define SPEECH_FORCED 7 */ +///from /mob/living/life() +#define COMSIG_MOB_LIFE "mob_life" + ///from /mob/say_dead(): (mob/speaker, message) #define COMSIG_MOB_DEADSAY "mob_deadsay" #define MOB_DEADSAY_SIGNAL_INTERCEPT (1<<0) @@ -420,6 +423,8 @@ ///from base of /obj/item/bodypart/proc/attach_limb(): (new_limb, special) allows you to fail limb attachment #define COMSIG_LIVING_ATTACH_LIMB "living_attach_limb" #define COMPONENT_NO_ATTACH 1 +///from base of /obj/item/bodypart/proc/drop_limb(): (special) +#define COMSIG_LIVING_DROP_LIMB "living_drop_limb" ///from base of mob/living/set_buckled(): (new_buckled) #define COMSIG_LIVING_SET_BUCKLED "living_set_buckled" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 8026f9635a97..f5c382c15c29 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -28,6 +28,13 @@ #define BLOOD_VOLUME_BAD 224 #define BLOOD_VOLUME_SURVIVE 122 +// Bloodloss +#define BLOOD_LOSS_MAXIMUM 30 +#define BLOOD_LOSS_DAMAGE_MAXIMUM 2 +#define BLOOD_LOSS_DAMAGE_BASE 0.013 +#define BLOOD_CAUTERIZATION_RATIO 10 +#define BLOOD_CAUTERIZATION_DAMAGE_RATIO 300 + //Sizes of mobs, used by mob/living/var/mob_size #define MOB_SIZE_TINY 0 #define MOB_SIZE_SMALL 1 diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 1cf0585c0ed4..58b9604e585a 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -153,7 +153,7 @@ var/armor_value = run_armor_check(attack_flag = "melee", armour_penetration = I.armour_penetration) //WS Edit - Simplemobs can have armor send_item_attack_message(I, user) if(I.force) - apply_damage(I.force, I.damtype, break_modifier = I.force, blocked = armor_value) //Bone break modifier = item force + apply_damage(I.force, I.damtype, break_modifier = I.force, blocked = armor_value, sharpness = I.get_sharpness()) //Bone break modifier = item force if(I.damtype == BRUTE) if(prob(33)) I.add_mob_blood(src) diff --git a/code/datums/components/bandage.dm b/code/datums/components/bandage.dm new file mode 100644 index 000000000000..16f6a2f0b059 --- /dev/null +++ b/code/datums/components/bandage.dm @@ -0,0 +1,60 @@ +#define TREATMENT_DAMAGE_MOD 2 + +/datum/component/bandage + /// How fast do we stop bleeding? + var/bleed_reduction = 0 + /// How many healing ticks will this bandage apply? Reduced by incoming damage and current bleeding + var/lifespan = 300 + var/bandage_name = "gauze" + /// The person this bandage is applied to + var/mob/living/mummy + +/datum/component/bandage/Initialize(_bleed_reduction, _lifespan, _bandage_name) + if(!istype(parent, /obj/item/bodypart)) + return COMPONENT_INCOMPATIBLE + var/obj/item/bodypart/BP = parent + mummy = BP.owner + if(!mummy) + return COMPONENT_INCOMPATIBLE + if(_bleed_reduction) + bleed_reduction = _bleed_reduction + if(_lifespan) + lifespan = _lifespan + if(_bandage_name) + bandage_name = _bandage_name + RegisterSignal(mummy, COMSIG_MOB_APPLY_DAMGE, PROC_REF(check_damage)) + RegisterSignal(mummy, COMSIG_MOB_LIFE, PROC_REF(bandage_effects)) + RegisterSignal(parent, COMSIG_LIVING_DROP_LIMB, PROC_REF(drop_bandage)) + +/// Checks if damage to the owner is applied to this limb and reduces lifespan (perforated bandages dont work as well) +/datum/component/bandage/proc/check_damage(attacker, damage, damagetype = BRUTE, def_zone = null) + SIGNAL_HANDLER + + if(parent != mummy.get_bodypart(check_zone(def_zone))) + return + lifespan -= damage / 100 * initial(lifespan) * TREATMENT_DAMAGE_MOD //take incoming damage as a % of durability + if(lifespan <= 0) + drop_bandage() + +/// Handles healing effects and passive lifespan usage +/datum/component/bandage/proc/bandage_effects() + SIGNAL_HANDLER + + var/obj/item/bodypart/heal_target = parent + lifespan -= 1 + heal_target.bleeding // particularly nasty bleeding can burn through dressing faster + heal_target.adjust_bleeding(-bleed_reduction) + if(lifespan <= 0 || !heal_target.bleeding) //remove treatment once it's no longer able to treat + drop_bandage(TRUE) + +/// Handles deleting the component when the bandage runs out of lifespan or finishes healing. Special = bandage didn't get torn off +/datum/component/bandage/proc/drop_bandage(special = FALSE) + SIGNAL_HANDLER + + var/obj/item/bodypart/BP = parent + if(special) + to_chat(mummy, span_notice("The [bandage_name] on your [parse_zone(BP.body_zone)] has [BP.bleeding ? "done what it can" : "stopped the bleeding"].")) + else + to_chat(mummy, span_warning("The [bandage_name] on your [parse_zone(BP.body_zone)] is damaged beyond use!")) + qdel(src) + +#undef TREATMENT_DAMAGE_MOD diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index 55ba84fb5f7d..3032a98dc85c 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -73,11 +73,17 @@ "Their neck has already been already cut, you can't make the bleeding any worse!") return + var/obj/item/bodypart/throat_in_question = H.get_bodypart(BODY_ZONE_HEAD) + if(!throat_in_question) + user.show_message("[H]... doesn't have a neck.", MSG_VISUAL, \ + "They don't seem to have a neck to cut.") + return + H.visible_message("[user] slits [H]'s throat!", \ "[user] slits your throat...") log_combat(user, H, "finishes slicing the throat of") H.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD) - H.bleed_rate = clamp(H.bleed_rate + 20, 0, 30) + throat_in_question.adjust_bleeding(20) H.apply_status_effect(/datum/status_effect/neck_slice) /datum/component/butchering/proc/Butcher(mob/living/butcher, mob/living/meat) diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index f6cd5698d73d..5f18f6c78a91 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -63,7 +63,7 @@ Bonus if(bleed) if(ishuman(M)) var/mob/living/carbon/human/H = M - H.bleed_rate += 5 * power + H.cause_bleeding(5 * power) return 1 /* diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 52087f03174c..02b96c1b81de 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -334,7 +334,8 @@ /datum/status_effect/neck_slice/tick() var/mob/living/carbon/human/H = owner - if(H.stat == DEAD || H.bleed_rate <= 8) + var/obj/item/bodypart/throat_in_question = H.get_bodypart(BODY_ZONE_HEAD) + if(H.stat == DEAD || throat_in_question?.bleeding <= 8) H.remove_status_effect(/datum/status_effect/neck_slice) if(prob(10)) H.emote(pick("gasp", "gag", "choke")) diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm index 52933a5aba74..2f60c799ae23 100644 --- a/code/game/machinery/medical_kiosk.dm +++ b/code/game/machinery/medical_kiosk.dm @@ -172,7 +172,7 @@ sickness_data = "\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]" if(altPatient.has_dna()) //Blood levels Information - if(altPatient.bleed_rate) + if(LAZYLEN(altPatient.get_bleeding_parts())) bleed_status = "Patient is currently bleeding!" if(blood_percent <= 80) blood_warning = " Patient has low blood levels. Seek a large meal, or iron supplements." diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 7db02250ce94..107c5656c034 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -462,6 +462,12 @@ else visible_message(span_warning("[src]'s door slides open, barraging you with the nauseating smell of charred flesh.")) mob_occupant.radiation = 0 + if(iscarbon(mob_occupant)) + var/mob/living/carbon/bacon = mob_occupant + for(var/obj/item/bodypart/grilling as anything in bacon.get_bleeding_parts(TRUE)) + if(!grilling.can_bandage()) + continue + grilling.apply_bandage(0.005, 600, "cauterization") playsound(src, 'sound/machines/airlocks/standard/close.ogg', 25, TRUE) var/list/things_to_clear = list() //Done this way since using GetAllContents on the SSU itself would include circuitry and such. if(suit) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 3f40f82fe0ae..cdded7e418ef 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -357,7 +357,7 @@ GENE SCANNER if(blood_id) if(ishuman(C)) var/mob/living/carbon/human/H = C - if(H.bleed_rate) + if(LAZYLEN(H.get_bleeding_parts())) render_list += "Subject is bleeding!\n" var/blood_percent = round((C.blood_volume / BLOOD_VOLUME_NORMAL)*100) var/blood_type = C.dna.blood_type.name diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index ca3b1e184168..264ab12646ea 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -137,12 +137,13 @@ /obj/item/stack/medical/gauze name = "medical gauze" - desc = "A roll of elastic cloth that is extremely effective at stopping bleeding, but does not heal wounds." + desc = "A roll of elastic cloth that is extremely effective at stopping bleeding and slowly heals wounds." gender = PLURAL singular_name = "medical gauze" icon_state = "gauze" apply_sounds = list('sound/effects/rip1.ogg', 'sound/effects/rip2.ogg') - var/stop_bleeding = 1800 + var/bleed_reduction = 0.02 + var/lifespan = 150 self_delay = 20 max_amount = 12 grind_results = list(/datum/reagent/cellulose = 2) @@ -152,13 +153,16 @@ amount = 12 /obj/item/stack/medical/gauze/heal(mob/living/target, mob/user) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(!H.bleedsuppress && H.bleed_rate) //so you can't stack bleed suppression - H.suppress_bloodloss(stop_bleeding) - to_chat(user, "You stop the bleeding of [target]!") + if(iscarbon(target)) + var/mob/living/carbon/C = target + var/obj/item/bodypart/BP = C.get_bodypart(check_zone(user.zone_selected)) + if(!BP) + to_chat(user, span_warning("[C] doesn't have \a [parse_zone(user.zone_selected)]!")) + return + if(BP.can_bandage(user)) + BP.apply_bandage(bleed_reduction, lifespan, name) + user.visible_message(span_notice("[user] wraps [C]'s [parse_zone(BP.body_zone)] with [src]."), span_notice("You wrap [C]'s [parse_zone(check_zone(user.zone_selected))] with [src]."), span_hear("You hear ruffling cloth.")) return TRUE - to_chat(user, "You can not use \the [src] on [target]!") /obj/item/stack/medical/gauze/attackby(obj/item/I, mob/user, params) if(I.tool_behaviour == TOOL_WIRECUTTER || I.get_sharpness()) @@ -178,8 +182,8 @@ /obj/item/stack/medical/gauze/improvised name = "improvised gauze" singular_name = "improvised gauze" - desc = "A roll of cloth roughly cut from something that can stop bleeding, but does not heal wounds." - stop_bleeding = 900 + desc = "A roll of cloth roughly cut from something that can stop bleeding and slowly heal wounds." + bleed_reduction = 0.005 /obj/item/stack/medical/gauze/cyborg custom_materials = null diff --git a/code/game/objects/items/stacks/tape.dm b/code/game/objects/items/stacks/tape.dm index 6a984c021a26..63fc55116d5a 100644 --- a/code/game/objects/items/stacks/tape.dm +++ b/code/game/objects/items/stacks/tape.dm @@ -80,7 +80,8 @@ grind_results = list(/datum/reagent/cellulose = 5) usesound = 'sound/items/tape.ogg' - var/stop_bleed = 600 + var/lifespan = 300 + var/bleed_reduction = 0.002 var/nonorganic_heal = 5 var/self_delay = 30 //! Also used for the tapecuff delay var/other_delay = 10 @@ -173,21 +174,17 @@ if(!affecting) //Missing limb? to_chat(user, "[C] doesn't have \a [parse_zone(user.zone_selected)]!") return - if(!IS_ORGANIC_LIMB(affecting)) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(!H.bleedsuppress && H.bleed_rate) - H.suppress_bloodloss(stop_bleed) - to_chat(user, "You tape up the bleeding of [C]!") - return TRUE - to_chat(user, "[C] has a problem \the [src] won't fix!") - else //Robotic patch-up + if(IS_ROBOTIC_LIMB(affecting)) //Robotic patch-up if(affecting.brute_dam) user.visible_message("[user] applies \the [src] on [C]'s [affecting.name].", "You apply \the [src] on [C]'s [affecting.name].") if(affecting.heal_damage(nonorganic_heal)) C.update_damage_overlays() return TRUE - to_chat(user, "[src] can't patch what [C] has...") + if(affecting.can_bandage(user)) + affecting.apply_bandage(bleed_reduction, lifespan, name) + to_chat(user, "You tape up [C]'s [parse_zone(affecting.body_zone)]!") + return TRUE + to_chat(user, "[src] can't patch what [C] has...") /obj/item/stack/tape/proc/apply_gag(mob/living/carbon/target, mob/user) if(target.is_muzzled() || target.is_mouth_covered()) @@ -272,7 +269,7 @@ desc = "This roll of silver sorcery can fix just about anything." icon_state = "tape_d" - stop_bleed = 800 + lifespan = 400 nonorganic_heal = 20 prefix = "super sticky" conferred_embed = EMBED_HARMLESS_SUPERIOR @@ -297,7 +294,6 @@ desc = "Specialty insulated strips of adhesive plastic. Made for securing cables." icon_state = "tape_e" - stop_bleed = 400 nonorganic_heal = 10 prefix = "insulated sticky" siemens_coefficient = 0 @@ -321,6 +317,6 @@ desc = "Now THIS is engineering." icon_state = "tape_y" - stop_bleed = 1000 + lifespan = 500 nonorganic_heal = 30 prefix = "industry-standard sticky" diff --git a/code/game/objects/structures/petrified_statue.dm b/code/game/objects/structures/petrified_statue.dm index c8b804469255..1be0a2517bd6 100644 --- a/code/game/objects/structures/petrified_statue.dm +++ b/code/game/objects/structures/petrified_statue.dm @@ -58,6 +58,9 @@ if(petrified_mob) petrified_mob.status_flags &= ~GODMODE + if(ishuman(petrified_mob)) + var/mob/living/carbon/human/H = petrified_mob + H.bleedsuppress = FALSE petrified_mob.forceMove(loc) REMOVE_TRAIT(petrified_mob, TRAIT_MUTE, STATUE_MUTE) petrified_mob.take_overall_damage((petrified_mob.health - obj_integrity + 100)) //any new damage the statue incurred is transfered to the mob @@ -80,7 +83,7 @@ return 0 var/obj/structure/statue/petrified/S = new(loc, src, statue_timer) S.name = "statue of [name]" - bleedsuppress = 1 + bleedsuppress = TRUE S.copy_overlays(src) var/newcolor = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0)) S.add_atom_colour(newcolor, FIXED_COLOUR_PRIORITY) diff --git a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm index 678016182428..2762892110f8 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/microwave.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/microwave.dm @@ -385,6 +385,12 @@ playsound(src, 'sound/items/cig_light.ogg', 50, 1) moveToNullspace() + +/obj/item/ration_heater/get_temperature() + if(!uses) + return 0 + . = ..() + /obj/item/ration_heater/proc/clear_cooking(datum/source) SIGNAL_HANDLER UnregisterSignal(tocook, COMSIG_PARENT_QDELETING) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index aec75960989d..1910347e4fdd 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -4,19 +4,6 @@ BLOOD SYSTEM ****************************************************/ -/mob/living/carbon/human/proc/suppress_bloodloss(amount) - if(bleedsuppress) - return - else - bleedsuppress = TRUE - addtimer(CALLBACK(src, PROC_REF(resume_bleeding)), amount) - -/mob/living/carbon/human/proc/resume_bleeding() - bleedsuppress = 0 - if(stat != DEAD && bleed_rate) - to_chat(src, "The blood soaks through your bandage.") - - /mob/living/carbon/monkey/handle_blood() if(bodytemperature >= TCRYO && !(HAS_TRAIT(src, TRAIT_HUSK))) //cryosleep or husked people do not pump the blood. //Blood regeneration if there is some space @@ -29,7 +16,6 @@ /mob/living/carbon/human/handle_blood() if(NOBLOOD in dna.species.species_traits) - bleed_rate = 0 return if(bodytemperature >= TCRYO && !(HAS_TRAIT(src, TRAIT_HUSK))) //cryosleep or husked people do not pump the blood. @@ -83,24 +69,20 @@ if(!HAS_TRAIT(src, TRAIT_NODEATH)) death() - var/temp_bleed = 0 //Bleeding out + var/limb_bleed = 0 for(var/obj/item/bodypart/BP as anything in bodyparts) - var/brutedamage = BP.brute_dam - + if(BP.GetComponent(/datum/component/bandage)) + continue //We want an accurate reading of .len listclearnulls(BP.embedded_objects) for(var/obj/item/embeddies in BP.embedded_objects) if(!embeddies.isEmbedHarmless()) - temp_bleed += 0.5 - - if(brutedamage >= 20) - temp_bleed += (brutedamage * 0.013) - - bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects (heparin) naturally decreases + BP.adjust_bleeding(0.1, BLOOD_LOSS_DAMAGE_MAXIMUM) + limb_bleed += BP.bleeding - if(bleed_rate && !bleedsuppress && !(HAS_TRAIT(src, TRAIT_FAKEDEATH))) - bleed(bleed_rate) + if(limb_bleed && !bleedsuppress && !HAS_TRAIT(src, TRAIT_FAKEDEATH)) + bleed(limb_bleed) //Makes a blood drop, leaking amt units of blood from the mob /mob/living/carbon/proc/bleed(amt) @@ -125,7 +107,8 @@ /mob/living/carbon/human/restore_blood() blood_volume = BLOOD_VOLUME_NORMAL - bleed_rate = 0 + for(var/obj/item/bodypart/BP as anything in get_bleeding_parts()) + BP.bleeding = 0 /**************************************************** BLOOD TRANSFERS diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 8377fc0833d6..916095ed266e 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -1,3 +1,25 @@ +/mob/living/carbon/attackby(obj/item/W, mob/user, params) + var/obj/item/bodypart/BP = get_bodypart(check_zone(user.zone_selected)) + var/has_painkillers = user.reagents.has_reagent(/datum/reagent/medicine/morphine, needs_metabolizing = TRUE) + if(W.tool_behaviour == TOOL_WELDER && IS_ROBOTIC_LIMB(BP) && BP.brute_dam > 5) //prioritize healing if we're synthetic + return ..() + if(user.a_intent != INTENT_HELP || !W.get_temperature() || !BP.can_bandage()) //this will also catch low damage synthetic welding + return ..() + . = TRUE + var/heal_time = 2 SECONDS + playsound(user, 'sound/surgery/cautery1.ogg', 20) + balloon_alert(user, "cauterizing...") + if(src == user && !has_painkillers) + heal_time *= 2 //oof ouch owie + user.visible_message(span_nicegreen("[user] holds [W] up to [user == src ? "their" : "[src]'s"] [parse_zone(BP.body_zone)], trying to slow [p_their()] bleeding..."), span_nicegreen("You hold [W] up to [user == src ? "your" : "[src]'s"] [parse_zone(BP.body_zone)], trying to slow [user == src ? "your" : p_their()] bleeding...")) + if(do_after(user, heal_time, target = src)) + playsound(user, 'sound/surgery/cautery2.ogg', 20) + BP.apply_bandage(0.005, W.get_temperature()/BLOOD_CAUTERIZATION_RATIO, "cauterization") //not particularly fast, this is the "I really would prefer not to be bleeding right now" option + BP.receive_damage(burn = W.get_temperature()/BLOOD_CAUTERIZATION_DAMAGE_RATIO) //my body is a MACHINE that turns BLEEDING into BURN DAMAGE + user.visible_message(span_nicegreen("[user] cauterizes the bleeding on [user == src ? "their" : "[src]'s"] [parse_zone(BP.body_zone)]!"), span_nicegreen("You cauterize the bleeding on [user == src ? "your" : "[src]'s"] [parse_zone(BP.body_zone)]!")) + else + to_chat(user, span_warning("You were interrupted!")) + /mob/living/carbon/get_eye_protection() . = ..() var/obj/item/organ/eyes/E = getorganslot(ORGAN_SLOT_EYES) diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm index 2045bfe4aa18..8c023299baed 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, break_modifier = 1) +/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, break_modifier = 1, sharpness = FALSE) SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) var/hit_percent = (100-blocked)/100 if(!damage || (!forced && hit_percent <= 0)) @@ -21,7 +21,7 @@ switch(damagetype) if(BRUTE) if(BP) - if(BP.receive_damage(damage_amount, 0, break_modifier)) + if(BP.receive_damage(damage_amount, 0, break_modifier, sharpness = sharpness)) update_damage_overlays() else //no bodypart, we deal damage with a more general method. adjustBruteLoss(damage_amount, forced = forced) @@ -29,7 +29,7 @@ shake_animation(damage_amount) if(BURN) if(BP) - if(BP.receive_damage(0, damage_amount, break_modifier)) + if(BP.receive_damage(0, damage_amount, break_modifier, sharpness = sharpness)) update_damage_overlays() else adjustFireLoss(damage_amount, forced = forced) @@ -267,3 +267,68 @@ if(update) update_damage_overlays() update_stamina() + +/// Gets a list of bleeding bodyparts, argument ignore_staunched = are we actively bleeding (no treatment) +/mob/living/carbon/proc/get_bleeding_parts(ignore_staunched = FALSE) + var/list/obj/item/bodypart/parts = list() + for(var/obj/item/bodypart/BP as anything in bodyparts) + if(BP.bleeding && (!ignore_staunched || !BP.GetComponent(/datum/component/bandage))) + parts += BP + return parts + +/// Gets a list of bandaged parts +/mob/living/carbon/proc/get_bandaged_parts() + var/list/obj/item/bodypart/parts = list() + for(var/obj/item/bodypart/BP as anything in bodyparts) + if(BP.GetComponent(/datum/component/bandage)) + parts += BP + return parts + +/// Apply bleeding to one random bodypart. +/mob/living/carbon/proc/cause_bleeding(amt) + if(amt <= 0) + return + var/list/obj/item/bodypart/parts = bodyparts.Copy() + if(!length(parts)) + return + var/obj/item/bodypart/part_in_question = pick(parts) + part_in_question.adjust_bleeding(amt) + +/// Heal bleeding from one random bodypart +/mob/living/carbon/proc/heal_bleeding(amt) + if(amt <= 0) + return + var/list/obj/item/bodypart/parts = get_bleeding_parts() + if(!length(parts)) + return + var/obj/item/bodypart/part_in_question = pick(parts) + part_in_question.adjust_bleeding(-amt) + var/bleed_calc = part_in_question.bleeding + return min(bleed_calc - part_in_question.bleeding, 0) + +/// Apply bleeding to all bodyparts +/mob/living/carbon/proc/cause_overall_bleeding(amt) + if(amt <= 0) + return + var/list/obj/item/bodypart/parts = bodyparts.Copy() + while(length(parts)) + var/obj/item/bodypart/part_in_question = pick(parts) + if(part_in_question.is_pseudopart) + parts -= part_in_question + continue + var/amount_to_take = min(part_in_question.bleeding, amt / length(parts)) + part_in_question.adjust_bleeding(amount_to_take) + amt -= amount_to_take + parts -= part_in_question + +/// Heal bleeding from all bodyparts +/mob/living/carbon/proc/heal_overall_bleeding(amt) + if(amt <= 0) + return + var/list/obj/item/bodypart/parts = get_bleeding_parts() + while(length(parts)) + var/obj/item/bodypart/part_in_question = pick(parts) + var/amount_to_take = min(part_in_question.bleeding, amt / length(parts)) + part_in_question.adjust_bleeding(-amount_to_take) + amt -= amount_to_take + parts -= part_in_question diff --git a/code/modules/mob/living/carbon/human/damage_procs.dm b/code/modules/mob/living/carbon/human/damage_procs.dm index 4883446b7cb1..d7f6834030b3 100644 --- a/code/modules/mob/living/carbon/human/damage_procs.dm +++ b/code/modules/mob/living/carbon/human/damage_procs.dm @@ -1,7 +1,7 @@ /// 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, break_modifier = 1) //WS Edit - Breakable Bones - return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage) +/mob/living/carbon/human/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, break_modifier = 1, sharpness = FALSE) //WS Edit - Breakable Bones + return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, forced, spread_damage, sharpness = sharpness) /mob/living/carbon/human/revive(full_heal = 0, admin_revive = 0) if(..()) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 64882c75ec4e..4430fc1da300 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -257,9 +257,12 @@ if(blood_volume < BLOOD_VOLUME_SAFE || skin_tone == "albino") msg += "[t_He] [t_has] pale skin.\n" - if(bleedsuppress) - msg += "[t_He] [t_is] bandaged with something.\n" - else if(bleed_rate) + + if(LAZYLEN(get_bandaged_parts())) + msg += "[t_He] [t_has] some dressed bleeding.\n" + + var/list/obj/item/bodypart/bleed_check = get_bleeding_parts(TRUE) + if(LAZYLEN(bleed_check)) if(reagents.has_reagent(/datum/reagent/toxin/heparin, needs_metabolizing = TRUE)) msg += "[t_He] [t_is] bleeding uncontrollably!\n" else @@ -414,6 +417,22 @@ /mob/living/carbon/human/examine_more(mob/user) . = ..() + for(var/obj/item/bodypart/BP as anything in get_bandaged_parts()) + var/datum/component/bandage/B = BP.GetComponent(/datum/component/bandage) + . += span_notice("[p_their(TRUE)] [parse_zone(BP.body_zone)] is dressed with [B.bandage_name]") + for(var/obj/item/bodypart/BP as anything in get_bleeding_parts(TRUE)) + var/bleed_text + switch(BP.bleeding) + if(0 to 0.5) + bleed_text = "lightly." + if(0.5 to 1) + bleed_text = "moderately." + if(1 to 1.5) + bleed_text = "heavily!" + else + bleed_text = "significantly!!" + . += span_warning("[p_their(TRUE)] [parse_zone(BP.body_zone)] is bleeding [bleed_text]") + if ((wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE))) return if(get_age()) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 3e3fd2209856..1cb061ff9fd9 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -779,10 +779,10 @@ combined_msg += "\t There is \a [I] embedded in your [LB.name]!" for(var/t in missing) - combined_msg += "Your [parse_zone(t)] is missing!" + combined_msg += span_boldannounce("Your [parse_zone(t)] is missing!") - if(bleed_rate) - combined_msg += "You are bleeding!" + for(var/obj/item/bodypart/BP in get_bleeding_parts(TRUE)) + combined_msg += span_danger("Your [parse_zone(BP.body_zone)] is bleeding!") if(getStaminaLoss()) if(getStaminaLoss() > 30) combined_msg += "You're completely exhausted." diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 8cc9e5002749..6556335c2bdb 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -63,9 +63,7 @@ /// Adjective used in get_generic_name(), if any var/generic_adjective - - var/bleed_rate = 0 //how much are we bleeding - var/bleedsuppress = 0 //for stopping bloodloss, eventually this will be limb-based like bleeding + var/bleedsuppress = 0 //for stopping bloodloss body-wide var/name_override //For temporary visible name changes diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index e848bc01d251..e061cd5c9793 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -1618,7 +1618,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) var/armor_block = H.run_armor_check(affecting, "melee", I.armour_penetration, FALSE, "Your armor has protected your [hit_area]!", "Your armor has softened a hit to your [hit_area]!") armor_block = min(90,armor_block) //cap damage reduction at 90% - apply_damage(I.force, I.damtype, def_zone, armor_block, H) + apply_damage(I.force, I.damtype, def_zone, armor_block, H, sharpness = I.get_sharpness()) H.send_item_attack_message(I, user, hit_area) @@ -1687,7 +1687,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) return TRUE -/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, break_modifier = 1) +/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, break_modifier = 1, sharpness = FALSE) SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) var/hit_percent = (100-(blocked+armor))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 @@ -1710,7 +1710,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.damageoverlaytemp = 20 var/damage_amount = forced ? damage : damage * hit_percent * brutemod * H.physiology.brute_mod if(BP) - if(BP.receive_damage(damage_amount, 0, break_modifier = break_modifier)) + if(BP.receive_damage(damage_amount, 0, break_modifier = break_modifier, sharpness = sharpness)) H.update_damage_overlays() else//no bodypart, we deal damage with a more general method. H.adjustBruteLoss(damage_amount) @@ -1720,7 +1720,7 @@ GLOBAL_LIST_EMPTY(roundstart_races) H.damageoverlaytemp = 20 var/damage_amount = forced ? damage : damage * hit_percent * burnmod * H.physiology.burn_mod if(BP) - if(BP.receive_damage(0, damage_amount, break_modifier = break_modifier)) + if(BP.receive_damage(0, damage_amount, break_modifier = break_modifier, sharpness = sharpness)) H.update_damage_overlays() else H.adjustFireLoss(damage_amount) 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 702adfb224a2..c90cbd0dbc8f 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -46,7 +46,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, sharpness = FALSE) . = ..() if(.) regen_cooldown = world.time + REGENERATION_DELAY diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 76daa5ba21d4..430d02f7f1ea 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -11,10 +11,12 @@ * * blocked - armor value applied * * forced - bypass hit percentage * * spread_damage - used in overrides + * * break_modifier - increases bone breaking chance + * * sharpness - used for bleeding * * Returns TRUE if damage applied */ -/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, break_modifier = 1)//WS Edit - Breakable Bones +/mob/living/proc/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, break_modifier = 1, sharpness = FALSE)//WS Edit - Breakable Bones SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone) var/hit_percent = (100-blocked)/100 if(!damage || (!forced && hit_percent <= 0) || !(flags_1 & INITIALIZED_1)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 47fc5bd82ecb..a25025294497 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -38,6 +38,8 @@ if (QDELETED(src)) // diseases can qdel the mob via transformations return + SEND_SIGNAL(src, COMSIG_MOB_LIFE) + if(stat != DEAD) //Random events (vomiting etc) handle_random_events() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index cb0ec02ced34..5c499fe21029 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -820,7 +820,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) || bleedsuppress || !LAZYLEN(get_bleeding_parts(TRUE))) return ..() diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index c1c4cd668792..eb622de65f28 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -54,7 +54,7 @@ var/armor = run_armor_check(def_zone, P.flag, P.armour_penetration, silent = TRUE) var/on_hit_state = P.on_hit(src, armor, piercing_hit) if(!P.nodamage && on_hit_state != BULLET_ACT_BLOCK && !QDELETED(src)) //QDELETED literally just for the instagib rifle. Yeah. - apply_damage(P.damage, P.damage_type, def_zone, armor) + apply_damage(P.damage, P.damage_type, def_zone, armor, sharpness = TRUE) recoil_camera(src, clamp((P.damage-armor)/4,0.5,10), clamp((P.damage-armor)/4,0.5,10), P.damage/8, P.Angle) apply_effects(P.stun, P.knockdown, P.unconscious, P.irradiate, P.slur, P.stutter, P.eyeblur, P.drowsy, armor, P.stamina, P.jitter, P.paralyze, P.immobilize) if(P.dismemberment) diff --git a/code/modules/mob/living/silicon/damage_procs.dm b/code/modules/mob/living/silicon/damage_procs.dm index a6d86d1507ba..80c643e0ceef 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, break_modifier = 1) +/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, break_modifier = 1, sharpness = FALSE) var/hit_percent = (100-blocked)/100 if((!damage || (!forced && hit_percent <= 0))) return 0 diff --git a/code/modules/mob/living/simple_animal/hostile/human/cat_butcher.dm b/code/modules/mob/living/simple_animal/hostile/human/cat_butcher.dm index 0e265fc3b531..0f14f615e5b6 100644 --- a/code/modules/mob/living/simple_animal/hostile/human/cat_butcher.dm +++ b/code/modules/mob/living/simple_animal/hostile/human/cat_butcher.dm @@ -68,11 +68,11 @@ L.adjustOxyLoss(-50)// do CPR first if(L.blood_volume <= 500) //bandage them up and give em some blood if they're bleeding L.blood_volume += 30 - L.suppress_bloodloss(1800) + L.heal_bleeding(10) if(L.getBruteLoss() >= 50)// first, did we beat them into crit? if so, heal that var/healing = min(L.getBruteLoss(), 120) L.adjustBruteLoss(-healing) - L.suppress_bloodloss(1800)//bandage their ass + L.heal_bleeding(10) return else if(L.getFireLoss() >= 50) // are they still down from other damage? fix it, but not as fast as the burns var/healing = min(L.getFireLoss(), 50) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 7fb71324d7af..4c4ad36b2413 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -1091,7 +1091,7 @@ M.adjustBruteLoss(-2*REM, 0) if(ishuman(M)) var/mob/living/carbon/human/H = M - H.bleed_rate = max(H.bleed_rate - 0.25, 0) + H.heal_bleeding(0.25) ..() . = 1 @@ -1638,7 +1638,7 @@ if(prob(50)) if(ishuman(M)) var/mob/living/carbon/human/H = M - H.bleed_rate = max(H.bleed_rate - 2, 0) + H.heal_bleeding(2) ..() . = 1 diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index e03c84dd7840..498017191179 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -769,7 +769,8 @@ /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) + for(var/obj/item/bodypart/BP in H.get_bleeding_parts()) + BP.adjust_bleeding(BP.bleeding * 0.1) H.adjustBruteLoss(1, 0) //Brute damage increases with the amount they're bleeding . = 1 return ..() || . diff --git a/code/modules/reagents/chemistry/reagents/trickwine_reagents.dm b/code/modules/reagents/chemistry/reagents/trickwine_reagents.dm index 88f1871e6a28..be5e2ce35f9a 100644 --- a/code/modules/reagents/chemistry/reagents/trickwine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/trickwine_reagents.dm @@ -115,7 +115,7 @@ M.adjust_bodytemperature(5 * TEMPERATURE_DAMAGE_COEFFICIENT, M.get_body_temp_normal()) if(ishuman(M)) var/mob/living/carbon/human/H = M - H.bleed_rate = max(H.bleed_rate - 0.25, 0) + H.heal_bleeding(0.25) return ..() /datum/reagent/consumable/ethanol/trickwine/hearth_wine/expose_mob(mob/living/M, method=TOUCH, reac_volume) diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 7cf31dc83ca6..92cb95a9c605 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -50,6 +50,16 @@ /// Is it fine, broken, splinted, or just straight up fucking gone var/bone_status = BONE_FLAG_NO_BONES var/bone_break_threshold = 30 + /// Threshold at which the limb will start bleeding if damaged by sharp items or projectiles + var/bleed_threshold = 10 + /// Threshold at which the limb will start bleeding if damaged by blunt items + var/bleed_threshold_blunt = 25 + /// Minimum damage of an incoming attack for it to cause bleeding + var/bleed_damage_min = 5 + /// Minimum damage of an incoming blunt attack for it to cause bleeding + var/bleed_damage_min_blunt = 10 + /// Current limb bleeding, increased when the limb takes brute damage over certain thresholds, decreased through bandages and cauterization + var/bleeding = 0 /// So we know if we need to scream if this limb hits max damage var/last_maxed @@ -202,11 +212,13 @@ if(stamina_dam > DAMAGE_PRECISION && owner.stam_regen_start_time <= world.time) //DO NOT update health here, it'll be done in the carbon's life. heal_damage(0, 0, INFINITY, null, FALSE) . |= BODYPART_LIFE_UPDATE_HEALTH + if(brute_dam < DAMAGE_PRECISION && bleeding) + adjust_bleeding(-0.2) //slowly stop bleeding if there's no damage left //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, blocked = 0, updating_health = TRUE, required_status = null, break_modifier = 1) +/obj/item/bodypart/proc/receive_damage(brute = 0, burn = 0, stamina = 0, blocked = 0, updating_health = TRUE, required_status = null, break_modifier = 1, sharpness = FALSE) var/hit_percent = (100-blocked)/100 if((!brute && !burn && !stamina) || hit_percent <= 0) return FALSE @@ -235,6 +247,10 @@ if((brute_dam > bone_break_threshold) && prob(brute_dam + break_modifier)) break_bone() + // Bleeding is applied here + if(brute_dam+brute >= (sharpness ? bleed_threshold : bleed_threshold_blunt) && brute >= (sharpness ? bleed_damage_min : bleed_damage_min_blunt)) + adjust_bleeding(brute * BLOOD_LOSS_DAMAGE_BASE, BLOOD_LOSS_DAMAGE_MAXIMUM) + var/can_inflict = max_damage - get_damage() if(can_inflict <= 0) return FALSE @@ -274,6 +290,7 @@ if(brute) set_brute_dam(round(max(brute_dam - brute, 0), DAMAGE_PRECISION)) + adjust_bleeding(-BLOOD_LOSS_DAMAGE_MAXIMUM * brute / max_damage) if(burn) set_burn_dam(round(max(burn_dam - burn, 0), DAMAGE_PRECISION)) if(stamina) @@ -315,6 +332,30 @@ . = stamina_dam stamina_dam = new_value +/// Adjusts bodypart bleeding, value = amount of change, maximum = maximum current bloodloss amount this can modify +/obj/item/bodypart/proc/adjust_bleeding(value, maximum = BLOOD_LOSS_MAXIMUM) + if(bleeding > maximum) + return + if(owner.dna && (NOBLOOD in owner.dna.species.species_traits)) + return + bleeding = round(clamp(bleeding+value, 0, maximum), 0.001) + +/// Checks if the bodypart is viable for bandaging, if it isn't, tells the person trying (if present) what's stopping it +/obj/item/bodypart/proc/can_bandage(user) + . = TRUE + if(is_pseudopart) + return FALSE + if(!bleeding) + if(user) + to_chat(user, span_warning("[owner]'s [parse_zone(body_zone)] isn't bleeding!")) + return FALSE + if(GetComponent(/datum/component/bandage)) + if(user) + to_chat(user, span_warning("[owner]'s [parse_zone(body_zone)] has already been dressed!")) + return FALSE + +/obj/item/bodypart/proc/apply_bandage(bleed_reduction, lifespan, name) + AddComponent(/datum/component/bandage, bleed_reduction, lifespan, name) //Returns total damage. /obj/item/bodypart/proc/get_damage(include_stamina = FALSE) diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 7c292ac21fc4..07d30e727c77 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -90,6 +90,7 @@ return var/atom/Tsec = owner.drop_location() var/mob/living/carbon/C = owner + SEND_SIGNAL(src, COMSIG_LIVING_DROP_LIMB) update_limb(TRUE) C.remove_bodypart(src) diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm index 4c416c31c633..a4628d19efff 100644 --- a/code/modules/surgery/coronary_bypass.dm +++ b/code/modules/surgery/coronary_bypass.dm @@ -41,7 +41,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(check_zone(surgery.location)) + BP.adjust_bleeding(10) target.apply_damage(15, BRUTE, "[target_zone]") return ..() @@ -51,7 +52,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(check_zone(surgery.location)) + BP.adjust_bleeding(20) H.adjustOrganLoss(ORGAN_SLOT_HEART, 10) target.apply_damage(15, BRUTE, "[target_zone]") @@ -90,5 +92,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, 30) - H.bleed_rate += 30 + var/obj/item/bodypart/BP = H.get_bodypart(check_zone(surgery.location)) + BP.adjust_bleeding(30) return FALSE diff --git a/code/modules/surgery/organic_steps.dm b/code/modules/surgery/organic_steps.dm index 5167bb3a4517..42018e6c1d87 100644 --- a/code/modules/surgery/organic_steps.dm +++ b/code/modules/surgery/organic_steps.dm @@ -30,7 +30,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 = H.get_bodypart(check_zone(surgery.location)) + if(BP) + BP.adjust_bleeding(3) return ..() /datum/surgery_step/incise/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) @@ -74,7 +76,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(check_zone(surgery.location)) + if(BP) + BP.adjust_bleeding(-3) return ..() /datum/surgery_step/clamp_bleeders/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) @@ -137,7 +141,9 @@ target.heal_bodypart_damage(15,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(check_zone(surgery.location)) + if(BP) + BP.adjust_bleeding(-3) return ..() //saw bone diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 5eed8abc46be..c0313adb2539 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -286,7 +286,7 @@ else if((findtext(message, bleed_words))) cooldown = COOLDOWN_DAMAGE for(var/mob/living/carbon/human/H in listeners) - H.bleed_rate += (5 * power_multiplier) + H.cause_overall_bleeding(5*power_multiplier) //FIRE else if((findtext(message, burn_words))) diff --git a/code/modules/surgery/surgery_helpers.dm b/code/modules/surgery/surgery_helpers.dm index c28ce0855933..f87c6e1bb923 100644 --- a/code/modules/surgery/surgery_helpers.dm +++ b/code/modules/surgery/surgery_helpers.dm @@ -115,7 +115,9 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M - H.bleed_rate = max((H.bleed_rate - 3), 0) + var/obj/item/bodypart/BP = H.get_bodypart(check_zone(S.location)) + if(BP) + BP.adjust_bleeding(-3) M.surgeries -= S user.visible_message("[user] closes [M]'s [parse_zone(selected_zone)] with [close_tool] and stops the surgery.", \ "You close [M]'s [parse_zone(selected_zone)] with [close_tool] and stop the surgery.") diff --git a/shiptest.dme b/shiptest.dme index 630c5d479767..565b4dec6c8f 100644 --- a/shiptest.dme +++ b/shiptest.dme @@ -460,6 +460,7 @@ #include "code\datums\components\anti_magic.dm" #include "code\datums\components\armor_plate.dm" #include "code\datums\components\art.dm" +#include "code\datums\components\bandage.dm" #include "code\datums\components\bane.dm" #include "code\datums\components\beetlejuice.dm" #include "code\datums\components\bloodysoles.dm"