From c4fa0d4affcd7f2273f4ae886fa83c26bb0c7391 Mon Sep 17 00:00:00 2001 From: Ryll Ryll <3589655+Ryll-Ryll@users.noreply.github.com> Date: Mon, 28 Mar 2022 13:24:03 -0400 Subject: [PATCH] Adds mob bleeding overlays, tweaks some bleeding balance (#65371) --- code/__DEFINES/mobs.dm | 62 +++++---- code/__DEFINES/wounds.dm | 6 +- code/datums/status_effects/debuffs.dm | 7 +- code/datums/wounds/_wounds.dm | 4 +- code/datums/wounds/slash.dm | 20 +-- code/modules/mob/living/blood.dm | 130 ++++++++++-------- code/modules/mob/living/carbon/carbon.dm | 4 +- .../mob/living/carbon/carbon_context.dm | 2 +- .../mob/living/carbon/carbon_defense.dm | 6 +- .../mob/living/carbon/carbon_update_icons.dm | 25 +++- .../mob/living/carbon/human/examine.dm | 2 +- .../mob/living/carbon/human/human_defense.dm | 2 +- code/modules/mob/living/living.dm | 4 + code/modules/surgery/bodyparts/_bodyparts.dm | 66 +++++++-- icons/mob/bleed_overlays.dmi | Bin 0 -> 1742 bytes 15 files changed, 216 insertions(+), 124 deletions(-) create mode 100644 icons/mob/bleed_overlays.dmi diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 5492f83cfa9..cf2d69b7059 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -511,65 +511,67 @@ // Mob Overlays Indexes /// Total number of layers for mob overlays -#define TOTAL_LAYERS 31 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; +#define TOTAL_LAYERS 32 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; /// Mutations layer - Tk headglows, cold resistance glow, etc -#define MUTATIONS_LAYER 31 +#define MUTATIONS_LAYER 32 /// Mutantrace features (tail when looking south) that must appear behind the body parts -#define BODY_BEHIND_LAYER 30 +#define BODY_BEHIND_LAYER 31 /// Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag -#define BODYPARTS_LAYER 29 +#define BODYPARTS_LAYER 30 /// Mutantrace features (snout, body markings) that must appear above the body parts -#define BODY_ADJ_LAYER 28 +#define BODY_ADJ_LAYER 29 /// Underwear, undershirts, socks, eyes, lips(makeup) -#define BODY_LAYER 27 +#define BODY_LAYER 28 /// Mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) -#define FRONT_MUTATIONS_LAYER 26 +#define FRONT_MUTATIONS_LAYER 27 /// Damage indicators (cuts and burns) -#define DAMAGE_LAYER 25 +#define DAMAGE_LAYER 26 /// Jumpsuit clothing layer -#define UNIFORM_LAYER 24 +#define UNIFORM_LAYER 25 /// ID card layer (might be deprecated) -#define ID_LAYER 23 +#define ID_LAYER 24 /// ID card layer -#define ID_CARD_LAYER 22 +#define ID_CARD_LAYER 23 /// Hands body part layer (or is this for the arm? not sure...) -#define HANDS_PART_LAYER 21 +#define HANDS_PART_LAYER 22 /// Gloves layer -#define GLOVES_LAYER 20 +#define GLOVES_LAYER 21 /// Shoes layer -#define SHOES_LAYER 19 +#define SHOES_LAYER 20 /// Ears layer (Spessmen have ears? Wow) -#define EARS_LAYER 18 +#define EARS_LAYER 19 /// Suit layer (armor, hardsuits, etc.) -#define SUIT_LAYER 17 +#define SUIT_LAYER 18 /// Glasses layer -#define GLASSES_LAYER 16 +#define GLASSES_LAYER 17 /// Belt layer -#define BELT_LAYER 15 //Possible make this an overlay of somethign required to wear a belt? +#define BELT_LAYER 16 //Possible make this an overlay of somethign required to wear a belt? /// Suit storage layer (tucking a gun or baton underneath your armor) -#define SUIT_STORE_LAYER 14 +#define SUIT_STORE_LAYER 15 /// Neck layer (for wearing ties and bedsheets) -#define NECK_LAYER 13 +#define NECK_LAYER 14 /// Back layer (for backpacks and equipment on your back) -#define BACK_LAYER 12 +#define BACK_LAYER 13 /// Hair layer (mess with the fro and you got to go!) -#define HAIR_LAYER 11 //TODO: make part of head layer? +#define HAIR_LAYER 12 //TODO: make part of head layer? /// Facemask layer (gas masks, breath masks, etc.) -#define FACEMASK_LAYER 10 +#define FACEMASK_LAYER 11 /// Head layer (hats, helmets, etc.) -#define HEAD_LAYER 9 +#define HEAD_LAYER 10 /// Handcuff layer (when your hands are cuffed) -#define HANDCUFF_LAYER 8 +#define HANDCUFF_LAYER 9 /// Legcuff layer (when your feet are cuffed) -#define LEGCUFF_LAYER 7 +#define LEGCUFF_LAYER 8 /// Hands layer (for the actual hand, not the arm... I think?) -#define HANDS_LAYER 6 +#define HANDS_LAYER 7 /// Body front layer. Usually used for mutant bodyparts that need to be in front of stuff (e.g. cat ears) -#define BODY_FRONT_LAYER 5 +#define BODY_FRONT_LAYER 6 /// Special body layer that actually require to be above the hair (e.g. lifted welding goggles) -#define ABOVE_BODY_FRONT_GLASSES_LAYER 4 +#define ABOVE_BODY_FRONT_GLASSES_LAYER 5 /// Special body layer for the rare cases where something on the head needs to be above everything else (e.g. flowers) -#define ABOVE_BODY_FRONT_HEAD_LAYER 3 +#define ABOVE_BODY_FRONT_HEAD_LAYER 4 +/// Bleeding wound icons +#define WOUND_LAYER 3 /// Blood cult ascended halo layer, because there's currently no better solution for adding/removing #define HALO_LAYER 2 /// Fire layer when you're on fire diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 3575397d4d1..f30947c0362 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -77,10 +77,12 @@ GLOBAL_LIST_INIT(global_all_wound_types, list(/datum/wound/blunt/critical, /datu // ~random wound balance defines /// how quickly sanitization removes infestation and decays per second #define WOUND_BURN_SANITIZATION_RATE 0.075 -/// how much blood you can lose per second per slash max. 4 is a LOT of blood for one cut so don't worry about hitting it easily -#define WOUND_SLASH_MAX_BLOODFLOW 4 +/// how much blood you can lose per tick per slash max. +#define WOUND_SLASH_MAX_BLOODFLOW 4.5 /// dead people don't bleed, but they can clot! this is the minimum amount of clotting per tick on dead people, so even critical cuts will slowly clot in dead people #define WOUND_SLASH_DEAD_CLOT_MIN 0.025 +/// further slash attacks on a bodypart with a slash wound have their blood_flow further increased by damage * this (10 damage slash adds .25 flow) +#define WOUND_SLASH_DAMAGE_FLOW_COEFF 0.025 /// if we suffer a bone wound to the head that creates brain traumas, the timer for the trauma cycle is +/- by this percent (0-100) #define WOUND_BONE_HEAD_TIME_VARIANCE 20 diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 245f5ac65b6..16c4d634ce6 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -243,7 +243,9 @@ owner.add_filter("stasis_status_ripple", 2, list("type" = "ripple", "flags" = WAVE_BOUNDED, "radius" = 0, "size" = 2)) var/filter = owner.get_filter("stasis_status_ripple") animate(filter, radius = 32, time = 15, size = 0, loop = -1) - + if(iscarbon(owner)) + var/mob/living/carbon/carbon_owner = owner + carbon_owner.update_bodypart_bleed_overlays() /datum/status_effect/grouped/stasis/tick() update_time_of_death() @@ -253,6 +255,9 @@ REMOVE_TRAIT(owner, TRAIT_HANDS_BLOCKED, TRAIT_STATUS_EFFECT(id)) owner.remove_filter("stasis_status_ripple") update_time_of_death() + if(iscarbon(owner)) + var/mob/living/carbon/carbon_owner = owner + carbon_owner.update_bodypart_bleed_overlays() return ..() /atom/movable/screen/alert/status_effect/stasis diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm index 441fcbf5ee2..0b39d88cc98 100644 --- a/code/datums/wounds/_wounds.dm +++ b/code/datums/wounds/_wounds.dm @@ -146,7 +146,7 @@ if(severity == WOUND_SEVERITY_TRIVIAL) return - if(!(silent || demoted)) + if(!silent && !demoted) var/msg = span_danger("[victim]'s [limb.name] [occur_text]!") var/vis_dist = COMBAT_MESSAGE_RANGE @@ -158,8 +158,8 @@ if(sound_effect) playsound(L.owner, sound_effect, 70 + 20 * severity, TRUE) + wound_injury(old_wound, attack_direction = attack_direction) if(!demoted) - wound_injury(old_wound, attack_direction = attack_direction) second_wind() /datum/wound/proc/null_victim() diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm index c6a750cca06..045f20b123c 100644 --- a/code/datums/wounds/slash.dm +++ b/code/datums/wounds/slash.dm @@ -18,7 +18,7 @@ var/initial_flow /// When we have less than this amount of flow, either from treatment or clotting, we demote to a lower cut or are healed of the wound var/minimum_flow - /// How much our blood_flow will naturally decrease per tick, not only do larger cuts bleed more blood faster, they clot slower (higher number = clot quicker, negative = opening up) + /// How much our blood_flow will naturally decrease per second, not only do larger cuts bleed more blood faster, they clot slower (higher number = clot quicker, negative = opening up) var/clot_rate /// Once the blood flow drops below minimum_flow, we demote it to this type of wound. If there's none, we're all better @@ -31,14 +31,15 @@ var/datum/scar/highest_scar /datum/wound/slash/wound_injury(datum/wound/slash/old_wound = null, attack_direction = null) - blood_flow = initial_flow if(old_wound) blood_flow = max(old_wound.blood_flow, initial_flow) if(old_wound.severity > severity && old_wound.highest_scar) set_highest_scar(old_wound.highest_scar) old_wound.clear_highest_scar() - else if(attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY) - victim.spray_blood(attack_direction, severity) + else + blood_flow = initial_flow + if(attack_direction && victim.blood_volume > BLOOD_VOLUME_OKAY) + victim.spray_blood(attack_direction, severity) if(!highest_scar) var/datum/scar/new_scar = new @@ -83,7 +84,7 @@ /datum/wound/slash/receive_damage(wounding_type, wounding_dmg, wound_bonus) if(victim.stat != DEAD && wound_bonus != CANT_WOUND && wounding_type == WOUND_SLASH) // can't stab dead bodies to make it bleed faster this way - blood_flow += 0.05 * wounding_dmg + blood_flow += WOUND_SLASH_DAMAGE_FLOW_COEFF * wounding_dmg /datum/wound/slash/drag_bleed_amount() // say we have 3 severe cuts with 3 blood flow each, pretty reasonable @@ -252,6 +253,7 @@ if(!do_after(user, base_treat_time * self_penalty_mult, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) return + user.visible_message(span_green("[user] stitches up some of the bleeding on [victim]."), span_green("You stitch up some of the bleeding on [user == victim ? "yourself" : "[victim]"].")) var/blood_sutured = I.stop_bleeding / self_penalty_mult blood_flow -= blood_sutured @@ -274,7 +276,7 @@ severity = WOUND_SEVERITY_MODERATE initial_flow = 2 minimum_flow = 0.5 - clot_rate = 0.06 + clot_rate = 0.05 threshold_minimum = 20 threshold_penalty = 10 status_effect_type = /datum/status_effect/wound/slash/moderate @@ -305,9 +307,9 @@ occur_text = "is torn open, spraying blood wildly" sound_effect = 'sound/effects/wounds/blood3.ogg' severity = WOUND_SEVERITY_CRITICAL - initial_flow = 4.25 - minimum_flow = 4 - clot_rate = -0.025 // critical cuts actively get worse instead of better + initial_flow = 4 + minimum_flow = 3.85 + clot_rate = -0.015 // critical cuts actively get worse instead of better threshold_minimum = 80 threshold_penalty = 40 demotes_to = /datum/wound/slash/severe diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 406b8dd5269..06fe6189032 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -10,65 +10,83 @@ if(NOBLOOD in dna.species.species_traits || HAS_TRAIT(src, TRAIT_NOBLEED) || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) return - if(bodytemperature >= TCRYO && !(HAS_TRAIT(src, TRAIT_HUSK))) //cryosleep or husked people do not pump the blood. + if(bodytemperature < TCRYO || (HAS_TRAIT(src, TRAIT_HUSK))) //cryosleep or husked people do not pump the blood. + return - //Blood regeneration if there is some space - if(blood_volume < BLOOD_VOLUME_NORMAL && !HAS_TRAIT(src, TRAIT_NOHUNGER)) - var/nutrition_ratio = 0 - switch(nutrition) - if(0 to NUTRITION_LEVEL_STARVING) - nutrition_ratio = 0.2 - if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) - nutrition_ratio = 0.4 - if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) - nutrition_ratio = 0.6 - if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) - nutrition_ratio = 0.8 - else - nutrition_ratio = 1 - if(satiety > 80) - nutrition_ratio *= 1.25 - adjust_nutrition(-nutrition_ratio * HUNGER_FACTOR * delta_time) - blood_volume = min(blood_volume + (BLOOD_REGEN_FACTOR * nutrition_ratio * delta_time), BLOOD_VOLUME_NORMAL) + //Blood regeneration if there is some space + if(blood_volume < BLOOD_VOLUME_NORMAL && !HAS_TRAIT(src, TRAIT_NOHUNGER)) + var/nutrition_ratio = 0 + switch(nutrition) + if(0 to NUTRITION_LEVEL_STARVING) + nutrition_ratio = 0.2 + if(NUTRITION_LEVEL_STARVING to NUTRITION_LEVEL_HUNGRY) + nutrition_ratio = 0.4 + if(NUTRITION_LEVEL_HUNGRY to NUTRITION_LEVEL_FED) + nutrition_ratio = 0.6 + if(NUTRITION_LEVEL_FED to NUTRITION_LEVEL_WELL_FED) + nutrition_ratio = 0.8 + else + nutrition_ratio = 1 + if(satiety > 80) + nutrition_ratio *= 1.25 + adjust_nutrition(-nutrition_ratio * HUNGER_FACTOR * delta_time) + blood_volume = min(blood_volume + (BLOOD_REGEN_FACTOR * nutrition_ratio * delta_time), BLOOD_VOLUME_NORMAL) - //Effects of bloodloss - var/word = pick("dizzy","woozy","faint") - switch(blood_volume) - if(BLOOD_VOLUME_EXCESS to BLOOD_VOLUME_MAX_LETHAL) - if(DT_PROB(7.5, delta_time)) - to_chat(src, span_userdanger("Blood starts to tear your skin apart. You're going to burst!")) - inflate_gib() - if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS) - if(DT_PROB(5, delta_time)) - to_chat(src, span_warning("You feel terribly bloated.")) - if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) - if(DT_PROB(2.5, delta_time)) - to_chat(src, span_warning("You feel [word].")) - adjustOxyLoss(round(0.005 * (BLOOD_VOLUME_NORMAL - blood_volume) * delta_time, 1)) - if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) - adjustOxyLoss(round(0.01 * (BLOOD_VOLUME_NORMAL - blood_volume) * delta_time, 1)) - if(DT_PROB(2.5, delta_time)) - blur_eyes(6) - to_chat(src, span_warning("You feel very [word].")) - if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) - adjustOxyLoss(2.5 * delta_time) - if(DT_PROB(7.5, delta_time)) - Unconscious(rand(20,60)) - to_chat(src, span_warning("You feel extremely [word].")) - if(-INFINITY to BLOOD_VOLUME_SURVIVE) - if(!HAS_TRAIT(src, TRAIT_NODEATH)) - death() + //Effects of bloodloss + var/word = pick("dizzy","woozy","faint") + switch(blood_volume) + if(BLOOD_VOLUME_EXCESS to BLOOD_VOLUME_MAX_LETHAL) + if(DT_PROB(7.5, delta_time)) + to_chat(src, span_userdanger("Blood starts to tear your skin apart. You're going to burst!")) + inflate_gib() + if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS) + if(DT_PROB(5, delta_time)) + to_chat(src, span_warning("You feel terribly bloated.")) + if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE) + if(DT_PROB(2.5, delta_time)) + to_chat(src, span_warning("You feel [word].")) + adjustOxyLoss(round(0.005 * (BLOOD_VOLUME_NORMAL - blood_volume) * delta_time, 1)) + if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY) + adjustOxyLoss(round(0.01 * (BLOOD_VOLUME_NORMAL - blood_volume) * delta_time, 1)) + if(DT_PROB(2.5, delta_time)) + blur_eyes(6) + to_chat(src, span_warning("You feel very [word].")) + if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD) + adjustOxyLoss(2.5 * delta_time) + if(DT_PROB(7.5, delta_time)) + Unconscious(rand(20,60)) + to_chat(src, span_warning("You feel extremely [word].")) + if(-INFINITY to BLOOD_VOLUME_SURVIVE) + if(!HAS_TRAIT(src, TRAIT_NODEATH)) + death() - var/temp_bleed = 0 - //Bleeding out - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - temp_bleed += BP.get_bleed_rate() * delta_time - BP.generic_bleedstacks = max(0, BP.generic_bleedstacks - 1) + var/temp_bleed = 0 + var/update_bleed_icons = FALSE + //Bleeding out + for(var/obj/item/bodypart/iter_part as anything in bodyparts) + var/iter_bleed_rate = iter_part.get_part_bleed_rate() + temp_bleed += iter_bleed_rate * delta_time + iter_part.generic_bleedstacks = max(0, iter_part.generic_bleedstacks - 1) + if(iter_part.update_part_wound_overlay()) + update_bleed_icons = TRUE - if(temp_bleed) - bleed(temp_bleed) - bleed_warn(temp_bleed) + if(update_bleed_icons) + update_wound_overlays() + + if(temp_bleed) + bleed(temp_bleed) + bleed_warn(temp_bleed) + +/// Has each bodypart update its bleed/wound overlay icon states. If any have changed, it has the owner update wound overlays and returns TRUE +/mob/living/carbon/proc/update_bodypart_bleed_overlays() + var/update_bleed_icons + for(var/obj/item/bodypart/iter_part as anything in bodyparts) + if(iter_part.update_part_wound_overlay()) + update_bleed_icons = TRUE + + if(update_bleed_icons) + update_wound_overlays() + return update_bleed_icons //Makes a blood drop, leaking amt units of blood from the mob /mob/living/carbon/proc/bleed(amt) @@ -92,7 +110,7 @@ var/bleed_amt = 0 for(var/X in bodyparts) var/obj/item/bodypart/iter_bodypart = X - bleed_amt += iter_bodypart.get_bleed_rate() + bleed_amt += iter_bodypart.get_part_bleed_rate() return bleed_amt /mob/living/carbon/human/get_bleed_rate() diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index f1fd5be4f94..c13b54af5e3 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1216,7 +1216,7 @@ /mob/living/carbon/proc/is_bleeding() for(var/i in bodyparts) var/obj/item/bodypart/BP = i - if(BP.get_bleed_rate()) + if(BP.get_part_bleed_rate()) return TRUE /// get our total bleedrate @@ -1224,7 +1224,7 @@ var/total_bleed_rate = 0 for(var/i in bodyparts) var/obj/item/bodypart/BP = i - total_bleed_rate += BP.get_bleed_rate() + total_bleed_rate += BP.get_part_bleed_rate() return total_bleed_rate diff --git a/code/modules/mob/living/carbon/carbon_context.dm b/code/modules/mob/living/carbon/carbon_context.dm index 826784c7789..eb30dbcaf62 100644 --- a/code/modules/mob/living/carbon/carbon_context.dm +++ b/code/modules/mob/living/carbon/carbon_context.dm @@ -15,7 +15,7 @@ else if (human_user == src) context[SCREENTIP_CONTEXT_LMB] = "Check injuries" - if (get_bodypart(human_user.zone_selected)?.get_bleed_rate()) + if (get_bodypart(human_user.zone_selected)?.get_part_bleed_rate()) context[SCREENTIP_CONTEXT_CTRL_LMB] = "Grab limb" if (human_user != src) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 551b0441a73..f4962699cc3 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -651,7 +651,7 @@ return ..() var/obj/item/bodypart/grasped_part = get_bodypart(zone_selected) - if(!grasped_part?.get_bleed_rate()) + if(!grasped_part?.get_part_bleed_rate()) return var/starting_hand_index = active_hand_index if(starting_hand_index == grasped_part.held_index) @@ -659,7 +659,7 @@ return to_chat(src, span_warning("You try grasping at your [grasped_part.name], trying to stop the bleeding...")) - if(!do_after(src, 1.5 SECONDS)) + if(!do_after(src, 0.75 SECONDS)) to_chat(src, span_danger("You fail to grasp your [grasped_part.name].")) return @@ -676,7 +676,7 @@ desc = "Sometimes all you can do is slow the bleeding." icon_state = "latexballon" inhand_icon_state = "nothing" - slowdown = 1 + slowdown = 0.5 item_flags = DROPDEL | ABSTRACT | NOBLUDGEON | SLOWS_WHILE_IN_HAND | HAND_ITEM /// The bodypart we're staunching bleeding on, which also has a reference to us in [/obj/item/bodypart/var/grasped_by] var/obj/item/bodypart/grasped_part diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 6e8dd843080..4bd940c9e9f 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -92,16 +92,26 @@ var/mutable_appearance/damage_overlay = mutable_appearance('icons/mob/dam_mob.dmi', "blank", -DAMAGE_LAYER) overlays_standing[DAMAGE_LAYER] = damage_overlay - for(var/X in bodyparts) - var/obj/item/bodypart/BP = X - if(BP.dmg_overlay_type) - if(BP.brutestate) - damage_overlay.add_overlay("[BP.dmg_overlay_type]_[BP.body_zone]_[BP.brutestate]0") //we're adding icon_states of the base image as overlays - if(BP.burnstate) - damage_overlay.add_overlay("[BP.dmg_overlay_type]_[BP.body_zone]_0[BP.burnstate]") + for(var/obj/item/bodypart/iter_part as anything in bodyparts) + if(iter_part.dmg_overlay_type) + if(iter_part.brutestate) + damage_overlay.add_overlay("[iter_part.dmg_overlay_type]_[iter_part.body_zone]_[iter_part.brutestate]0") //we're adding icon_states of the base image as overlays + if(iter_part.burnstate) + damage_overlay.add_overlay("[iter_part.dmg_overlay_type]_[iter_part.body_zone]_0[iter_part.burnstate]") apply_overlay(DAMAGE_LAYER) +/mob/living/carbon/update_wound_overlays() + remove_overlay(WOUND_LAYER) + + var/mutable_appearance/wound_overlay = mutable_appearance('icons/mob/bleed_overlays.dmi', "blank", -WOUND_LAYER) + overlays_standing[WOUND_LAYER] = wound_overlay + + for(var/obj/item/bodypart/iter_part as anything in bodyparts) + if(iter_part.bleed_overlay_icon) + wound_overlay.add_overlay(iter_part.bleed_overlay_icon) + + apply_overlay(WOUND_LAYER) /mob/living/carbon/update_inv_wear_mask() remove_overlay(FACEMASK_LAYER) @@ -254,6 +264,7 @@ apply_overlay(BODYPARTS_LAYER) update_damage_overlays() + update_wound_overlays() diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 2eac1502ec4..d2143b6f4e1 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -256,7 +256,7 @@ for(var/i in bodyparts) var/obj/item/bodypart/body_part = i - if(body_part.get_bleed_rate()) + if(body_part.get_part_bleed_rate()) bleeding_limbs += body_part if(body_part.grasped_by) grasped_limbs += body_part diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index b469a56b113..e6ab4dd5796 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -809,7 +809,7 @@ var/list/obj/item/bodypart/bleeding_limbs = list() for(var/i in bodyparts) var/obj/item/bodypart/BP = i - if(BP.get_bleed_rate()) + if(BP.get_part_bleed_rate()) bleeding_limbs += BP var/num_bleeds = LAZYLEN(bleeding_limbs) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index bd9dd088918..8e5578c80b2 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -859,6 +859,10 @@ /mob/living/proc/update_damage_overlays() return +/// Proc that only really gets called for humans, to handle bleeding overlays. +/mob/living/proc/update_wound_overlays() + return + /mob/living/Move(atom/newloc, direct, glide_size_override) if(lying_angle != 0) lying_angle_on_movement(direct) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 951847922ed..71f39a0b81d 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -76,6 +76,8 @@ var/species_flags_list = list() ///the type of damage overlay (if any) to use when this bodypart is bruised/burned. var/dmg_overlay_type + /// If we're bleeding, which icon are we displaying on this part + var/bleed_overlay_icon //Damage messages used by help_shake_act() var/light_brute_msg = "bruised" @@ -969,8 +971,13 @@ wound_damage_multiplier = dam_mul - -/obj/item/bodypart/proc/get_bleed_rate() +/** + * Calculates how much blood this limb is losing per life tick + * + * Arguments: + * * ignore_modifiers - If TRUE, ignore the bleed reduction for laying down and grabbing your limb + */ +/obj/item/bodypart/proc/get_part_bleed_rate(ignore_modifiers = FALSE) if(HAS_TRAIT(owner, TRAIT_NOBLEED)) return if(status != BODYPART_ORGANIC) // maybe in the future we can bleed oil from aug parts, but not now @@ -986,20 +993,61 @@ if(!embeddies.isEmbedHarmless()) bleed_rate += 0.25 - for(var/datum/wound/wound as anything in wounds) - bleed_rate += wound.blood_flow + for(var/datum/wound/iter_wound as anything in wounds) + bleed_rate += iter_wound.blood_flow - if(owner.body_position == LYING_DOWN) - bleed_rate *= 0.75 - - if(grasped_by) - bleed_rate *= 0.7 + if(!ignore_modifiers) + if(owner.body_position == LYING_DOWN) + bleed_rate *= 0.75 + if(grasped_by) + bleed_rate *= 0.7 if(!bleed_rate) QDEL_NULL(grasped_by) return bleed_rate +// how much blood the limb needs to be losing per tick (not counting laying down/self grasping modifiers) to get the different bleed icons +#define BLEED_OVERLAY_LOW 0.5 +#define BLEED_OVERLAY_MED 1.5 +#define BLEED_OVERLAY_GUSH 3.25 + +/obj/item/bodypart/proc/update_part_wound_overlay() + if(!owner) + return FALSE + if(HAS_TRAIT(owner, TRAIT_NOBLEED) || status != BODYPART_ORGANIC) + if(bleed_overlay_icon) + bleed_overlay_icon = null + owner.update_wound_overlays() + return FALSE + + var/bleed_rate = get_part_bleed_rate(ignore_modifiers = TRUE) + var/new_bleed_icon + + switch(bleed_rate) + if(-INFINITY to BLEED_OVERLAY_LOW) + new_bleed_icon = null + if(BLEED_OVERLAY_LOW to BLEED_OVERLAY_MED) + new_bleed_icon = "[body_zone]_1" + if(BLEED_OVERLAY_MED to BLEED_OVERLAY_GUSH) + if(owner.body_position == LYING_DOWN || IS_IN_STASIS(owner) || owner.stat == DEAD) + new_bleed_icon = "[body_zone]_2s" + else + new_bleed_icon = "[body_zone]_2" + if(BLEED_OVERLAY_GUSH to INFINITY) + if(IS_IN_STASIS(owner) || owner.stat == DEAD) + new_bleed_icon = "[body_zone]_2s" + else + new_bleed_icon = "[body_zone]_3" + + if(new_bleed_icon != bleed_overlay_icon) + bleed_overlay_icon = new_bleed_icon + return TRUE + +#undef BLEED_OVERLAY_LOW +#undef BLEED_OVERLAY_MED +#undef BLEED_OVERLAY_GUSH + /** * apply_gauze() is used to- well, apply gauze to a bodypart * diff --git a/icons/mob/bleed_overlays.dmi b/icons/mob/bleed_overlays.dmi new file mode 100644 index 0000000000000000000000000000000000000000..aa82d5578f8a55fe2b835ddbaad1e5af106476ed GIT binary patch literal 1742 zcmYjQ4Ls9n9RGI+m0o7L6*F&hmSWeHmz<_q3(L!GGNep6FT>)NytQ1nu<{ZnFRhfk z-3Rlw+N2_RiQEWn%Uc(jBdu_bF&w?n@AJI;p5OERy*|H(;pA{kWz+6W005}i+Fgi+h|}sPS2l>#ap$A;_4UgL%~n8lPC?;?np)4sjZ?7AD?;r-7tso40~PN; z0IbBKUsn!;QO>SZjPGyWVF72U0hAB`h+vg5k8Ezx0=IO`BYbMHoHBBzC;{3_gru1$ z5I-{WD6iY0&HghwlT-BE6OVjVQX$-`mr4A;BC-S}aq(R1cF*Pe{Xt8`yT(!>dzBoEJks z6IuCB>~j3m#Gb9p6ZX>i#9Zy+u(&g-xG?}wh_b~TagJcUAHX>b+du#k%c@-N2LrKu zYUhO8zZ-Ot3We3#3!Yv*+qLe$^V$|ZR8>bwWw%fWleyp|=kS4n4bDa8oOdbQT^nIJ zg{M$dP*1==afB)|(Rwmgq9)PLAJBrGsttQ))>h$A)ae7;4Lz4Y9Iy;g6-AbiD z6VmAd&hgl55}rF_yfw44bd0i#B}={|lRj-nNdzIwN$H&#sZL+fv1QRlJ?FWek6hCw z)sxYEJD4Vhkn@-F&deW@(srNwZxhwn>cb_DQN4^(=Qg0m_J~x>6r2$u`>Z1Ta5oFa z-v8c{GPkE~PF$dMX%&x;Q;Xo~jlqm_4=4`)w{1z@iAVUDTe ztr-4v26>m%u=t{t!7Y+_paM>Hevmy(0x;Hf`DVN#tW&q&tboJvd zSagw^2`V;2nXCB~Tc1bG&GLwNJN3kS>CV;= zvzsyfcP|UfFD6d3H&+`f(9tpJ7lH!nOks|yQ;W6y8N;Ua7=R$Y_cI}CLUVdCCJ*Yh%Y6KCX7K;fj8vyYZnuJc>~*{y5VsB-2>u55#y({qA@q=`u9z*le9sYF7U? zSlD_)0>DzaQ(*roUkK~{)v}g%Tj=K}JC=RPyf!nlXvTd>N~MmRo{_LpWBa`Pfw`{ij`wXGK(lpPQ+idMx>=f9(1RZkeZoQ1Zi`uebEw@q=uoDYQGm-;PU4*yE!V>W<&sZM$13}PWqqMGW)!1{j4AvH0 z6zb>0{PTlm>bKt$07Uv^1`&%!Bup4|vwQ3Ejn?~c-Np)6PgxvwnLRoDqhZ)_m$o-g zqf$n$Rmb~}=4QJuB-9Q2Sr(8dbwkES@*W!N=WB%eW@efmGB0up^fg*Bp!VN`!7ErVmRK z>Fo9)P?X(@=e@{pDn4ELFV$CXZGhJ_T_mr$!uX3-12sxUmKeJi66Z#+gA{(Vs8(g) zyllp8en)giosr=SrNVm&6eYrZ=w!|W|0uGudldJ;LVf-1S2LfHDK;XJUXF-_Usmng M9(BO5(WFcN1G$0Wk^lez literal 0 HcmV?d00001