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"