diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index d3b3a10117de..269585c0239e 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -73,8 +73,10 @@
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack"
///from base of atom/animal_attack(): (/mob/user)
#define COMSIG_ATOM_ATTACK_ANIMAL "attack_animal"
-///from base of atom/examine(): (/mob)
+///from base of atom/examine(): (examining_user, examine_list)
#define COMSIG_PARENT_EXAMINE "atom_examine"
+///from base of atom/examine_more(): (examining_user, examine_list)
+#define COMSIG_PARENT_EXAMINE_MORE "atom_examine_more"
///from base of atom/get_examine_name(): (/mob, list/overrides)
#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name"
//Positions for overrides list
@@ -508,6 +510,16 @@
//sent from a mob when they set themselves to DNR
#define COMSIG_LIVING_SET_DNR "set_dnr"
+// Sent from a surgery step when blood is being splashed. (datum/surgery, mob/user, mob/target, zone, obj/item/tool)
+#define COMSIG_SURGERY_BLOOD_SPLASH "surgery_blood_splash"
+ /// If returned from this signal, will prevent any surgery splashing.
+ #define COMPONENT_BLOOD_SPLASH_HANDLED (1<<0)
+
+// Sent from a surgery step when organs are being spread from an incision
+#define COMSIG_SURGERY_GERM_SPREAD "surgery_germ_spread"
+ /// If returned from this signal, germ spread will be blocked.
+ #define COMPONENT_GERM_SPREAD_BLOCK (1<<0)
+
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
// none of these are called as of right now, as there is nothing listening for them.
diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm
index e0a5c1bfcc73..54fd99d2cf84 100644
--- a/code/datums/components/surgery_initiator.dm
+++ b/code/datums/components/surgery_initiator.dm
@@ -24,6 +24,13 @@
/// Also, note that for anything sharp, SURGERY_INITIATOR_ORGANIC should be set as well.
var/valid_starting_types = SURGERY_INITIATOR_ORGANIC
+ /// How effective this is at preventing infections.
+ /// 0 = preventing nothing, 1 = preventing any infection
+ var/germ_prevention_quality = 0
+
+ /// The sound to play when starting surgeries
+ var/surgery_start_sound = null
+
// Replace any other surgery initiator
dupe_type = /datum/component/surgery_initiator
@@ -43,10 +50,16 @@
/datum/component/surgery_initiator/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(initiate_surgery_moment))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS, PROC_REF(on_parent_sharpness_change))
+ RegisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE, PROC_REF(on_parent_examine_more))
/datum/component/surgery_initiator/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ITEM_ATTACK)
UnregisterSignal(parent, COMSIG_ATOM_UPDATE_SHARPNESS)
+ UnregisterSignal(parent, COMSIG_PARENT_EXAMINE_MORE)
+
+/datum/component/surgery_initiator/proc/on_parent_examine_more(datum/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER // COMSIG_PARENT_EXAMINE_MORE
+ examine_list += "You can use this on someone who is laying down to begin surgery on them."
/// Keep tabs on the attached item's sharpness.
/// This component gets added in atoms when they're made sharp as well.
@@ -134,8 +147,14 @@
if(!procedure)
return
+ if(!on_surgery_selection(user, target, procedure))
+ return
+
return try_choose_surgery(user, target, procedure)
+/datum/component/surgery_initiator/proc/on_surgery_selection(mob/user, mob/living/target, datum/surgery/target_surgery)
+ return TRUE
+
/datum/component/surgery_initiator/proc/get_available_surgeries(mob/user, mob/living/target)
var/list/available_surgeries = list()
for(var/datum/surgery/surgery in GLOB.surgeries_list)
@@ -150,16 +169,19 @@
return available_surgeries
+/datum/component/surgery_initiator/proc/cancel_unstarted_surgery_fluff(datum/surgery/the_surgery, mob/living/patient, mob/user, selected_zone)
+ user.visible_message(
+ "[user] stops the surgery on [patient]'s [parse_zone(selected_zone)] with [parent].",
+ "You stop the surgery on [patient]'s [parse_zone(selected_zone)] with [parent].",
+ )
+
/// Does the surgery de-initiation.
/datum/component/surgery_initiator/proc/attempt_cancel_surgery(datum/surgery/the_surgery, mob/living/patient, mob/user)
var/selected_zone = user.zone_selected
/// We haven't even started yet. Any surgery can be cancelled at this point.
if(the_surgery.step_number == 1)
patient.surgeries -= the_surgery
- user.visible_message(
- "[user] stops the surgery on [patient]'s [parse_zone(selected_zone)] with [parent].",
- "You stop the surgery on [patient]'s [parse_zone(selected_zone)] with [parent].",
- )
+ cancel_unstarted_surgery_fluff(the_surgery, patient, user, selected_zone)
qdel(the_surgery)
return TRUE
@@ -288,10 +310,17 @@
var/datum/surgery/procedure = new surgery.type(target, selected_zone, affecting_limb)
+
+ RegisterSignal(procedure, COMSIG_SURGERY_BLOOD_SPLASH, PROC_REF(on_blood_splash))
+
+ procedure.germ_prevention_quality = germ_prevention_quality
+
show_starting_message(user, target, procedure)
log_attack(user, target, "operated on (OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
+ return procedure
+
/datum/component/surgery_initiator/proc/surgery_needs_exposure(datum/surgery/surgery, mob/living/target)
return !surgery.ignore_clothes && !get_location_accessible(target, target.zone_selected)
@@ -302,6 +331,14 @@
"You hold [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for \an [procedure.name].",
)
+/datum/component/surgery_initiator/proc/on_prevent_germs()
+ SIGNAL_HANDLER //
+ return
+
+/datum/component/surgery_initiator/proc/on_blood_splash()
+ SIGNAL_HANDLER // COMSIG_SURGERY_BLOOD_SPLASH
+ return
+
/datum/component/surgery_initiator/limb
can_cancel = FALSE // don't let a leg cancel a surgery
@@ -310,3 +347,50 @@
/datum/component/surgery_initiator/robo/sharp
valid_starting_types = SURGERY_INITIATOR_ORGANIC | SURGERY_INITIATOR_ROBOTIC
+
+/datum/component/surgery_initiator/cloth
+ can_cancel = FALSE
+ surgery_start_sound = "rustle"
+
+/datum/component/surgery_initiator/cloth/show_starting_message(mob/user, mob/living/target, datum/surgery/procedure)
+ user.visible_message(
+ "[user] drapes [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for surgery.",
+ "You drape [parent] over [target]'s [parse_zone(user.zone_selected)] to prepare for \an [procedure.name].",
+ )
+
+/datum/component/surgery_initiator/cloth/try_choose_surgery(mob/user, mob/living/target, datum/surgery/surgery)
+ var/datum/surgery/new_procedure = ..()
+ if(!istype(new_procedure))
+ return
+
+ new_procedure.started_with_drapes = TRUE
+
+/datum/component/surgery_initiator/cloth/on_surgery_selection(mob/user, mob/living/target, datum/surgery/target_surgery)
+ user.visible_message(
+ "[user] starts to apply [parent] onto [target].",
+ "You start to apply [parent] onto [target].",
+ )
+
+ if(!isnull(surgery_start_sound))
+ playsound(src, surgery_start_sound, 50, TRUE)
+
+ playsound(src, surgery_start_sound)
+ if(!do_after_once(user, 3 SECONDS, TRUE, target))
+ user.visible_message(
+ "[user] stops applying [parent] onto [target].",
+ "You stop applying [parent] onto [target]."
+ )
+ return
+
+
+ if(!isnull(surgery_start_sound))
+ playsound(src, surgery_start_sound, 50, TRUE)
+
+ return TRUE
+
+/datum/component/surgery_initiator/cloth/on_blood_splash(datum/surgery, mob/user, mob/target, zone, obj/item/tool)
+ if(prob(90 * germ_prevention_quality))
+ target.visible_message("Blood splashes onto the dressing.")
+ var/obj/item/I = parent // safety: this component can only go onto an item
+ I.add_mob_blood(target)
+ return COMPONENT_BLOOD_SPLASH_HANDLED
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 4ed17d0b76ce..78d92c6673d3 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -449,8 +449,8 @@
/atom/proc/examine_more(mob/user)
SHOULD_CALL_PARENT(TRUE)
RETURN_TYPE(/list)
-
- return list()
+ . = list()
+ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE_MORE, user, .)
/**
* Updates the appearence of the icon
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 3a710799b8b4..94585a31f1a3 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -368,6 +368,7 @@ GLOBAL_LIST_INIT(cloth_recipes, list (
)),
null,
new /datum/stack_recipe("improvised gauze", /obj/item/stack/medical/bruise_pack/improvised, 1, 2, 6),
+ new /datum/stack_recipe("imrovised drapes", /obj/item/surgical_drapes/improvised, 1),
new /datum/stack_recipe("rag", /obj/item/reagent_containers/glass/rag, 1),
new /datum/stack_recipe("bedsheet", /obj/item/bedsheet, 3),
new /datum/stack_recipe("empty sandbag", /obj/item/emptysandbag, 4),
@@ -407,6 +408,7 @@ GLOBAL_LIST_INIT(durathread_recipes, list (
new /datum/stack_recipe("durathread beret", /obj/item/clothing/head/beret/durathread, 2, time = 4 SECONDS),
new /datum/stack_recipe("durathread beanie", /obj/item/clothing/head/beanie/durathread, 2, time = 4 SECONDS),
new /datum/stack_recipe("durathread bandana", /obj/item/clothing/mask/bandana/durathread, 1, time = 2.5 SECONDS),
+ new /datum/stack_recipe("surgical drapes", /obj/item/surgical_drapes, 1, time = 3 SECONDS)
))
/obj/item/stack/sheet/durathread
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 7d76006723eb..304d37ed247e 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -596,6 +596,7 @@
new /obj/item/bonegel(src)
new /obj/item/bonesetter(src)
new /obj/item/FixOVein(src)
+ new /obj/item/surgical_drapes(src)
new /obj/item/clothing/suit/straight_jacket(src)
new /obj/item/clothing/mask/muzzle(src)
new /obj/item/reagent_containers/glass/bottle/reagent/hydrocodone(src)
diff --git a/code/game/objects/mail.dm b/code/game/objects/mail.dm
index 6726c7ff2f6f..6175ec810e73 100644
--- a/code/game/objects/mail.dm
+++ b/code/game/objects/mail.dm
@@ -123,6 +123,7 @@
/obj/item/clothing/glasses/sunglasses,
/obj/item/food/snacks/fortunecookie,
/obj/item/scalpel/laser/laser1,
+ /obj/item/surgical_drapes,
/obj/item/toy/figure/crew/cmo,
/obj/item/toy/figure/crew/chemist,
/obj/item/toy/figure/crew/geneticist,
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index 91106241eaff..90ddae2fd6d8 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -26,6 +26,10 @@ LINEN BINS
var/list/nightmare_messages = list("black")
var/comfort = 0.5
+/obj/item/bedsheet/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/surgery_initiator/cloth, null, 0.45) // honestly, not bad.
+
/obj/item/bedsheet/attack_hand(mob/user)
if(isturf(loc) && user.Move_Pulled(src)) // make sure its on the ground first, prevents a speed exploit
return
diff --git a/code/modules/clothing/under/suit.dm b/code/modules/clothing/under/suit.dm
index 9ebee0602d99..f15b44f7ca3d 100644
--- a/code/modules/clothing/under/suit.dm
+++ b/code/modules/clothing/under/suit.dm
@@ -9,6 +9,11 @@
"Kidan" = 'icons/mob/clothing/species/kidan/under/suit.dmi'
)
+/obj/item/clothing/under/suit/Initialize(mapload)
+ . = ..()
+ // better than nothing! ghetto surgery time
+ AddComponent(/datum/component/surgery_initiator/cloth, null, 0.1)
+
/obj/item/clothing/under/suit/black
name = "black suit"
desc = "A black suit and red tie. Very formal."
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index f1dd7d9fa61e..00f93669a175 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -355,7 +355,8 @@
/obj/item/stack/medical/ointment/advanced/cyborg,
/obj/item/stack/medical/splint/cyborg,
/obj/item/stack/nanopaste/cyborg,
- /obj/item/gripper/medical
+ /obj/item/gripper/medical,
+ /obj/item/surgical_drapes,
)
malf_modules = list(/obj/item/gun/syringemalf)
special_rechargables = list(
diff --git a/code/modules/supply/supply_packs/pack_medical.dm b/code/modules/supply/supply_packs/pack_medical.dm
index dba5ff3f29bf..2328591115f2 100644
--- a/code/modules/supply/supply_packs/pack_medical.dm
+++ b/code/modules/supply/supply_packs/pack_medical.dm
@@ -154,7 +154,8 @@
/obj/item/bonegel,
/obj/item/retractor,
/obj/item/bonesetter,
- /obj/item/circular_saw)
+ /obj/item/circular_saw,
+ /obj/item/surgical_drapes)
cost = 400
containertype = /obj/structure/closet/crate/secure
containername = "surgery crate"
diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm
index 5411e8c8209f..925bf8b7c640 100644
--- a/code/modules/surgery/generic.dm
+++ b/code/modules/surgery/generic.dm
@@ -28,8 +28,8 @@
/datum/surgery_step/generic/cut_open/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message(
- "[user] starts the incision on [target]'s [affected.name] with \the [tool].",
- "You start the incision on [target]'s [affected.name] with \the [tool].",
+ "[user] starts the incision on [target]'s [affected.name] with [tool].",
+ "You start the incision on [target]'s [affected.name] with [tool].",
chat_message_type = MESSAGE_TYPE_COMBAT
)
affected.custom_pain("You feel a horrible pain as if from a sharp knife in your [affected.name]!")
@@ -38,8 +38,8 @@
/datum/surgery_step/generic/cut_open/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message(
- " [user] has made an incision on [target]'s [affected.name] with \the [tool].",
- " You have made an incision on [target]'s [affected.name] with \the [tool].",
+ "[user] has made an incision on [target]'s [affected.name] with [tool].",
+ "You have made an incision on [target]'s [affected.name] with [tool].",
chat_message_type = MESSAGE_TYPE_COMBAT
)
affected.open = ORGAN_ORGANIC_OPEN
@@ -49,8 +49,8 @@
/datum/surgery_step/generic/cut_open/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message(
- " [user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!",
- " Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with \the [tool]!",
+ "[user]'s hand slips, slicing open [target]'s [affected.name] in a wrong spot with [tool]!",
+ "Your hand slips, slicing open [target]'s [affected.name] in a wrong spot with [tool]!",
chat_message_type = MESSAGE_TYPE_COMBAT
)
affected.receive_damage(10)
@@ -93,8 +93,8 @@
/datum/surgery_step/generic/clamp_bleeders/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery)
var/obj/item/organ/external/affected = target.get_organ(target_zone)
user.visible_message(
- " [user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",
- " Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",
+ "[user]'s hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",
+ "Your hand slips, tearing blood vessels and causing massive bleeding in [target]'s [affected.name] with \the [tool]!",
chat_message_type = MESSAGE_TYPE_COMBAT
)
affected.receive_damage(10)
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 20cdc7e04251..35ae69f16ee4 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -44,7 +44,10 @@
var/abstract = FALSE
/// Whether this surgery should be cancelled when an organ change happens. (removed if requires bodypart, or added if doesn't require bodypart)
var/cancel_on_organ_change = TRUE
-
+ /// Whether the surgery was started with drapes.
+ var/started_with_drapes = FALSE
+ /// How likely it should be for the surgery to cause infection: 0-1
+ var/germ_prevention_quality = 0
/datum/surgery/New(atom/surgery_target, surgery_location, surgery_bodypart)
..()
@@ -440,16 +443,21 @@
SHOULD_CALL_PARENT(TRUE)
if(ishuman(target))
var/obj/item/organ/external/affected = target.get_organ(target_zone)
- if(can_infect && affected)
+ if(can_infect && affected && !prob(surgery.germ_prevention_quality))
spread_germs_to_organ(affected, user, tool)
if(ishuman(user) && !isalien(target) && prob(60))
var/mob/living/carbon/human/H = user
+
+ var/blood_spread = SEND_SIGNAL(surgery, COMSIG_SURGERY_BLOOD_SPLASH, user, target, target_zone, tool)
+ if(blood_spread == COMPONENT_BLOOD_SPLASH_HANDLED)
+ return
switch(blood_level)
if(SURGERY_BLOODSPREAD_HANDS)
+ target.visible_message("Blood splashes onto [user]'s hands.")
H.make_bloody_hands(target.get_blood_dna_list(), target.get_blood_color())
if(SURGERY_BLOODSPREAD_FULLBODY)
+ target.visible_message("A spray of blood coats [user].")
H.bloody_body(target)
- return
/**
* Finish a surgery step, performing anything that runs on the tail-end of a successful surgery.
@@ -484,7 +492,7 @@
* * user - The user who's manipulating the organ.
* * tool - The tool the user is using to mess with the organ.
*/
-/proc/spread_germs_to_organ(obj/item/organ/target_organ, mob/living/carbon/human/user, obj/item/tool)
+/datum/surgery_step/proc/spread_germs_to_organ(obj/item/organ/target_organ, mob/living/carbon/human/user, obj/item/tool, datum/surgery/surgery)
if(!istype(user) || !istype(target_organ) || target_organ.is_robotic() || target_organ.sterile)
return
@@ -502,7 +510,7 @@
* * E - An external organ being operated on.
* * tool - The tool performing the operation.
*/
-/proc/spread_germs_by_incision(obj/item/organ/external/E, obj/item/tool)
+/datum/surgery_step/proc/spread_germs_by_incision(obj/item/organ/external/E, obj/item/tool, datum/surgery/surgery)
if(!is_external_organ(E))
return
if(!E.owner)
diff --git a/code/modules/surgery/tools.dm b/code/modules/surgery/tools.dm
index e711b0cf2311..1a012ae609c2 100644
--- a/code/modules/surgery/tools.dm
+++ b/code/modules/surgery/tools.dm
@@ -274,9 +274,24 @@
/obj/item/surgical_drapes
name = "surgical drapes"
- desc = "Apearature brand surgical drapes providing privacy and infection control."
+ desc = "Apearature brand surgical drapes providing privacy and infection control. Built from durathread."
icon = 'icons/obj/surgery.dmi'
icon_state = "surgical_drapes"
w_class = WEIGHT_CLASS_SMALL
origin_tech = "biotech=1"
attack_verb = list("slapped")
+ /// How effective this is at preventing infections during surgeries.
+ var/surgery_effectiveness = 0.9
+
+/obj/item/surgical_drapes/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_SURGICAL, ROUNDSTART_TRAIT)
+ AddComponent(/datum/component/surgery_initiator/cloth, null, surgery_effectiveness)
+
+/obj/item/surgical_drapes/improvised
+ name = "improvised drapes"
+ desc = "Hastily-sliced fabric that seems like it'd be useful for surgery. Probably better than the shirt off your back."
+ icon = 'icons/obj/stacks/miscellaneous.dmi'
+ icon_state = "empty-sandbags"
+ origin_tech = null
+ surgery_effectiveness = 0.67