diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index f90722e9b1a..cefc4c0d5d2 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -13,14 +13,15 @@ #define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) //Human Overlays Indexes///////// -#define MUTATIONS_LAYER 28 //mutations. Tk headglows, cold resistance glow, etc -#define BODY_BEHIND_LAYER 27 //certain mutantrace features (tail when looking south) that must appear behind the body parts -#define BODYPARTS_LAYER 26 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag -#define BODY_ADJ_LAYER 25 //certain mutantrace features (snout, body markings) that must appear above the body parts -#define BODY_LAYER 24 //underwear, undershirts, socks, eyes, lips(makeup) -#define FRONT_MUTATIONS_LAYER 23 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) -#define DAMAGE_LAYER 22 //damage indicators (cuts and burns) -#define UNIFORM_LAYER 21 +#define MUTATIONS_LAYER 29 //mutations. Tk headglows, cold resistance glow, etc +#define BODY_BEHIND_LAYER 28 //certain mutantrace features (tail when looking south) that must appear behind the body parts +#define BODYPARTS_LAYER 27 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag +#define BODY_ADJ_LAYER 26 //certain mutantrace features (snout, body markings) that must appear above the body parts +#define BODY_LAYER 25 //underwear, undershirts, socks, eyes, lips(makeup) +#define FRONT_MUTATIONS_LAYER 24 //mutations that should appear above body, body_adj and bodyparts layer (e.g. laser eyes) +#define DAMAGE_LAYER 23 //damage indicators (cuts and burns) +#define UNIFORM_LAYER 22 +#define BANDAGE_LAYER 21 //Overlays related to wounds, bandages and splints too //SKYRAT EDIT ADDITION - MEDICAL #define ID_LAYER 20 //lmao at the idiot who put both ids and hands on the same layer #define HANDS_PART_LAYER 19 #define GLOVES_LAYER 18 @@ -41,7 +42,7 @@ #define BODY_FRONT_LAYER 3 #define HALO_LAYER 2 //blood cult ascended halo, because there's currently no better solution for adding/removing #define FIRE_LAYER 1 //If you're on fire -#define TOTAL_LAYERS 28 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; +#define TOTAL_LAYERS 29 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; //SKYRAT EDIT CHANGE - 29 from 28. Added BANDAGE_LAYER //Human Overlay Index Shortcuts for alternate_worn_layer, layers //Because I *KNOW* somebody will think layer+1 means "above" diff --git a/code/__DEFINES/wounds.dm b/code/__DEFINES/wounds.dm index 92a1d60ece9..7059eeb0d16 100644 --- a/code/__DEFINES/wounds.dm +++ b/code/__DEFINES/wounds.dm @@ -32,6 +32,10 @@ #define WOUND_PIERCE 3 /// any concentrated burn attack (lasers really). rolls for burning wounds #define WOUND_BURN 4 +//SKYRAT EDIT ADDITION BEGIN - MEDICAL +/// any brute attacks, rolled on a chance +#define WOUND_MUSCLE 5 +//SKYRAT EDIT ADDITION END // ~determination second wind defines @@ -49,14 +53,15 @@ GLOBAL_LIST_INIT(global_wound_types, list(WOUND_BLUNT = list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate), WOUND_SLASH = list(/datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate), WOUND_PIERCE = list(/datum/wound/pierce/critical, /datum/wound/pierce/severe, /datum/wound/pierce/moderate), - WOUND_BURN = list(/datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate) + WOUND_BURN = list(/datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate), + WOUND_MUSCLE = list(/datum/wound/muscle/severe, /datum/wound/muscle/moderate) )) // every single type of wound that can be rolled naturally, in case you need to pull a random one GLOBAL_LIST_INIT(global_all_wound_types, list(/datum/wound/blunt/critical, /datum/wound/blunt/severe, /datum/wound/blunt/moderate, /datum/wound/slash/critical, /datum/wound/slash/severe, /datum/wound/slash/moderate, /datum/wound/pierce/critical, /datum/wound/pierce/severe, /datum/wound/pierce/moderate, - /datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate)) + /datum/wound/burn/critical, /datum/wound/burn/severe, /datum/wound/burn/moderate, /datum/wound/muscle/severe, /datum/wound/muscle/moderate)) // ~burn wound infection defines @@ -118,6 +123,10 @@ GLOBAL_LIST_INIT(global_all_wound_types, list(/datum/wound/blunt/critical, /datu #define MANGLES_BONE (1<<3) /// If this wound marks the limb as being allowed to have gauze applied #define ACCEPTS_GAUZE (1<<4) +//SKYRAT EDIT ADDITION BEGIN - MEDICAL +/// If this wound marks the limb as being allowed to have splints applied +#define ACCEPTS_SPLINT (1<<5) +//SKYRAT EDIT ADDITION END // ~scar persistence defines diff --git a/code/__DEFINES/~skyrat_defines/medical_defines.dm b/code/__DEFINES/~skyrat_defines/medical_defines.dm new file mode 100644 index 00000000000..34fe88709c7 --- /dev/null +++ b/code/__DEFINES/~skyrat_defines/medical_defines.dm @@ -0,0 +1,5 @@ +#define GAUZE_STAIN_BLOOD 1 +#define GAUZE_STAIN_PUS 2 + +#define COMSIG_BODYPART_SPLINTED "bodypart_splinted" // from /obj/item/bodypart/proc/apply_gauze(/obj/item/stack/gauze) +#define COMSIG_BODYPART_SPLINT_DESTROYED "bodypart_desplinted" // from [/obj/item/bodypart/proc/seep_gauze] when it runs out of absorption diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm index 9090aac93d0..0ad69355c39 100644 --- a/code/datums/wounds/bones.dm +++ b/code/datums/wounds/bones.dm @@ -1,3 +1,5 @@ +//SKYRAT EDIT REMOVAL - MOVED - MEDICINE +/* /* Blunt/Bone wounds @@ -446,3 +448,4 @@ else if(limb.body_zone == BODY_ZONE_CHEST && victim.blood_volume) . += "Ribcage Trauma Detected: Further trauma to chest is likely to worsen internal bleeding until bone is repaired." . += "" +*/ diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm index 9525ef0bd0e..cd0ed2f5417 100644 --- a/code/datums/wounds/burns.dm +++ b/code/datums/wounds/burns.dm @@ -1,3 +1,5 @@ +//SKYRAT EDIT REMOVAL - MOVED - MEDICINE +/* /* Burn wounds @@ -301,3 +303,4 @@ infestation_rate = 0.15 // appx 4.33 minutes to reach sepsis without any treatment flesh_damage = 20 scar_keyword = "burncritical" +*/ diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm index 1142fd2c165..65cc08dc21c 100644 --- a/code/datums/wounds/pierce.dm +++ b/code/datums/wounds/pierce.dm @@ -1,3 +1,5 @@ +//SKYRAT EDIT REMOVAL - MOVED - MEDICINE +/* /* Piercing wounds @@ -177,3 +179,4 @@ status_effect_type = /datum/status_effect/wound/pierce/critical scar_keyword = "piercecritical" wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH) +*/ diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm index 0f091cb95ec..abe454e351e 100644 --- a/code/datums/wounds/slash.dm +++ b/code/datums/wounds/slash.dm @@ -1,3 +1,5 @@ +//SKYRAT EDIT REMOVAL - MOVED - MEDICINE +/* /* Slashing wounds @@ -297,3 +299,4 @@ status_effect_type = /datum/status_effect/wound/slash/critical scar_keyword = "slashcritical" wound_flags = (FLESH_WOUND | ACCEPTS_GAUZE | MANGLES_FLESH) +*/ diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 89dd43a97e4..80302362cd6 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -119,7 +119,8 @@ /obj/item/stack/medical/gauze name = "medical gauze" - desc = "A roll of elastic cloth, perfect for stabilizing all kinds of wounds, from cuts and burns, to broken bones. " + //desc = "A roll of elastic cloth, perfect for stabilizing all kinds of wounds, from cuts and burns, to broken bones. " //ORIGINAL + desc = "A roll of elastic cloth, perfect for stabilizing all kinds of slashes, punctures and burns. " //SKYRAT EDIT CHANGE - MEDICAL gender = PLURAL singular_name = "medical gauze" icon_state = "gauze" @@ -133,6 +134,7 @@ absorption_capacity = 5 splint_factor = 0.35 merge_type = /obj/item/stack/medical/gauze + var/gauze_type = /datum/bodypart_aid/gauze //SKYRAT EDIT ADDITION - MEDICAL // gauze is only relevant for wounds, which are handled in the wounds themselves /obj/item/stack/medical/gauze/try_heal(mob/living/M, mob/user, silent) @@ -154,9 +156,16 @@ to_chat(user, "There's no wounds that require bandaging on [user==M ? "your" : "[M]'s"] [limb.name]!") // good problem to have imo return + //SKYRAT EDIT CHANGE BEGIN - MEDICAL + /* if(limb.current_gauze && (limb.current_gauze.absorption_capacity * 0.8 > absorption_capacity)) // ignore if our new wrap is < 20% better than the current one, so someone doesn't bandage it 5 times in a row to_chat(user, "The bandage currently on [user==M ? "your" : "[M]'s"] [limb.name] is still in good condition!") return + */ + if(limb.current_gauze) + to_chat(user, "[user==M ? "Your" : "[M]'s"] [limb.name] is already bandaged!") + return + //SKYRAT EDIT CHANGE END user.visible_message("[user] begins wrapping the wounds on [M]'s [limb.name] with [src]...", "You begin wrapping the wounds on [user == M ? "your" : "[M]'s"] [limb.name] with [src]...") if(!do_after(user, (user == M ? self_delay : other_delay), target=M)) @@ -194,6 +203,7 @@ absorption_rate = 0.15 absorption_capacity = 4 merge_type = /obj/item/stack/medical/gauze/improvised + gauze_type = /datum/bodypart_aid/gauze/improvised //SKYRAT EDIT ADDITION - MEDICAL /* The idea is for the following medical devices to work like a hybrid of the old brute packs and tend wounds, diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm index 8452e545e15..cfe55aca46a 100644 --- a/code/game/objects/items/storage/firstaid.dm +++ b/code/game/objects/items/storage/firstaid.dm @@ -32,6 +32,7 @@ return var/static/items_inside = list( /obj/item/stack/medical/gauze = 1, + /obj/item/stack/medical/splint = 1, //SKYRAT EDIT ADDITION - MEDICAL /obj/item/stack/medical/suture = 2, /obj/item/stack/medical/mesh = 2, /obj/item/reagent_containers/hypospray/medipen = 1) @@ -48,6 +49,7 @@ var/static/items_inside = list( /obj/item/healthanalyzer/wound = 1, /obj/item/stack/medical/gauze = 1, + /obj/item/stack/medical/splint = 1, //SKYRAT EDIT ADDITION - MEDICAL /obj/item/stack/medical/suture/emergency = 1, /obj/item/stack/medical/ointment = 1, /obj/item/reagent_containers/hypospray/medipen/ekit = 2, @@ -124,6 +126,7 @@ var/static/items_inside = list( /obj/item/healthanalyzer = 1, /obj/item/stack/medical/gauze/twelve = 1, + /obj/item/stack/medical/splint/twelve = 1, //SKYRAT EDIT ADDITION - MEDICAL /obj/item/stack/medical/suture = 2, /obj/item/stack/medical/mesh = 2, /obj/item/reagent_containers/hypospray/medipen = 1, @@ -142,6 +145,7 @@ return var/static/items_inside = list( /obj/item/stack/medical/gauze = 1, + /obj/item/stack/medical/splint = 1, //SKYRAT EDIT ADDITION - MEDICAL /obj/item/stack/medical/bruise_pack = 3, /obj/item/stack/medical/ointment= 3) generate_items_inside(items_inside,src) @@ -246,6 +250,7 @@ var/static/items_inside = list( /obj/item/reagent_containers/pill/patch/libital = 3, /obj/item/stack/medical/gauze = 1, + /obj/item/stack/medical/splint = 1, //SKYRAT EDIT ADDITION - MEDICAL /obj/item/storage/pill_bottle/probital = 1, /obj/item/reagent_containers/hypospray/medipen/salacid = 1) generate_items_inside(items_inside,src) @@ -265,6 +270,7 @@ /obj/item/reagent_containers/pill/patch/synthflesh = 3, /obj/item/reagent_containers/hypospray/medipen/atropine = 2, /obj/item/stack/medical/gauze = 1, + /obj/item/stack/medical/splint = 1, //SKYRAT EDIT ADDITION - MEDICAL /obj/item/storage/pill_bottle/penacid = 1) generate_items_inside(items_inside,src) @@ -283,6 +289,7 @@ if(empty) return new /obj/item/stack/medical/gauze(src) + new /obj/item/stack/medical/splint //SKYRAT EDIT ADDITION - MEDICAL new /obj/item/defibrillator/compact/combat/loaded(src) new /obj/item/reagent_containers/hypospray/combat(src) new /obj/item/reagent_containers/pill/patch/libital(src) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 0fad7fbdd0d..37e9e6221a2 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -700,6 +700,8 @@ return ..() var/obj/item/bodypart/grasped_part = get_bodypart(zone_selected) + //SKYRAT EDIT CHANGE BEGIN - MEDICAL + /* if(!grasped_part?.get_bleed_rate()) return var/starting_hand_index = active_hand_index @@ -718,6 +720,9 @@ QDEL_NULL(grasp) return grasp.grasp_limb(grasped_part) + */ + self_grasp_bleeding_limb(grasped_part, supress_message) + //SKYRAT EDIT CHANGE END /// an abstract item representing you holding your own limb to staunch the bleeding, see [/mob/living/carbon/proc/grabbedby] will probably need to find somewhere else to put this. /obj/item/self_grasp diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index d00cba54e68..974c020c2a1 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -154,6 +154,8 @@ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) +//SKYRAT EDIT REMOVAL - MOVED - MEDICAL +/* /mob/living/carbon/examine_more(mob/user) if(!all_scars) return ..() @@ -175,3 +177,4 @@ msg += "[scar_text]" return msg +*/ diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 6f4db839c0f..d8eaecfb01d 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -741,13 +741,17 @@ var/msg switch(W.severity) if(WOUND_SEVERITY_TRIVIAL) - msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]." + //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]." //ORIGINAL + msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]." //SKYRAT EDIT CHANGE - MEDICAL if(WOUND_SEVERITY_MODERATE) - msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" + //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" //ORIGINAL + msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!" //SKYRAT EDIT CHANGE - MEDICAL if(WOUND_SEVERITY_SEVERE) - msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" + //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!" //ORIGINAL + msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!" //SKYRAT EDIT CHANGE - MEDICAL if(WOUND_SEVERITY_CRITICAL) - msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!!" + //msg = "\t Your [LB.name] is suffering [W.a_or_from] [lowertext(W.name)]!!" //ORIGINAL + msg = "\t Your [LB.name] is suffering [W.a_or_from] [W.get_topic_name(src)]!!" //SKYRAT EDIT CHANGE - MEDICAL combined_msg += msg for(var/obj/item/I in LB.embedded_objects) @@ -756,6 +760,15 @@ else combined_msg += "\t There is \a [I] embedded in your [LB.name]!" + //SKYRAT EDIT ADDITION BEGIN - MEDICAL + if(LB.current_gauze) + var/datum/bodypart_aid/current_gauze = LB.current_gauze + combined_msg += "\t Your [LB.name] is [current_gauze.desc_prefix] with [current_gauze.get_description()]." + if(LB.current_splint) + var/datum/bodypart_aid/current_splint = LB.current_splint + combined_msg += "\t Your [LB.name] is [current_splint.desc_prefix] with [current_splint.get_description()]." + //SKYRAT EDIT END + for(var/t in missing) combined_msg += "Your [parse_zone(t)] is missing!" diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index 28258cfb3a3..eb159984ea9 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -90,8 +90,16 @@ var/last_maxed /// How much generic bleedstacks we have on this bodypart var/generic_bleedstacks + //SKYRAT EDIT CHANGE BEGIN - MEDICAL + /* /// If we have a gauze wrapping currently applied (not including splints) var/obj/item/stack/current_gauze + */ + /// If we have a gauze wrapping currently applied + var/datum/bodypart_aid/gauze/current_gauze + /// If we have a splint currently applied + var/datum/bodypart_aid/splint/current_splint + //SKYRAT EDIT CHANGE END /// If something is currently grasping this bodypart and trying to staunch bleeding (see [/obj/item/self_grasp]) var/obj/item/self_grasp/grasped_by var/rendered_bp_icon //SKYRAT EDIT ADDITION - CUSTOMIZATION @@ -114,6 +122,12 @@ if(length(wounds)) stack_trace("[type] qdeleted with [length(wounds)] uncleared wounds") wounds.Cut() + //SKYRAT EDIT ADDITION BEGIN - MEDICAL + if(current_gauze) + qdel(current_gauze) + if(current_splint) + qdel(current_splint) + //SKYRAT EDIT ADDITION END return ..() @@ -181,7 +195,11 @@ var/turf/T = get_turf(src) if(status != BODYPART_ROBOTIC) playsound(T, 'sound/misc/splort.ogg', 50, TRUE, -1) - seep_gauze(9999) // destroy any existing gauze if any exists + //seep_gauze(9999) // destroy any existing gauze if any exists + if(current_gauze) + qdel(current_gauze) + if(current_splint) + qdel(current_splint) for(var/obj/item/organ/drop_organ in get_organs()) drop_organ.transfer_to_limb(src, owner) for(var/obj/item/I in src) @@ -268,6 +286,11 @@ // note that there's no handling for BIO_JUST_FLESH since we don't have any that are that right now (slimepeople maybe someday) // standard humanoids if(BIO_FLESH_BONE) + //SKYRAT EDIT ADDITION BEGIN - MEDICAL + //We do a body zone check here because muscles dont have any variants for head or chest, and rolling a muscle wound on them wound end up on a wasted wound roll + if(body_zone != BODY_ZONE_CHEST && body_zone != BODY_ZONE_HEAD && prob(35)) + wounding_type = WOUND_MUSCLE + //SKYRAT EDIT ADDITION END // if we've already mangled the skin (critical slash or piercing wound), then the bone is exposed, and we can damage it with sharp weapons at a reduced rate // So a big sharp weapon is still all you need to destroy a limb if(mangled_state == BODYPART_MANGLED_FLESH && sharpness) @@ -282,6 +305,12 @@ // now we have our wounding_type and are ready to carry on with wounds and dealing the actual damage if(owner && wounding_dmg >= WOUND_MINIMUM_DAMAGE && wound_bonus != CANT_WOUND) + //SKYRAT EDIT ADDITION - MEDICAL + if(current_gauze) + current_gauze.take_damage() + if(current_splint) + current_splint.take_damage() + //SKYRAT EDIT ADDITION - MEDICAL check_wounding(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus) for(var/i in wounds) @@ -907,9 +936,11 @@ var/datum/wound/iter_wound = i dam_mul *= iter_wound.damage_mulitplier_penalty + /* if(!LAZYLEN(wounds) && current_gauze && !replaced) // no more wounds = no need for the gauze anymore owner.visible_message("\The [current_gauze] on [owner]'s [name] fall away.", "The [current_gauze] on your [name] fall away.") QDEL_NULL(current_gauze) + */ wound_damage_multiplier = dam_mul @@ -954,6 +985,7 @@ * Arguments: * * gauze- Just the gauze stack we're taking a sheet from to apply here */ +/* /obj/item/bodypart/proc/apply_gauze(obj/item/stack/gauze) if(!istype(gauze) || !gauze.absorption_capacity) return @@ -965,6 +997,7 @@ gauze.use(1) if(newly_gauzed) SEND_SIGNAL(src, COMSIG_BODYPART_GAUZED, gauze) +*/ /** * seep_gauze() is for when a gauze wrapping absorbs blood or pus from wounds, lowering its absorption capacity. @@ -974,6 +1007,7 @@ * Arguments: * * seep_amt - How much absorption capacity we're removing from our current bandages (think, how much blood or pus are we soaking up this tick?) */ +/* /obj/item/bodypart/proc/seep_gauze(seep_amt = 0) if(!current_gauze) return @@ -982,3 +1016,4 @@ owner.visible_message("\The [current_gauze] on [owner]'s [name] fall away in rags.", "\The [current_gauze] on your [name] fall away in rags.", vision_distance=COMBAT_MESSAGE_RANGE) QDEL_NULL(current_gauze) SEND_SIGNAL(src, COMSIG_BODYPART_GAUZE_DESTROYED) +*/ diff --git a/modular_skyrat/modules/medical/code/bodypart.dm b/modular_skyrat/modules/medical/code/bodypart.dm new file mode 100644 index 00000000000..6b77decd5df --- /dev/null +++ b/modular_skyrat/modules/medical/code/bodypart.dm @@ -0,0 +1,11 @@ +/obj/item/bodypart/proc/apply_gauze(obj/item/stack/medical/gauze/new_gauze) + if(!istype(new_gauze) || current_gauze) + return + current_gauze = new new_gauze.gauze_type(src) + new_gauze.use(1) + +/obj/item/bodypart/proc/apply_splint(obj/item/stack/medical/splint/new_splint) + if(!istype(new_splint) || current_splint) + return + current_splint = new new_splint.splint_type(src) + new_splint.use(1) diff --git a/modular_skyrat/modules/medical/code/bodypart_aid.dm b/modular_skyrat/modules/medical/code/bodypart_aid.dm new file mode 100644 index 00000000000..c74874deab4 --- /dev/null +++ b/modular_skyrat/modules/medical/code/bodypart_aid.dm @@ -0,0 +1,231 @@ +#define SELF_AID_REMOVE_DELAY 5 SECONDS +#define OTHER_AID_REMOVE_DELAY 2 SECONDS + +/datum/bodypart_aid + var/name + /// Keeping track of how damaged our aid is from external things, such as hits, it will break when it reaches 0 + var/integrity = 2 + /// To which bodypart we're attached to + var/obj/item/bodypart/bodypart + /// Which item do we get when we rip this off, while its in pristine condition + var/stack_to_drop + /// Suffix for our used overlay. The suffix is the bodypart zone, and "_digitigrade" for some legs. If this is null then it wont make an overlay. Gauzes are rendered before splints + var/overlay_prefix + /// Bodypart is [this_prefix] with get_description() + var/desc_prefix + +/datum/bodypart_aid/Topic(href, href_list) + . = ..() + if(href_list["remove"]) + if(!bodypart.owner) + return + if(!iscarbon(usr)) + return + if(!in_range(usr, bodypart.owner)) + return + var/mob/living/carbon/C = usr + var/self = (C == bodypart.owner) + C.visible_message("[C] begins removing [name] from [self ? "[bodypart.owner.p_their(TRUE)]" : "[bodypart.owner]'s" ] [bodypart.name]...", "You begin to remove [name] from [self ? "your" : "[bodypart.owner]'s"] [bodypart.name]...") + if(!do_after(C, (self ? SELF_AID_REMOVE_DELAY : OTHER_AID_REMOVE_DELAY), target=bodypart.owner)) + return + if(QDELETED(src)) + return + C.visible_message("[C] removes [name] from [self ? "[bodypart.owner.p_their(TRUE)]" : "[bodypart.owner]'s" ] [bodypart.name].", "You remove [name] from [self ? "your" : "[bodypart.owner]'s" ] [bodypart.name].") + var/obj/item/gotten = rip_off() + if(gotten && !C.put_in_hands(gotten)) + gotten.forceMove(get_turf(C)) + +/datum/bodypart_aid/New(obj/item/bodypart/BP) + //'bodypart == BP' is set in subtypes to ensure some proper signals and behaviours + if(overlay_prefix && bodypart.owner) + bodypart.owner.update_bandage_overlays() + +/datum/bodypart_aid/Destroy() + if(overlay_prefix && bodypart.owner) + bodypart.owner.update_bandage_overlays() + bodypart = null + ..() + +/** + * take_damage() called when the bandage gets damaged + * + * This proc will subtract integrity and delete the bandage with a to_chat message to whoever was bandaged + * + */ + +/datum/bodypart_aid/proc/take_damage() + integrity-- + if(integrity <= 0) + if(bodypart.owner) + to_chat(bodypart.owner, "The [name] on your [bodypart.name] tears and falls off!") + qdel(src) + +/** + * rip_off() called when someone rips it off + * + * It will return the bandage if it's considered pristine + * + */ + +/datum/bodypart_aid/proc/rip_off() + if(is_pristine()) + . = new stack_to_drop(null, 1) + qdel(src) + +/** + * get_description() called by examine procs + * + * It will returns a description of the bandage + * + */ + +/datum/bodypart_aid/proc/get_description() + return "[name]" + +/** + * is_pristine() called by rip_off() + * + * Used to determine whether the bandage can be re-used and won't qdel itself + * + */ + +/datum/bodypart_aid/proc/is_pristine() + return (integrity == initial(integrity)) + +/datum/bodypart_aid/splint + name = "splint" + overlay_prefix = "splint" + desc_prefix = "fastened" + stack_to_drop = /obj/item/stack/medical/splint + /// How effective are we in keeping the bodypart rigid + var/splint_factor = 0.3 + /// Whether the splint prevents the limb from being disabled, with a ruptured tendon or a shattered bone + var/helps_disabled = TRUE + /// Total condition of our splint, the more we use it the more it gets looser + var/sling_condition = 5 + +/datum/bodypart_aid/splint/get_description() + var/desc + switch(sling_condition) + if(0 to 1.25) + desc = "barely holding" + if(1.25 to 2.75) + desc = "loose" + if(2.75 to 4) + desc = "rigid" + if(4 to INFINITY) + desc = "tight" + desc += " [name]" + return desc + +/datum/bodypart_aid/splint/Destroy() + SEND_SIGNAL(bodypart, COMSIG_BODYPART_SPLINT_DESTROYED) + bodypart.current_splint = null + return ..() + +/datum/bodypart_aid/splint/New(obj/item/bodypart/BP) + bodypart = BP + BP.current_splint = src + SEND_SIGNAL(BP, COMSIG_BODYPART_SPLINTED, src) + ..() + +/datum/bodypart_aid/splint/improvised + name = "improvised splint" + splint_factor = 0.6 + helps_disabled= FALSE + stack_to_drop = /obj/item/stack/medical/splint/improvised + overlay_prefix = "splint_improv" + +/datum/bodypart_aid/splint/tribal + name = "tribal splint" + splint_factor = 0.5 + stack_to_drop = /obj/item/stack/medical/splint/tribal + overlay_prefix = "splint_tribal" + +/datum/bodypart_aid/gauze + name = "gauze" + stack_to_drop = /obj/item/stack/medical/gauze + overlay_prefix = "gauze" + desc_prefix = "bandaged" + /// How much more can we absorb + var/absorption_capacity = 5 + /// How fast do we absorb + var/absorption_rate = 0.12 + /// How much does the gauze help with keeping infections clean + var/sanitisation_factor = 0.4 + /// How much sanitisation we've got after we become fairly stained and worn + var/sanitisation_factor_stained = 0.8 + /// Is it blood stained? For description + var/blood_stained = FALSE + /// Is it pus stained? For description + var/pus_stained = FALSE + +/datum/bodypart_aid/gauze/get_description() + var/desc + switch(absorption_capacity) + if(0 to 1.25) + desc = "nearly ruined" + if(1.25 to 2.75) + desc = "badly worn" + if(2.75 to 4) + desc = "slightly used" + if(4 to INFINITY) + desc = "clean" + if(blood_stained) + desc += ", bloodied" + if(pus_stained) + desc += ", pus stained" + desc += " [name]" + return desc + +/datum/bodypart_aid/gauze/New(obj/item/bodypart/BP) + bodypart = BP + BP.current_gauze = src + SEND_SIGNAL(BP, COMSIG_BODYPART_GAUZED, src) + ..() + +/datum/bodypart_aid/gauze/Destroy() + SEND_SIGNAL(bodypart, COMSIG_BODYPART_GAUZE_DESTROYED) + bodypart.current_gauze = null + return ..() + +/datum/bodypart_aid/gauze/is_pristine() + . = ..() + if(.) + return (absorption_capacity == initial(absorption_capacity)) + +/** + * seep_gauze() is for when a gauze wrapping absorbs blood or pus from wounds, lowering its absorption capacity. + * + * The passed amount of seepage is deducted from the bandage's absorption capacity, and if we reach a negative absorption capacity, the bandage won't help our wounds. + * When the bandage is left with a low amount of absorption, it'll notify user and act worse as a sanitiser for infections + * Returns TRUE if the bandage absorbed anything, FALSE if it's fully stained. + * + * Arguments: + * * seep_amt - How much absorption capacity we're removing from our current bandages (think, how much blood or pus are we soaking up this tick?) + * * type - Is it blood or pus we're being stained with? GAUZE_STAIN_BLOOD, GAUZE_STAIN_PUS defines from wounds.dm + */ + +/datum/bodypart_aid/gauze/proc/seep_gauze(amount, type) + if(absorption_capacity > 0) + . = TRUE + absorption_capacity -= amount + else + return FALSE + //If our remaining absorption capacity is low, make so blood and pus stains show + if(absorption_capacity < 2) + sanitisation_factor = sanitisation_factor_stained + if(type == GAUZE_STAIN_BLOOD && !blood_stained) + blood_stained = TRUE + if(bodypart.owner) + to_chat(bodypart.owner, "The [name] on your [bodypart.name] [pick(list("pools", "trickles", "seeps"))] with blood.") + else if(type == GAUZE_STAIN_PUS && !pus_stained) + pus_stained = TRUE + if(bodypart.owner) + to_chat(bodypart.owner, "The [name] on your [bodypart.name] [pick(list("pools", "trickles", "seeps"))] with pus.") + +/datum/bodypart_aid/gauze/improvised + name = "improvised gauze" + stack_to_drop = /obj/item/stack/medical/gauze/improvised + absorption_rate = 0.09 + absorption_capacity = 3 diff --git a/modular_skyrat/modules/medical/code/carbon_defense.dm b/modular_skyrat/modules/medical/code/carbon_defense.dm new file mode 100644 index 00000000000..06dcd0e45c0 --- /dev/null +++ b/modular_skyrat/modules/medical/code/carbon_defense.dm @@ -0,0 +1,19 @@ +/mob/living/carbon/proc/self_grasp_bleeding_limb(obj/item/bodypart/grasped_part, supress_message = FALSE) + if(!grasped_part?.get_bleed_rate()) + return + var/starting_hand_index = active_hand_index + if(starting_hand_index == grasped_part.held_index) + to_chat(src, "You can't grasp your [grasped_part.name] with itself!") + return + + to_chat(src, "You try grasping at your [grasped_part.name], trying to stop the bleeding...") + if(!do_after(src, 1.5 SECONDS)) + to_chat(src, "You fail to grasp your [grasped_part.name].") + return + + var/obj/item/self_grasp/grasp = new + if(starting_hand_index != active_hand_index || !put_in_active_hand(grasp)) + to_chat(src, "You fail to grasp your [grasped_part.name].") + QDEL_NULL(grasp) + return + grasp.grasp_limb(grasped_part) diff --git a/modular_skyrat/modules/medical/code/carbon_examine.dm b/modular_skyrat/modules/medical/code/carbon_examine.dm new file mode 100644 index 00000000000..f3356ba172e --- /dev/null +++ b/modular_skyrat/modules/medical/code/carbon_examine.dm @@ -0,0 +1,76 @@ +/mob/living/carbon/examine_more(mob/user) + var/msg = list("You examine [src] closer, and note the following...") + var/t_His = p_their(TRUE) + var/t_He = p_they(TRUE) + var/t_Has = p_have() + + var/any_bodypart_damage = FALSE + for(var/X in bodyparts) + var/obj/item/bodypart/LB = X + if(LB.is_pseudopart) + continue + var/limb_max_damage = LB.max_damage + var/status = "" + var/brutedamage = round(LB.brute_dam/limb_max_damage*100) + var/burndamage = round(LB.burn_dam/limb_max_damage*100) + switch(brutedamage) + if(20 to 50) + status = LB.light_brute_msg + if(51 to 75) + status = LB.medium_brute_msg + if(76 to 100) + status += LB.heavy_brute_msg + + if(burndamage >= 20 && status) + status += "and " + switch(burndamage) + if(20 to 50) + status += LB.light_burn_msg + if(51 to 75) + status += LB.medium_burn_msg + if(76 to 100) + status += LB.heavy_burn_msg + + if(status) + any_bodypart_damage = TRUE + msg += "\t[t_His] [LB.name] is [status]." + + for(var/thing in LB.wounds) + any_bodypart_damage = TRUE + var/datum/wound/W = thing + switch(W.severity) + if(WOUND_SEVERITY_TRIVIAL) + msg += "\t[t_His] [LB.name] is suffering [W.a_or_from] [W.get_topic_name(user)]." + if(WOUND_SEVERITY_MODERATE) + msg += "\t[t_His] [LB.name] is suffering [W.a_or_from] [W.get_topic_name(user)]!" + if(WOUND_SEVERITY_SEVERE) + msg += "\t[t_His] [LB.name] is suffering [W.a_or_from] [W.get_topic_name(user)]!" + if(WOUND_SEVERITY_CRITICAL) + msg += "\t[t_His] [LB.name] is suffering [W.a_or_from] [W.get_topic_name(user)]!!" + if(LB.current_gauze) + var/datum/bodypart_aid/current_gauze = LB.current_gauze + msg += "\t[t_His] [LB.name] is [current_gauze.desc_prefix] with [current_gauze.get_description()]." + if(LB.current_splint) + var/datum/bodypart_aid/current_splint = LB.current_splint + msg += "\t[t_His] [LB.name] is [current_splint.desc_prefix] with [current_splint.get_description()]." + + if(!any_bodypart_damage) + msg += "\t[t_He] [t_Has] no significantly damaged bodyparts." + + var/list/visible_scars + if(all_scars) + for(var/i in all_scars) + var/datum/scar/S = i + if(S.is_visible(user)) + LAZYADD(visible_scars, S) + + if(!visible_scars) + msg |= "\t[t_He] [t_Has] no visible scars." + else + for(var/i in visible_scars) + var/datum/scar/S = i + var/scar_text = S.get_examine_description(user) + if(scar_text) + msg += "[scar_text]" + + return msg diff --git a/modular_skyrat/modules/medical/code/carbon_update_icons.dm b/modular_skyrat/modules/medical/code/carbon_update_icons.dm new file mode 100644 index 00000000000..47bc71abc03 --- /dev/null +++ b/modular_skyrat/modules/medical/code/carbon_update_icons.dm @@ -0,0 +1,20 @@ +/mob/living/carbon/proc/update_bandage_overlays() + remove_overlay(BANDAGE_LAYER) + + var/mutable_appearance/overlays = mutable_appearance('modular_skyrat/modules/medical/icons/on_limb_overlays.dmi', "", -BANDAGE_LAYER) + overlays_standing[BANDAGE_LAYER] = overlays + + for(var/b in bodyparts) + var/obj/item/bodypart/BP = b + if(BP.current_gauze && BP.current_gauze.overlay_prefix) + var/bp_suffix = BP.body_zone + if(BP.use_digitigrade) + bp_suffix += "_digitigrade" + overlays.add_overlay("[BP.current_gauze.overlay_prefix]_[bp_suffix]") + if(BP.current_splint && BP.current_splint.overlay_prefix) + var/bp_suffix = BP.body_zone + if(BP.use_digitigrade) + bp_suffix += "_digitigrade" + overlays.add_overlay("[BP.current_splint.overlay_prefix]_[bp_suffix]") + + apply_overlay(BANDAGE_LAYER) diff --git a/modular_skyrat/modules/medical/code/crafting_recipes.dm b/modular_skyrat/modules/medical/code/crafting_recipes.dm new file mode 100644 index 00000000000..318673dfbc8 --- /dev/null +++ b/modular_skyrat/modules/medical/code/crafting_recipes.dm @@ -0,0 +1,14 @@ +/datum/crafting_recipe/tribalsplint + name = "Tribal Splint" + result = /obj/item/stack/medical/splint/tribal + time = 30 + reqs = list(/obj/item/stack/sheet/bone = 2, + /obj/item/stack/sheet/sinew = 1) + category = CAT_PRIMAL + +/datum/crafting_recipe/improvsplint + name = "Improvised Splint" + result = /obj/item/stack/medical/splint/improvised + time = 30 + reqs = list(/obj/item/stack/sheet/mineral/wood = 2, /obj/item/stack/sheet/cloth = 2) + category = CAT_MISC diff --git a/modular_skyrat/modules/medical/code/splint.dm b/modular_skyrat/modules/medical/code/splint.dm new file mode 100644 index 00000000000..f8c707fff90 --- /dev/null +++ b/modular_skyrat/modules/medical/code/splint.dm @@ -0,0 +1,66 @@ +/obj/item/stack/medical/splint + name = "medical splint" + desc = "Medical splints, designed to fastened a limb with ease, perfect for stabilizing broken bones and torn muscles. " + gender = PLURAL + singular_name = "medical splint" + icon_state = "splint" + icon = 'modular_skyrat/modules/medical/icons/stack_medical.dmi' + self_delay = 5 SECONDS + other_delay = 2 SECONDS + max_amount = 12 + amount = 6 + grind_results = list(/datum/reagent/carbon = 2) + custom_price = PAYCHECK_ASSISTANT * 2 + merge_type = /obj/item/stack/medical/splint + var/splint_type = /datum/bodypart_aid/splint + +/obj/item/stack/medical/splint/try_heal(mob/living/M, mob/user, silent) + var/obj/item/bodypart/limb = M.get_bodypart(check_zone(user.zone_selected)) + if(!limb) + to_chat(user, "There's nothing there to bandage!") + return + if(!LAZYLEN(limb.wounds)) + to_chat(user, "There's no wounds that require bandaging on [user==M ? "your" : "[M]'s"] [limb.name]!") // good problem to have imo + return + + var/splintable_wound = FALSE + for(var/i in limb.wounds) + var/datum/wound/woundies = i + if(woundies.wound_flags & ACCEPTS_SPLINT) + splintable_wound = TRUE + break + if(!splintable_wound) + to_chat(user, "There's no wounds that require splinting on [user==M ? "your" : "[M]'s"] [limb.name]!") // good problem to have imo + return + + if(limb.current_splint) + to_chat(user, "[user==M ? "Your" : "[M]'s"] [limb.name] is already fastened in a splint!") + return + + user.visible_message("[user] begins fastening [M]'s [limb.name] with [src]...", "You begin to fasten [user == M ? "your" : "[M]'s"] [limb.name] with [src]...") + if(!do_after(user, (user == M ? self_delay : other_delay), target=M)) + return + + user.visible_message("[user] applies [src] to [M]'s [limb.name].", "You splint [user == M ? "your" : "[M]'s"] [limb.name].") + limb.apply_splint(src) + +/obj/item/stack/medical/splint/twelve + amount = 12 + +/obj/item/stack/medical/splint/tribal + name = "tribal splint" + desc = "Bone fastened with sinew, used to keep injured limbs rigid, surprisingly effective." + singular_name = "tribal splint" + icon_state = "splint_tribal" + amount = 1 + splint_type = /datum/bodypart_aid/splint/tribal + merge_type = /obj/item/stack/medical/splint/tribal + +/obj/item/stack/medical/splint/improvised + name = "improvised splint" + desc = "Crudely made out splints with wood and some cotton sling, you doubt this will be any good." + singular_name = "improvised splint" + icon_state = "splint_improv" + amount = 1 + splint_type = /datum/bodypart_aid/splint/improvised + merge_type = /obj/item/stack/medical/splint/improvised diff --git a/modular_skyrat/modules/medical/code/wounds/_wounds.dm b/modular_skyrat/modules/medical/code/wounds/_wounds.dm new file mode 100644 index 00000000000..71f74b011dd --- /dev/null +++ b/modular_skyrat/modules/medical/code/wounds/_wounds.dm @@ -0,0 +1,7 @@ +/// Whether we should show an interactable topic in examines of the wound. href_list["wound_topic"] +/datum/wound/proc/show_wound_topic(mob/user) + return FALSE + +/// Gets the name of the wound with any interactable topic if possible +/datum/wound/proc/get_topic_name(mob/user) + return show_wound_topic(user) ? "[lowertext(name)]" : lowertext(name) diff --git a/modular_skyrat/modules/medical/code/wounds/bones.dm b/modular_skyrat/modules/medical/code/wounds/bones.dm new file mode 100644 index 00000000000..ac39afb143c --- /dev/null +++ b/modular_skyrat/modules/medical/code/wounds/bones.dm @@ -0,0 +1,448 @@ + +/* + Blunt/Bone wounds +*/ +// TODO: well, a lot really, but i'd kill to get overlays and a bonebreaking effect like Blitz: The League, similar to electric shock skeletons + +/datum/wound/blunt + name = "Blunt (Bone) Wound" + sound_effect = 'sound/effects/wounds/crack1.ogg' + wound_type = WOUND_BLUNT + wound_flags = (BONE_WOUND | ACCEPTS_SPLINT) + + /// Have we been taped? + var/taped + /// Have we been bone gel'd? + var/gelled + /// If we did the gel + surgical tape healing method for fractures, how many ticks does it take to heal by default + var/regen_ticks_needed + /// Our current counter for gel + surgical tape regeneration + var/regen_ticks_current + /// If we suffer severe head booboos, we can get brain traumas tied to them + var/datum/brain_trauma/active_trauma + /// What brain trauma group, if any, we can draw from for head wounds + var/brain_trauma_group + /// If we deal brain traumas, when is the next one due? + var/next_trauma_cycle + /// How long do we wait +/- 20% for the next trauma? + var/trauma_cycle_cooldown + /// If this is a chest wound and this is set, we have this chance to cough up blood when hit in the chest + var/internal_bleeding_chance = 0 + +/* + Overwriting of base procs +*/ +/datum/wound/blunt/wound_injury(datum/wound/old_wound = null) + // hook into gaining/losing gauze so crit bone wounds can re-enable/disable depending if they're slung or not + RegisterSignal(limb, list(COMSIG_BODYPART_SPLINTED, COMSIG_BODYPART_SPLINT_DESTROYED), .proc/update_inefficiencies) + + if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group) + processes = TRUE + active_trauma = victim.gain_trauma_type(brain_trauma_group, TRAUMA_RESILIENCE_WOUND) + next_trauma_cycle = world.time + (rand(100-WOUND_BONE_HEAD_TIME_VARIANCE, 100+WOUND_BONE_HEAD_TIME_VARIANCE) * 0.01 * trauma_cycle_cooldown) + + RegisterSignal(victim, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, .proc/attack_with_hurt_hand) + if(limb.held_index && victim.get_item_for_held_index(limb.held_index) && (disabling || prob(30 * severity))) + var/obj/item/I = victim.get_item_for_held_index(limb.held_index) + if(istype(I, /obj/item/offhand)) + I = victim.get_inactive_held_item() + + if(I && victim.dropItemToGround(I)) + victim.visible_message("[victim] drops [I] in shock!", "The force on your [limb.name] causes you to drop [I]!", vision_distance=COMBAT_MESSAGE_RANGE) + + update_inefficiencies() + +/datum/wound/blunt/remove_wound(ignore_limb, replaced) + limp_slowdown = 0 + QDEL_NULL(active_trauma) + if(limb) + UnregisterSignal(limb, list(COMSIG_BODYPART_GAUZED, COMSIG_BODYPART_GAUZE_DESTROYED)) + if(victim) + UnregisterSignal(victim, COMSIG_HUMAN_EARLY_UNARMED_ATTACK) + return ..() + +/datum/wound/blunt/handle_process() + . = ..() + if(limb.body_zone == BODY_ZONE_HEAD && brain_trauma_group && world.time > next_trauma_cycle) + if(active_trauma) + QDEL_NULL(active_trauma) + else + active_trauma = victim.gain_trauma_type(brain_trauma_group, TRAUMA_RESILIENCE_WOUND) + next_trauma_cycle = world.time + (rand(100-WOUND_BONE_HEAD_TIME_VARIANCE, 100+WOUND_BONE_HEAD_TIME_VARIANCE) * 0.01 * trauma_cycle_cooldown) + + if(!gelled || !taped) + return + + regen_ticks_current++ + if(victim.body_position == LYING_DOWN) + if(prob(50)) + regen_ticks_current += 0.5 + if(victim.IsSleeping() && prob(50)) + regen_ticks_current += 0.5 + + if(prob(severity * 3)) + victim.take_bodypart_damage(rand(1, severity * 2), stamina=rand(2, severity * 2.5), wound_bonus=CANT_WOUND) + if(prob(33)) + to_chat(victim, "You feel a sharp pain in your body as your bones are reforming!") + + if(regen_ticks_current > regen_ticks_needed) + if(!victim || !limb) + qdel(src) + return + to_chat(victim, "Your [limb.name] has recovered from your fracture!") + remove_wound() + +/// If we're a human who's punching something with a broken arm, we might hurt ourselves doing so +/datum/wound/blunt/proc/attack_with_hurt_hand(mob/M, atom/target, proximity) + SIGNAL_HANDLER + + if(victim.get_active_hand() != limb || victim.a_intent == INTENT_HELP || !ismob(target) || severity <= WOUND_SEVERITY_MODERATE) + return + + // With a severe or critical wound, you have a 15% or 30% chance to proc pain on hit + if(prob((severity - 1) * 15)) + // And you have a 70% or 50% chance to actually land the blow, respectively + if(prob(70 - 20 * (severity - 1))) + to_chat(victim, "The fracture in your [limb.name] shoots with pain as you strike [target]!") + limb.receive_damage(brute=rand(1,5)) + else + victim.visible_message("[victim] weakly strikes [target] with [victim.p_their()] broken [limb.name], recoiling from pain!", \ + "You fail to strike [target] as the fracture in your [limb.name] lights up in unbearable pain!", vision_distance=COMBAT_MESSAGE_RANGE) + INVOKE_ASYNC(victim, /mob.proc/emote, "scream") + victim.Stun(0.5 SECONDS) + limb.receive_damage(brute=rand(3,7)) + return COMPONENT_CANCEL_ATTACK_CHAIN + + +/datum/wound/blunt/receive_damage(wounding_type, wounding_dmg, wound_bonus) + if(!victim || wounding_dmg < WOUND_MINIMUM_DAMAGE) + return + if(ishuman(victim)) + var/mob/living/carbon/human/human_victim = victim + if(NOBLOOD in human_victim.dna?.species.species_traits) + return + + if(limb.body_zone == BODY_ZONE_CHEST && victim.blood_volume && prob(internal_bleeding_chance + wounding_dmg)) + var/blood_bled = rand(1, wounding_dmg * (severity == WOUND_SEVERITY_CRITICAL ? 2 : 1.5)) // 12 brute toolbox can cause up to 18/24 bleeding with a severe/critical chest wound + switch(blood_bled) + if(1 to 6) + victim.bleed(blood_bled, TRUE) + if(7 to 13) + victim.visible_message("[victim] coughs up a bit of blood from the blow to [victim.p_their()] chest.", "You cough up a bit of blood from the blow to your chest.", vision_distance=COMBAT_MESSAGE_RANGE) + victim.bleed(blood_bled, TRUE) + if(14 to 19) + victim.visible_message("[victim] spits out a string of blood from the blow to [victim.p_their()] chest!", "You spit out a string of blood from the blow to your chest!", vision_distance=COMBAT_MESSAGE_RANGE) + new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) + victim.bleed(blood_bled) + if(20 to INFINITY) + victim.visible_message("[victim] chokes up a spray of blood from the blow to [victim.p_their()] chest!", "You choke up on a spray of blood from the blow to your chest!", vision_distance=COMBAT_MESSAGE_RANGE) + victim.bleed(blood_bled) + new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir) + victim.add_splatter_floor(get_step(victim.loc, victim.dir)) + + +/datum/wound/blunt/get_examine_description(mob/user) + if(!limb.current_splint && !gelled && !taped) + return ..() + + var/list/msg = list() + if(!limb.current_splint) + msg += "[victim.p_their(TRUE)] [limb.name] [examine_desc]" + else + var/sling_condition = "" + // how much life we have left in these bandages + switch(limb.current_splint.sling_condition) + if(0 to 1.25) + sling_condition = "just barely" + if(1.25 to 2.75) + sling_condition = "loosely" + if(2.75 to 4) + sling_condition = "mostly" + if(4 to INFINITY) + sling_condition = "tightly" + + msg += "[victim.p_their(TRUE)] [limb.name] is [sling_condition] fastened with a [limb.current_splint.name]" + + if(taped) + msg += ", and appears to be reforming itself under some surgical tape!" + else if(gelled) + msg += ", with fizzing flecks of blue bone gel sparking off the bone!" + else + msg += "!" + return "[msg.Join()]" + +/* + New common procs for /datum/wound/blunt/ +*/ + +/datum/wound/blunt/proc/update_inefficiencies() + if(limb.body_zone in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) + if(limb.current_splint) + limp_slowdown = initial(limp_slowdown) * limb.current_splint.splint_factor + else + limp_slowdown = initial(limp_slowdown) + victim.apply_status_effect(STATUS_EFFECT_LIMP) + else if(limb.body_zone in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) + if(limb.current_splint) + interaction_efficiency_penalty = 1 + ((interaction_efficiency_penalty - 1) * limb.current_splint.splint_factor) + else + interaction_efficiency_penalty = interaction_efficiency_penalty + + if(initial(disabling)) + set_disabling(!(limb.current_splint && limb.current_splint.helps_disabled)) + + limb.update_wounds() + +/// Joint Dislocation (Moderate Blunt) +/datum/wound/blunt/moderate + name = "Joint Dislocation" + desc = "Patient's bone has been unset from socket, causing pain and reduced motor function." + treat_text = "Recommended application of bonesetter to affected limb, though manual relocation by applying an aggressive grab to the patient and helpfully interacting with afflicted limb may suffice." + examine_desc = "is awkwardly jammed out of place" + occur_text = "jerks violently and becomes unseated" + severity = WOUND_SEVERITY_MODERATE + viable_zones = list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + interaction_efficiency_penalty = 1.5 + limp_slowdown = 3 + threshold_minimum = 35 + threshold_penalty = 15 + treatable_tool = TOOL_BONESET + wound_flags = (BONE_WOUND) + status_effect_type = /datum/status_effect/wound/blunt/moderate + scar_keyword = "bluntmoderate" + +/datum/wound/blunt/moderate/Destroy() + if(victim) + UnregisterSignal(victim, COMSIG_LIVING_DOORCRUSHED) + return ..() + +/datum/wound/blunt/moderate/wound_injury(datum/wound/old_wound) + . = ..() + RegisterSignal(victim, COMSIG_LIVING_DOORCRUSHED, .proc/door_crush) + +/// Getting smushed in an airlock/firelock is a last-ditch attempt to try relocating your limb +/datum/wound/blunt/moderate/proc/door_crush() + if(prob(33)) + victim.visible_message("[victim]'s dislocated [limb.name] pops back into place!", "Your dislocated [limb.name] pops back into place! Ow!") + remove_wound() + +/datum/wound/blunt/moderate/try_handling(mob/living/carbon/human/user) + if(user.pulling != victim || user.zone_selected != limb.body_zone || user.a_intent == INTENT_GRAB) + return FALSE + + if(user.grab_state == GRAB_PASSIVE) + to_chat(user, "You must have [victim] in an aggressive grab to manipulate [victim.p_their()] [lowertext(name)]!") + return TRUE + + if(user.grab_state >= GRAB_AGGRESSIVE) + user.visible_message("[user] begins twisting and straining [victim]'s dislocated [limb.name]!", "You begin twisting and straining [victim]'s dislocated [limb.name]...", ignored_mobs=victim) + to_chat(victim, "[user] begins twisting and straining your dislocated [limb.name]!") + if(user.a_intent == INTENT_HELP) + chiropractice(user) + else + malpractice(user) + return TRUE + +/// If someone is snapping our dislocated joint back into place by hand with an aggro grab and help intent +/datum/wound/blunt/moderate/proc/chiropractice(mob/living/carbon/human/user) + var/time = base_treat_time + + if(!do_after(user, time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + return + + if(prob(65)) + user.visible_message("[user] snaps [victim]'s dislocated [limb.name] back into place!", "You snap [victim]'s dislocated [limb.name] back into place!", ignored_mobs=victim) + to_chat(victim, "[user] snaps your dislocated [limb.name] back into place!") + victim.emote("scream") + limb.receive_damage(brute=20, wound_bonus=CANT_WOUND) + qdel(src) + else + user.visible_message("[user] wrenches [victim]'s dislocated [limb.name] around painfully!", "You wrench [victim]'s dislocated [limb.name] around painfully!", ignored_mobs=victim) + to_chat(victim, "[user] wrenches your dislocated [limb.name] around painfully!") + limb.receive_damage(brute=10, wound_bonus=CANT_WOUND) + chiropractice(user) + +/// If someone is snapping our dislocated joint into a fracture by hand with an aggro grab and harm or disarm intent +/datum/wound/blunt/moderate/proc/malpractice(mob/living/carbon/human/user) + var/time = base_treat_time + + if(!do_after(user, time, target=victim, extra_checks = CALLBACK(src, .proc/still_exists))) + return + + if(prob(65)) + user.visible_message("[user] snaps [victim]'s dislocated [limb.name] with a sickening crack!", "You snap [victim]'s dislocated [limb.name] with a sickening crack!", ignored_mobs=victim) + to_chat(victim, "[user] snaps your dislocated [limb.name] with a sickening crack!") + victim.emote("scream") + limb.receive_damage(brute=25, wound_bonus=30) + else + user.visible_message("[user] wrenches [victim]'s dislocated [limb.name] around painfully!", "You wrench [victim]'s dislocated [limb.name] around painfully!", ignored_mobs=victim) + to_chat(victim, "[user] wrenches your dislocated [limb.name] around painfully!") + limb.receive_damage(brute=10, wound_bonus=CANT_WOUND) + malpractice(user) + + +/datum/wound/blunt/moderate/treat(obj/item/I, mob/user) + if(victim == user) + victim.visible_message("[user] begins resetting [victim.p_their()] [limb.name] with [I].", "You begin resetting your [limb.name] with [I]...") + else + user.visible_message("[user] begins resetting [victim]'s [limb.name] with [I].", "You begin resetting [victim]'s [limb.name] with [I]...") + + if(!do_after(user, base_treat_time * (user == victim ? 1.5 : 1), target = victim, extra_checks=CALLBACK(src, .proc/still_exists))) + return + + if(victim == user) + limb.receive_damage(brute=15, wound_bonus=CANT_WOUND) + victim.visible_message("[user] finishes resetting [victim.p_their()] [limb.name]!", "You reset your [limb.name]!") + else + limb.receive_damage(brute=10, wound_bonus=CANT_WOUND) + user.visible_message("[user] finishes resetting [victim]'s [limb.name]!", "You finish resetting [victim]'s [limb.name]!", victim) + to_chat(victim, "[user] resets your [limb.name]!") + + victim.emote("scream") + qdel(src) + +/* + Severe (Hairline Fracture) +*/ + +/datum/wound/blunt/severe + name = "Hairline Fracture" + desc = "Patient's bone has suffered a crack in the foundation, causing serious pain and reduced limb functionality." + treat_text = "Recommended light surgical application of bone gel, though a sling of medical gauze will prevent worsening situation." + examine_desc = "appears grotesquely swollen, its attachment weakened" + occur_text = "sprays chips of bone and develops a nasty looking bruise" + + severity = WOUND_SEVERITY_SEVERE + interaction_efficiency_penalty = 2 + limp_slowdown = 6 + threshold_minimum = 60 + threshold_penalty = 30 + treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel) + status_effect_type = /datum/status_effect/wound/blunt/severe + scar_keyword = "bluntsevere" + brain_trauma_group = BRAIN_TRAUMA_MILD + trauma_cycle_cooldown = 1.5 MINUTES + internal_bleeding_chance = 40 + wound_flags = (BONE_WOUND | ACCEPTS_SPLINT | MANGLES_BONE) + regen_ticks_needed = 120 // ticks every 2 seconds, 240 seconds, so roughly 4 minutes default + +/// Compound Fracture (Critical Blunt) +/datum/wound/blunt/critical + name = "Compound Fracture" + desc = "Patient's bones have suffered multiple gruesome fractures, causing significant pain and near uselessness of limb." + treat_text = "Immediate binding of affected limb, followed by surgical intervention ASAP." + examine_desc = "is mangled and pulped, seemingly held together by tissue alone" + occur_text = "cracks apart, exposing broken bones to open air" + + severity = WOUND_SEVERITY_CRITICAL + interaction_efficiency_penalty = 4 + limp_slowdown = 9 + sound_effect = 'sound/effects/wounds/crack2.ogg' + threshold_minimum = 115 + threshold_penalty = 50 + disabling = TRUE + treatable_by = list(/obj/item/stack/sticky_tape/surgical, /obj/item/stack/medical/bone_gel) + status_effect_type = /datum/status_effect/wound/blunt/critical + scar_keyword = "bluntcritical" + brain_trauma_group = BRAIN_TRAUMA_SEVERE + trauma_cycle_cooldown = 2.5 MINUTES + internal_bleeding_chance = 60 + wound_flags = (BONE_WOUND | ACCEPTS_SPLINT | MANGLES_BONE) + regen_ticks_needed = 240 // ticks every 2 seconds, 480 seconds, so roughly 8 minutes default + +// doesn't make much sense for "a" bone to stick out of your head +/datum/wound/blunt/critical/apply_wound(obj/item/bodypart/L, silent, datum/wound/old_wound, smited) + if(L.body_zone == BODY_ZONE_HEAD) + occur_text = "splits open, exposing a bare, cracked skull through the flesh and blood" + examine_desc = "has an unsettling indent, with bits of skull poking out" + . = ..() + +/// if someone is using bone gel on our wound +/datum/wound/blunt/proc/gel(obj/item/stack/medical/bone_gel/I, mob/user) + if(gelled) + to_chat(user, "[user == victim ? "Your" : "[victim]'s"] [limb.name] is already coated with bone gel!") + return + + user.visible_message("[user] begins hastily applying [I] to [victim]'s' [limb.name]...", "You begin hastily applying [I] to [user == victim ? "your" : "[victim]'s"] [limb.name], disregarding the warning label...") + + if(!do_after(user, base_treat_time * 1.5 * (user == victim ? 1.5 : 1), target = victim, extra_checks=CALLBACK(src, .proc/still_exists))) + return + + I.use(1) + victim.emote("scream") + if(user != victim) + user.visible_message("[user] finishes applying [I] to [victim]'s [limb.name], emitting a fizzing noise!", "You finish applying [I] to [victim]'s [limb.name]!", ignored_mobs=victim) + to_chat(victim, "[user] finishes applying [I] to your [limb.name], and you can feel the bones exploding with pain as they begin melting and reforming!") + else + var/painkiller_bonus = 0 + if(victim.drunkenness > 10) + painkiller_bonus += 10 + if(victim.reagents.has_reagent(/datum/reagent/medicine/morphine)) + painkiller_bonus += 20 + if(victim.reagents.has_reagent(/datum/reagent/determination)) + painkiller_bonus += 10 + if(victim.reagents.has_reagent(/datum/reagent/consumable/ethanol/painkiller)) + painkiller_bonus += 5 + if(victim.reagents.has_reagent(/datum/reagent/medicine/mine_salve)) + painkiller_bonus += 20 + + if(prob(25 + (20 * (severity - 2)) - painkiller_bonus)) // 25%/45% chance to fail self-applying with severe and critical wounds, modded by painkillers + victim.visible_message("[victim] fails to finish applying [I] to [victim.p_their()] [limb.name], passing out from the pain!", "You pass out from the pain of applying [I] to your [limb.name] before you can finish!") + victim.AdjustUnconscious(5 SECONDS) + return + victim.visible_message("[victim] finishes applying [I] to [victim.p_their()] [limb.name], grimacing from the pain!", "You finish applying [I] to your [limb.name], and your bones explode in pain!") + + limb.receive_damage(25, stamina=100, wound_bonus=CANT_WOUND) + if(!gelled) + gelled = TRUE + +/// if someone is using surgical tape on our wound +/datum/wound/blunt/proc/tape(obj/item/stack/sticky_tape/surgical/I, mob/user) + if(!gelled) + to_chat(user, "[user == victim ? "Your" : "[victim]'s"] [limb.name] must be coated with bone gel to perform this emergency operation!") + return + if(taped) + to_chat(user, "[user == victim ? "Your" : "[victim]'s"] [limb.name] is already wrapped in [I.name] and reforming!") + return + + user.visible_message("[user] begins applying [I] to [victim]'s' [limb.name]...", "You begin applying [I] to [user == victim ? "your" : "[victim]'s"] [limb.name]...") + + if(!do_after(user, base_treat_time * (user == victim ? 1.5 : 1), target = victim, extra_checks=CALLBACK(src, .proc/still_exists))) + return + + if(victim == user) + regen_ticks_needed *= 1.5 + + I.use(1) + if(user != victim) + user.visible_message("[user] finishes applying [I] to [victim]'s [limb.name], emitting a fizzing noise!", "You finish applying [I] to [victim]'s [limb.name]!", ignored_mobs=victim) + to_chat(victim, "[user] finishes applying [I] to your [limb.name], you immediately begin to feel your bones start to reform!") + else + victim.visible_message("[victim] finishes applying [I] to [victim.p_their()] [limb.name], !", "You finish applying [I] to your [limb.name], and you immediately begin to feel your bones start to reform!") + + taped = TRUE + processes = TRUE + +/datum/wound/blunt/treat(obj/item/I, mob/user) + if(istype(I, /obj/item/stack/medical/bone_gel)) + gel(I, user) + else if(istype(I, /obj/item/stack/sticky_tape/surgical)) + tape(I, user) + +/datum/wound/blunt/get_scanner_description(mob/user) + . = ..() + + . += "